diff -Nru ecflow-4.9.0/ACore/src/ArgvCreator.hpp ecflow-4.11.1/ACore/src/ArgvCreator.hpp --- ecflow-4.9.0/ACore/src/ArgvCreator.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/ArgvCreator.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -24,7 +24,7 @@ class ArgvCreator : private boost::noncopyable { public: // Create argc/argv from a vector of strings - ArgvCreator(const std::vector & ); + explicit ArgvCreator(const std::vector & ); // Destroys argv array ~ArgvCreator(); diff -Nru ecflow-4.9.0/ACore/src/Calendar.hpp ecflow-4.11.1/ACore/src/Calendar.hpp --- ecflow-4.9.0/ACore/src/Calendar.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Calendar.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -113,7 +113,7 @@ void init(const boost::posix_time::ptime& time, Clock_t clock = Calendar::REAL, bool startStopWithServer = false); /// Start the Calendar. Parameter time can include gain. - void begin(const boost::posix_time::ptime& time); + void begin(const boost::posix_time::ptime&); /// Update the calendar using the input. Will Store internally the time duration /// between the init() function call and the last update. diff -Nru ecflow-4.9.0/ACore/src/CalendarUpdateParams.hpp ecflow-4.11.1/ACore/src/CalendarUpdateParams.hpp --- ecflow-4.9.0/ACore/src/CalendarUpdateParams.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/CalendarUpdateParams.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -38,7 +38,7 @@ // For use in the simulator/ unit tests - CalendarUpdateParams(const boost::posix_time::time_duration& serverPollPeriod) + explicit CalendarUpdateParams(const boost::posix_time::time_duration& serverPollPeriod) : timeNow_( boost::date_time::not_a_date_time), serverPollPeriod_(serverPollPeriod), serverRunning_( true ), diff -Nru ecflow-4.9.0/ACore/src/DState.hpp ecflow-4.11.1/ACore/src/DState.hpp --- ecflow-4.9.0/ACore/src/DState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/DState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -30,7 +30,7 @@ class DState { public: enum State { UNKNOWN =0, COMPLETE=1, QUEUED=2, ABORTED=3, SUBMITTED=4, ACTIVE=5, SUSPENDED=6}; - DState(State s): state_(s), state_change_no_(0) {} + explicit DState(State s): state_(s), state_change_no_(0) {} DState(): state_(QUEUED),state_change_no_(0) {} State state() const { return state_;} @@ -46,12 +46,12 @@ static DState::State default_state() { return DState::QUEUED; } static NState::State convert(DState::State); - static const char* toString(DState::State s); - static const char* to_html(DState::State s); + static const char* toString(DState::State); + static const char* to_html(DState::State); static const char* toString(const DState& ns) { return toString(ns.state());} static std::string to_string( DState::State s){ return std::string( toString(s) );} - static DState::State toState(const std::string& state); - static bool isValid(const std::string& state); + static DState::State toState(const std::string&); + static bool isValid(const std::string&); static std::vector allStates(); static std::vector states(); @@ -71,8 +71,8 @@ // Thin wrapper over DState, to aid python. i.e Task("t").add(Defstatus(DState.complete)) class Defstatus { public: - Defstatus(DState::State state) : state_(state) {} - Defstatus(const std::string& ds) : state_(DState::toState(ds)) {} + explicit Defstatus(DState::State state) : state_(state) {} + explicit Defstatus(const std::string& ds) : state_(DState::toState(ds)) {} DState::State state() const { return state_;} std::string to_string() const { return DState::to_string(state_);} private: diff -Nru ecflow-4.9.0/ACore/src/ecflow_version.h ecflow-4.11.1/ACore/src/ecflow_version.h --- ecflow-4.9.0/ACore/src/ecflow_version.h 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/ecflow_version.h 2018-10-23 11:41:34.000000000 +0000 @@ -1,9 +1,9 @@ #ifndef ecflow_version_config_h #define ecflow_version_config_h -#define ECFLOW_VERSION "4.9.0" +#define ECFLOW_VERSION "4.11.1" #define ECFLOW_RELEASE "4" -#define ECFLOW_MAJOR "9" -#define ECFLOW_MINOR "0" +#define ECFLOW_MAJOR "11" +#define ECFLOW_MINOR "1" #endif diff -Nru ecflow-4.9.0/ACore/src/Extract.hpp ecflow-4.11.1/ACore/src/Extract.hpp --- ecflow-4.9.0/ACore/src/Extract.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Extract.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -36,11 +36,11 @@ /// extract integer or throw an std::runtime_error on failure, /// the error message passed in is used to configure the returned exception - static int theInt(const std::string& token,const std::string& errorContext) ; + static int theInt(const std::string& token,const std::string& errorMsg) ; /// extract YMD, integer of the form yyyymmdd, will throw std::runtime_error on failure /// the error message passed in is used to configure the returned exception - static int ymd(const std::string& token, std::string& errorContext); + static int ymd(const std::string& ymdToken, std::string& errorMsg); /// extract optional int, else return -1 /// the error message passed in is used to configure the returned exception @@ -49,7 +49,7 @@ // repeat integer variable 3 4 # a comment // hence we must check the first character, and not the complete token static int optionalInt(const std::vector& lineTokens, - int pos,int defaultvalue,const std::string& errorContext); + int pos,int defaultValue,const std::string& errorMsg); private: Extract(); }; diff -Nru ecflow-4.9.0/ACore/src/File.cpp ecflow-4.11.1/ACore/src/File.cpp --- ecflow-4.9.0/ACore/src/File.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/File.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -67,19 +67,24 @@ path = paths[i]; path += '/'; path += file; - if (fs::exists(path)) { - return paths[i]; + try { + if (fs::exists(path)) { + return paths[i]; + } + } + catch(...) { + // could be permission denied. } } } return std::string(); } -std::string File::getExt(const std::string& s) +std::string File::getExt(const std::string& file) { - size_t i = s.rfind('.',s.length()); + size_t i = file.rfind('.',file.length()); if (i != std::string::npos) { - return s.substr(i+1); + return file.substr(i+1); } return string(); } diff -Nru ecflow-4.9.0/ACore/src/File_r.cpp ecflow-4.11.1/ACore/src/File_r.cpp --- ecflow-4.9.0/ACore/src/File_r.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/File_r.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -14,6 +14,7 @@ //============================================================================ #include + #include "File_r.hpp" using namespace std; @@ -21,4 +22,3 @@ File_r::File_r(const std::string& file_name) : file_name_(file_name), fp_(file_name.c_str(), std::ios_base::in) {} File_r::~File_r() { fp_.close(); } - diff -Nru ecflow-4.9.0/ACore/src/File_r.hpp ecflow-4.11.1/ACore/src/File_r.hpp --- ecflow-4.9.0/ACore/src/File_r.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/File_r.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -24,10 +24,12 @@ class File_r : private boost::noncopyable { public: - File_r(const std::string& file_name); + explicit File_r(const std::string& file_name); ~File_r(); bool ok() const { return (fp_) ? true : false; } + std::streamoff pos() { return fp_.tellg();} + void setPos(std::streamoff pos) { fp_.seekg(pos,fp_.beg);} bool good() const { return fp_.good(); } void getline(std::string& line) { std::getline(fp_,line); } const std::string& file_name() const { return file_name_; } diff -Nru ecflow-4.9.0/ACore/src/Host.hpp ecflow-4.11.1/ACore/src/Host.hpp --- ecflow-4.9.0/ACore/src/Host.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Host.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ public: /// can throw std::runtime_error if the gethostname fails Host(); - Host(const std::string& host); + explicit Host(const std::string& host); /// return the host name std::string name() const; @@ -46,7 +46,7 @@ std::string ecf_passwd_file(const std::string& port) const; /// Given a port and file name, will return ..file_name - std::string prefix_host_and_port( const std::string& port,const std::string& list_file ) const; + std::string prefix_host_and_port( const std::string& port,const std::string& file_name ) const; private: std::string host_port_prefix(const std::string& port) const; diff -Nru ecflow-4.9.0/ACore/src/Log.hpp ecflow-4.11.1/ACore/src/Log.hpp --- ecflow-4.9.0/ACore/src/Log.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Log.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -90,7 +90,7 @@ void clear(); /// Close the existing log file, and new start writing to the new location - void new_path(const std::string& the_path); + void new_path(const std::string& the_new_path); /// Returns the current log file path name std::string path() const; @@ -102,11 +102,11 @@ /// make sure path is not a directory & path has a parent directory. /// Will throw std::runtime_error for errors - static void check_new_path(const std::string& the_new_path); + static void check_new_path(const std::string& new_path); private: ~Log(); - Log(const std::string& filename); + explicit Log(const std::string& fileName); static Log* instance_; bool enable_auto_flush_; diff -Nru ecflow-4.9.0/ACore/src/LogVerification.cpp ecflow-4.11.1/ACore/src/LogVerification.cpp --- ecflow-4.9.0/ACore/src/LogVerification.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/LogVerification.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,10 +39,10 @@ return false; } - int line_number = 0; + //int line_number = 0; for(std::vector::iterator i=lines.begin(); i!=lines.end(); ++i) { - line_number++; + //line_number++; if ((*i).find("LOG:") == std::string::npos) { continue; // State changes have type Log::LOG } diff -Nru ecflow-4.9.0/ACore/src/LogVerification.hpp ecflow-4.11.1/ACore/src/LogVerification.hpp --- ecflow-4.9.0/ACore/src/LogVerification.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/LogVerification.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -32,7 +32,7 @@ /// Will compare the input log file, with gold reference. /// Will compare the node state changes only /// Compensate for states that are scheduler dependent - static bool compareNodeStates( const std::string& logfile, const std::string& goldenRefLogFile, std::string& errormsg); + static bool compareNodeStates( const std::string& logfile, const std::string& goldenRefLogFile, std::string& errorMsg); private: LogVerification() {} diff -Nru ecflow-4.9.0/ACore/src/NOrder.cpp ecflow-4.11.1/ACore/src/NOrder.cpp --- ecflow-4.9.0/ACore/src/NOrder.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/NOrder.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -42,14 +42,14 @@ return NOrder::TOP; } -bool NOrder::isValid(const std::string& state) +bool NOrder::isValid(const std::string& order) { - if (state == "top") return true; - if (state == "bottom") return true; - if (state == "alpha") return true; - if (state == "order") return true; - if (state == "up") return true; - if (state == "down") return true; + if (order == "top") return true; + if (order == "bottom") return true; + if (order == "alpha") return true; + if (order == "order") return true; + if (order == "up") return true; + if (order == "down") return true; return false; } diff -Nru ecflow-4.9.0/ACore/src/NState.hpp ecflow-4.11.1/ACore/src/NState.hpp --- ecflow-4.9.0/ACore/src/NState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/NState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ class NState { public: enum State { UNKNOWN =0, COMPLETE=1, QUEUED=2, ABORTED=3, SUBMITTED=4, ACTIVE=5 }; - NState(State s): state_(s), state_change_no_(0) {} + explicit NState(State s): state_(s), state_change_no_(0) {} NState(): state_(UNKNOWN),state_change_no_(0) {} State state() const { return state_;} @@ -46,7 +46,7 @@ static const char* toString(NState::State s); static const char* to_html(NState::State s); static const char* toString(const NState& ns) { return toString(ns.state());} - static NState::State toState(const std::string& state); + static NState::State toState(const std::string&); static bool isValid(const std::string& state); static std::vector allStates(); static std::vector states(); diff -Nru ecflow-4.9.0/ACore/src/PrintStyle.hpp ecflow-4.11.1/ACore/src/PrintStyle.hpp --- ecflow-4.9.0/ACore/src/PrintStyle.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/PrintStyle.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -28,7 +28,7 @@ MIGRATE = 3 // Output the definition that is fully parse-able & includes state }; - PrintStyle(Type_t t) : old_style_(getStyle()) { setStyle(t);} + explicit PrintStyle(Type_t t) : old_style_(getStyle()) { setStyle(t);} ~PrintStyle() { setStyle(old_style_); } // reset to old style on destruction diff -Nru ecflow-4.9.0/ACore/src/SState.hpp ecflow-4.11.1/ACore/src/SState.hpp --- ecflow-4.9.0/ACore/src/SState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/SState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -37,8 +37,8 @@ static std::string to_string(int status); static std::string to_string(SState::State); - static SState::State toState(const std::string& state); - static bool isValid(const std::string& state); + static SState::State toState(const std::string&); + static bool isValid(const std::string&); private: SState(); diff -Nru ecflow-4.9.0/ACore/src/Str.cpp ecflow-4.11.1/ACore/src/Str.cpp --- ecflow-4.9.0/ACore/src/Str.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Str.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -112,13 +112,13 @@ return false; } -bool Str::replace_all(std::string& subject, const std::string& search, const std::string& replace) +bool Str::replace_all(std::string& subject, const std::string& stringToFind, const std::string& stringToReplace) { bool replaced = false ; size_t pos = 0; - while ((pos = subject.find(search, pos)) != std::string::npos) { - subject.replace(pos, search.length(), replace); - pos += replace.length(); + while ((pos = subject.find(stringToFind, pos)) != std::string::npos) { + subject.replace(pos, stringToFind.length(), stringToReplace); + pos += stringToReplace.length(); replaced = true; } return replaced; diff -Nru ecflow-4.9.0/ACore/src/Str.hpp ecflow-4.11.1/ACore/src/Str.hpp --- ecflow-4.9.0/ACore/src/Str.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/Str.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -42,9 +42,9 @@ /// Find 'stringToFind' in 'jobLine' and replace with string 'stringToRplace' /// return true if replace ok else returns false; - static bool replace(std::string& jobLine, const std::string& stringToFind, const std::string& stringToRplace); - static bool replace_all(std::string& jobLine, const std::string& stringToFind, const std::string& stringToRplace); - static void replaceall(std::string& jobLine, const std::string& stringToFind, const std::string& stringToRplace); + static bool replace(std::string& subject, const std::string& stringToFind, const std::string& stringToRplace); + static bool replace_all(std::string& subject, const std::string& stringToFind, const std::string& stringToRplace); + static void replaceall(std::string& subject, const std::string& stringToFind, const std::string& stringToRplace); // extract data member value, ie given a string of the form: // str=cmd a b fred:value @@ -56,18 +56,18 @@ /// The split is based on *ANY* of the characters in the delimiters. /// **** Hence a delimiter of "==" will still split "a = complete" /// **** sequential delimiter character are ignored **** - static void split(const std::string& str, - std::vector< std::string >& lineTokens, + static void split(const std::string& line, + std::vector< std::string >& tokens, const std::string& delimiters = " \t"); /// case in sensitive string comparison - static bool caseInsCompare( const std::string& str1, const std::string& str2); + static bool caseInsCompare( const std::string&, const std::string&); /// case insenstive less - static bool caseInsLess( const std::string& str1, const std::string& str2); + static bool caseInsLess( const std::string&, const std::string&); /// case insenstive Greater - static bool caseInsGreater( const std::string& str1, const std::string& str2); + static bool caseInsGreater( const std::string&, const std::string&); /// Used for checking node names static bool valid_name(const std::string& name, std::string &msg); @@ -81,8 +81,8 @@ /// Truncate the input string at the start/end if exceeds max_lines_ newlines /// returns true if truncated false otherwise - static bool truncate_at_start( std::string& fileContents, size_t max_lines_); - static bool truncate_at_end( std::string& fileContents, size_t max_lines_); + static bool truncate_at_start( std::string& fileContents, size_t max_lines); + static bool truncate_at_end( std::string& fileContents, size_t max_lines); /// Only use strcmp if the first characters are the same static int local_strcmp(const char* s, const char* t) diff -Nru ecflow-4.9.0/ACore/src/TimeSeries.hpp ecflow-4.11.1/ACore/src/TimeSeries.hpp --- ecflow-4.9.0/ACore/src/TimeSeries.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/TimeSeries.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -52,9 +52,9 @@ class TimeSeries { public: TimeSeries(); - TimeSeries(int hour, int minute, bool relativeToSuiteStart = false ); - TimeSeries(const TimeSlot&, bool relativeToSuiteStart = false ); - TimeSeries(const TimeSlot& start, const TimeSlot& finish, const TimeSlot& incr,bool relativeToSuiteStart = false); + TimeSeries(int hour, int minute, bool relative = false ); + TimeSeries(const TimeSlot&, bool relative = false ); + TimeSeries(const TimeSlot& start, const TimeSlot& finish, const TimeSlot& incr,bool relative = false); bool operator<(const TimeSeries& rhs) const; diff -Nru ecflow-4.9.0/ACore/src/TimeSlot.hpp ecflow-4.11.1/ACore/src/TimeSlot.hpp --- ecflow-4.9.0/ACore/src/TimeSlot.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/src/TimeSlot.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -36,7 +36,7 @@ TimeSlot(int hour, int min) : hour_(hour), minute_(min),isNull_(false) { assert(hour >= 0 && min >=0 ); } - TimeSlot(const boost::posix_time::time_duration& td) + explicit TimeSlot(const boost::posix_time::time_duration& td) : hour_(td.hours()), minute_(td.minutes()),isNull_(false) { assert( hour_ < 60 && minute_ < 60);} diff -Nru ecflow-4.9.0/ACore/test/TestStr.cpp ecflow-4.11.1/ACore/test/TestStr.cpp --- ecflow-4.9.0/ACore/test/TestStr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/test/TestStr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -691,4 +691,64 @@ // } //} + +BOOST_AUTO_TEST_CASE( test_str_valid_name ) +{ + cout << "ACore:: ...test_str_valid_name\n"; + + std::vector valid; + valid.push_back("a"); + valid.push_back("a122345"); + valid.push_back("_a122345"); + valid.push_back("_"); + valid.push_back("0"); + valid.push_back("1"); + valid.push_back("2"); + valid.push_back("3"); + valid.push_back("4"); + valid.push_back("5"); + valid.push_back("6"); + valid.push_back("7"); + valid.push_back("8"); + valid.push_back("9"); + valid.push_back("11"); + valid.push_back("111"); + for(size_t i = 0; i < valid.size(); i++) { + std::string msg; + BOOST_CHECK_MESSAGE( Str::valid_name( valid[i],msg ) ,"Expected " << valid[i] << " to be valid" ); + BOOST_CHECK_MESSAGE( Str::valid_name( valid[i]) ,"Expected " << valid[i] << " to be valid" ); + } + + BOOST_CHECK_MESSAGE( !Str::valid_name( "") ,"Expected empty string to be in-valid" ); + BOOST_CHECK_MESSAGE( !Str::valid_name( ".") ,"Expected '.' string to be in-valid" ); + std::vector invalid; + invalid.push_back("?"); + invalid.push_back("!"); + invalid.push_back("\""); + invalid.push_back("$"); + invalid.push_back("%"); + invalid.push_back("^"); + invalid.push_back("*"); + invalid.push_back("("); + invalid.push_back(")"); + invalid.push_back("-"); + invalid.push_back("+"); + invalid.push_back(":"); + invalid.push_back(";"); + invalid.push_back("@"); + invalid.push_back("~"); + invalid.push_back("<"); + invalid.push_back(">"); + invalid.push_back("!"); + for(size_t i = 0; i < invalid.size(); i++) { + std::string msg; + BOOST_CHECK_MESSAGE( !Str::valid_name( invalid[i],msg ) ,"Expected " << invalid[i] << " to be in-valid" ); + BOOST_CHECK_MESSAGE( !Str::valid_name( invalid[i]) ,"Expected " << invalid[i] << " to be in-valid" ); + + std::string s = "a" + invalid[i]; + BOOST_CHECK_MESSAGE( !Str::valid_name( s,msg ) ,"Expected " << s << " to be in-valid" ); + BOOST_CHECK_MESSAGE( !Str::valid_name( s ) ,"Expected " << s << " to be in-valid" ); + } +} + BOOST_AUTO_TEST_SUITE_END() diff -Nru ecflow-4.9.0/ACore/test/TestVersioning.hpp ecflow-4.11.1/ACore/test/TestVersioning.hpp --- ecflow-4.9.0/ACore/test/TestVersioning.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ACore/test/TestVersioning.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ namespace version0 { class X { public: - X(int h = 0) : hour_(h) {} + explicit X(int h = 0) : hour_(h) {} bool operator==(const X& rhs) const { return hour_ == rhs.hour_; } private: int hour_; @@ -56,7 +56,7 @@ namespace version_change_dm_name { class X { public: - X(int h = 0) : hours_(h) {} + explicit X(int h = 0) : hours_(h) {} bool operator==(const X& rhs) const { return hours_ == rhs.hours_; } private: int hours_; @@ -72,7 +72,7 @@ namespace version_change_dm_type { class X { public: - X(const std::string& h = "") : hour_(h) {} + explicit X(const std::string& h = "") : hour_(h) {} bool operator==(const X& rhs) const { return hour_ == rhs.hour_; } std::string str() const { return hour_; } private: diff -Nru ecflow-4.9.0/ANattr/src/AutoCancelAttr.hpp ecflow-4.11.1/ANattr/src/AutoCancelAttr.hpp --- ecflow-4.9.0/ANattr/src/AutoCancelAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/AutoCancelAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -27,7 +27,7 @@ AutoCancelAttr() : relative_(true),days_(false) {} AutoCancelAttr(int hour, int minute, bool relative ) : timeStruct_(hour, minute), relative_(relative), days_(false) {} AutoCancelAttr(const TimeSlot& ts, bool relative ) : timeStruct_(ts), relative_(relative), days_(false) {} - AutoCancelAttr(int days) : timeStruct_( TimeSlot(days*24,0) ), relative_(true), days_(true) {} + explicit AutoCancelAttr(int days) : timeStruct_( TimeSlot(days*24,0) ), relative_(true), days_(true) {} std::ostream& print(std::ostream&) const; bool operator==(const AutoCancelAttr& rhs) const; diff -Nru ecflow-4.9.0/ANattr/src/ClockAttr.hpp ecflow-4.11.1/ANattr/src/ClockAttr.hpp --- ecflow-4.9.0/ANattr/src/ClockAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/ClockAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -40,7 +40,7 @@ /// create a clock attribute initialised with given date and time ClockAttr(const boost::posix_time::ptime&, bool hybrid = false, bool positiveGain = true); ClockAttr(int day, int month, int year, bool hybrid = false ); - ClockAttr(bool hybrid = false); + explicit ClockAttr(bool hybrid = false); std::ostream& print(std::ostream&) const; bool operator==(const ClockAttr& rhs) const; diff -Nru ecflow-4.9.0/ANattr/src/CronAttr.hpp ecflow-4.11.1/ANattr/src/CronAttr.hpp --- ecflow-4.9.0/ANattr/src/CronAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/CronAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -31,9 +31,9 @@ class CronAttr { public: CronAttr(); - CronAttr(const std::string& time_series); + explicit CronAttr(const std::string& time_series); CronAttr(const TimeSlot& s, const TimeSlot& f, const TimeSlot& i) : timeSeries_(s,f,i),makeFree_(false),state_change_no_(0) {} - CronAttr(const TimeSeries& ts ) : timeSeries_(ts),makeFree_(false),state_change_no_(0) {} + explicit CronAttr(const TimeSeries& ts ) : timeSeries_(ts),makeFree_(false),state_change_no_(0) {} CronAttr(int h, int m, bool relative = false) : timeSeries_(h,m,relative),makeFree_(false),state_change_no_(0) {} std::ostream& print(std::ostream&) const; diff -Nru ecflow-4.9.0/ANattr/src/DateAttr.hpp ecflow-4.11.1/ANattr/src/DateAttr.hpp --- ecflow-4.9.0/ANattr/src/DateAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/DateAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,11 +29,11 @@ class DateAttr { public: DateAttr(int day, int month, int year); // will throw std::out_of_range for if invalid date - DateAttr(const std::string&); // will throw std::runtime_error for if invalid date + explicit DateAttr(const std::string&); // will throw std::runtime_error for if invalid date DateAttr() : day_(0), month_(0), year_(0), makeFree_(false), state_change_no_(0) {} // for serialisation - DateAttr(const boost::gregorian::date& date) + explicit DateAttr(const boost::gregorian::date& date) : day_(date.day()), month_(date.month()), year_(date.year()), makeFree_(false), state_change_no_(0) {} // for test diff -Nru ecflow-4.9.0/ANattr/src/DayAttr.hpp ecflow-4.11.1/ANattr/src/DayAttr.hpp --- ecflow-4.9.0/ANattr/src/DayAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/DayAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -31,9 +31,9 @@ public: enum Day_t { SUNDAY=0, MONDAY=1, TUESDAY=2, WEDNESDAY=3, THURSDAY=4, FRIDAY=5, SATURDAY=6 }; DayAttr() : day_(DayAttr::SUNDAY), makeFree_(false),state_change_no_(0) {} - DayAttr(Day_t day) : day_(day), makeFree_(false),state_change_no_(0) {} - DayAttr(const std::string& str) : day_(DayAttr::getDay(str)), makeFree_(false),state_change_no_(0) {} - DayAttr(const boost::gregorian::date& date) + explicit DayAttr(Day_t day) : day_(day), makeFree_(false),state_change_no_(0) {} + explicit DayAttr(const std::string& str) : day_(DayAttr::getDay(str)), makeFree_(false),state_change_no_(0) {} + explicit DayAttr(const boost::gregorian::date& date) : day_(static_cast(date.day_of_week().as_number())), makeFree_(false),state_change_no_(0) {} std::ostream& print(std::ostream&) const; diff -Nru ecflow-4.9.0/ANattr/src/NodeAttr.hpp ecflow-4.11.1/ANattr/src/NodeAttr.hpp --- ecflow-4.9.0/ANattr/src/NodeAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/NodeAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -97,7 +97,7 @@ class Event { public: Event(int number, const std::string& eventName = ""); - Event(const std::string& eventName); + explicit Event(const std::string& eventName); Event() : value_(false), number_(std::numeric_limits::max()), diff -Nru ecflow-4.9.0/ANattr/src/RepeatAttr.cpp ecflow-4.11.1/ANattr/src/RepeatAttr.cpp --- ecflow-4.9.0/ANattr/src/RepeatAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/RepeatAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -597,6 +597,9 @@ if ( !Str::valid_name( variable ) ) { throw std::runtime_error("RepeatEnumerated: Invalid name: " + variable); } + if (theEnums.empty()) { + throw std::runtime_error("RepeatEnumerated: " + variable + " is empty"); + } } int RepeatEnumerated::end() const { @@ -789,6 +792,9 @@ if ( !Str::valid_name( variable ) ) { throw std::runtime_error("RepeatString:: Invalid name: " + variable); } + if (theEnums.empty()) { + throw std::runtime_error("RepeatString : " + variable + " is empty"); + } } int RepeatString::end() const { diff -Nru ecflow-4.9.0/ANattr/src/RepeatAttr.hpp ecflow-4.11.1/ANattr/src/RepeatAttr.hpp --- ecflow-4.9.0/ANattr/src/RepeatAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/RepeatAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,7 +39,7 @@ // class RepeatBase { public: - RepeatBase(const std::string& name) : state_change_no_(0), name_(name) {} + explicit RepeatBase(const std::string& name) : state_change_no_(0), name_(name) {} RepeatBase() : state_change_no_(0) {} virtual ~RepeatBase(); @@ -463,6 +463,13 @@ friend class boost::serialization::access; template void serialize(Archive & ar, const unsigned int /*version*/) { +#if defined(__clang__) + ar.register_type(static_cast(NULL)); + ar.register_type(static_cast(NULL)); + ar.register_type(static_cast(NULL)); + ar.register_type(static_cast(NULL)); + ar.register_type(static_cast(NULL)); +#endif ar & repeatType_; } }; diff -Nru ecflow-4.9.0/ANattr/src/TimeAttr.hpp ecflow-4.11.1/ANattr/src/TimeAttr.hpp --- ecflow-4.9.0/ANattr/src/TimeAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/TimeAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -59,13 +59,13 @@ // Use compiler , destructor, assignment, copy constructor, class TimeAttr { public: - TimeAttr(const std::string&); + explicit TimeAttr(const std::string&); TimeAttr() : makeFree_(false), state_change_no_(0) {} TimeAttr(int hour, int minute, bool relative = false ) : timeSeries_(hour, minute,relative), makeFree_(false),state_change_no_(0) {} TimeAttr(const TimeSlot& t, bool relative = false ) : timeSeries_(t,relative), makeFree_(false),state_change_no_(0) {} - TimeAttr(const TimeSeries& ts) + explicit TimeAttr(const TimeSeries& ts) : timeSeries_(ts), makeFree_(false),state_change_no_(0) {} TimeAttr(const TimeSlot& start, const TimeSlot& finish, const TimeSlot& incr, bool relative = false) : timeSeries_(start,finish,incr,relative), makeFree_(false),state_change_no_(0) {} diff -Nru ecflow-4.9.0/ANattr/src/TodayAttr.hpp ecflow-4.11.1/ANattr/src/TodayAttr.hpp --- ecflow-4.9.0/ANattr/src/TodayAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/src/TodayAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -95,13 +95,13 @@ // Use compiler , destructor, assignment, copy constructor class TodayAttr { public: - TodayAttr(const std::string&); + explicit TodayAttr(const std::string&); TodayAttr() : makeFree_(false), state_change_no_(0) {} TodayAttr(int hour, int minute, bool relative = false ) : timeSeries_(hour, minute,relative), makeFree_(false),state_change_no_(0) {} TodayAttr(const TimeSlot& t, bool relative = false ) : timeSeries_(t,relative), makeFree_(false),state_change_no_(0) {} - TodayAttr(const TimeSeries& ts) + explicit TodayAttr(const TimeSeries& ts) : timeSeries_(ts), makeFree_(false),state_change_no_(0) {} TodayAttr(const TimeSlot& start, const TimeSlot& finish, const TimeSlot& incr,bool relative = false) : timeSeries_(start,finish,incr,relative), makeFree_(false),state_change_no_(0) {} diff -Nru ecflow-4.9.0/ANattr/test/TestLateAttr.cpp ecflow-4.11.1/ANattr/test/TestLateAttr.cpp --- ecflow-4.9.0/ANattr/test/TestLateAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/test/TestLateAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -57,7 +57,7 @@ // set submitted state at 00:05:00 //cout << "start:" << to_simple_string(calendar.suiteTime()) << "\n"; - std::pair state = std::make_pair(NState::SUBMITTED, calendar.duration() ); + std::pair state = std::make_pair(NState(NState::SUBMITTED), calendar.duration() ); // after four minutes in submitted state, we should be late for(int m=1; m < 10; m++) { @@ -96,7 +96,7 @@ // set submitted state at 00:00:00 //cout << "start:" << to_simple_string(calendar.suiteTime()) << "\n"; - std::pair state = std::make_pair(NState::SUBMITTED, calendar.duration() ); + std::pair state = std::make_pair(NState(NState::SUBMITTED), calendar.duration() ); // after 10 hours we, if we are not active, we should be late for(int m=1; m < 23; m++) { @@ -136,7 +136,7 @@ // set active state at 00:00:00 // cout << "start:" << to_simple_string(calendar.suiteTime()) << "\n"; - std::pair state = std::make_pair(NState::ACTIVE, calendar.duration() ); + std::pair state = std::make_pair(NState(NState::ACTIVE), calendar.duration() ); // after 15 minutes relative, if we are not complete, we should be late for(int m=1; m < 23; m++) { @@ -175,7 +175,7 @@ // set active state at 00:00:00 // cout << "start:" << to_simple_string(calendar.suiteTime()) << "\n"; - std::pair state = std::make_pair(NState::ACTIVE, calendar.duration() ); + std::pair state = std::make_pair(NState(NState::ACTIVE), calendar.duration() ); // after 3 hours we, if we are not complete, we should be late for(int m=1; m < 7; m++) { diff -Nru ecflow-4.9.0/ANattr/test/TestRepeat.cpp ecflow-4.11.1/ANattr/test/TestRepeat.cpp --- ecflow-4.9.0/ANattr/test/TestRepeat.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANattr/test/TestRepeat.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -517,6 +517,18 @@ { cout << "ANattr:: ...test_repeat_date_errors \n"; + std::vector empty; + std::vector stringList;stringList.push_back("a");stringList.push_back("b"); + BOOST_REQUIRE_THROW( RepeatEnumerated("",stringList),std::runtime_error); // empty name + BOOST_REQUIRE_THROW( RepeatEnumerated(" ",stringList),std::runtime_error); // empty name + BOOST_REQUIRE_THROW( RepeatEnumerated("*",stringList),std::runtime_error); // illegal name + BOOST_REQUIRE_THROW( RepeatString("",stringList),std::runtime_error); // empty name + BOOST_REQUIRE_THROW( RepeatString(" ",stringList),std::runtime_error); // empty name + BOOST_REQUIRE_THROW( RepeatString("!£$%^&*()",stringList),std::runtime_error); // illegal name + BOOST_REQUIRE_THROW( RepeatEnumerated("AEnum",empty),std::runtime_error); // empty enumerations + BOOST_REQUIRE_THROW( RepeatString("AEnum",empty),std::runtime_error); // empty string list + + BOOST_REQUIRE_THROW( RepeatDate("",20090916,20090920,1),std::runtime_error); BOOST_REQUIRE_THROW( RepeatDate("YMD",200909161,20090920,1),std::runtime_error); // start > 8 BOOST_REQUIRE_THROW( RepeatDate("YMD",20090916,200909201,1),std::runtime_error); // end > 8 @@ -529,6 +541,7 @@ BOOST_REQUIRE_THROW( RepeatDate("YMD",20090920,20090916,1),std::runtime_error); // start day > end day, and delta > 0 BOOST_REQUIRE_THROW( RepeatDate("YMD",20090916,20090920,-1),std::runtime_error); // start day < end day, and delta < 0 + RepeatDate date("YMD",20150514,20150730,7); BOOST_REQUIRE_THROW( date.changeValue(20150513),std::runtime_error); // outside of range BOOST_REQUIRE_THROW( date.changeValue(20150731),std::runtime_error); // outside of range diff -Nru ecflow-4.9.0/ANode/parser/src/AutoCancelParser.hpp ecflow-4.11.1/ANode/parser/src/AutoCancelParser.hpp --- ecflow-4.9.0/ANode/parser/src/AutoCancelParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/AutoCancelParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class AutoCancelParser : public Parser { public: - AutoCancelParser(DefsStructureParser* p) : Parser(p) {} + explicit AutoCancelParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "autocancel"; } virtual bool doParse(const std::string& line,std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/CalendarParser.hpp ecflow-4.11.1/ANode/parser/src/CalendarParser.hpp --- ecflow-4.9.0/ANode/parser/src/CalendarParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/CalendarParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class CalendarParser : public Parser { public: - CalendarParser(DefsStructureParser* p) : Parser(p) {} + explicit CalendarParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "calendar"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/ClockParser.hpp ecflow-4.11.1/ANode/parser/src/ClockParser.hpp --- ecflow-4.9.0/ANode/parser/src/ClockParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/ClockParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -21,14 +21,14 @@ class ClockParser : public Parser { public: - ClockParser(DefsStructureParser* p) : Parser(p) {} + explicit ClockParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "clock"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; class EndClockParser : public Parser { public: - EndClockParser(DefsStructureParser* p) : Parser(p) {} + explicit EndClockParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "endclock"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/CronParser.hpp ecflow-4.11.1/ANode/parser/src/CronParser.hpp --- ecflow-4.9.0/ANode/parser/src/CronParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/CronParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class CronParser : public Parser { public: - CronParser(DefsStructureParser* p) : Parser(p) {} + explicit CronParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "cron"; } virtual bool doParse(const std::string& line,std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/DateParser.hpp ecflow-4.11.1/ANode/parser/src/DateParser.hpp --- ecflow-4.9.0/ANode/parser/src/DateParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DateParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class DateParser : public Parser { public: - DateParser(DefsStructureParser* p) : Parser(p) {} + explicit DateParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "date"; } virtual bool doParse(const std::string& /*line*/, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/DayParser.hpp ecflow-4.11.1/ANode/parser/src/DayParser.hpp --- ecflow-4.9.0/ANode/parser/src/DayParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DayParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class DayParser : public Parser { public: - DayParser(DefsStructureParser* p) : Parser(p) {} + explicit DayParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "day"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/DefsParser.cpp ecflow-4.11.1/ANode/parser/src/DefsParser.cpp --- ecflow-4.9.0/ANode/parser/src/DefsParser.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DefsParser.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -65,7 +65,7 @@ class TextParser : public Parser { public: - TextParser(DefsStructureParser* p) : Parser(p) {} + explicit TextParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "text"; } @@ -79,7 +79,7 @@ class AliasParser : public Parser { public: - AliasParser(DefsStructureParser* p) : Parser(p) { + explicit AliasParser(DefsStructureParser* p) : Parser(p) { reserve_vec(19); addParser( new VariableParser(p) ); addParser( new LabelParser(p) ); @@ -161,7 +161,7 @@ class TaskParser : public Parser { public: - TaskParser(DefsStructureParser* p) : Parser(p) { + explicit TaskParser(DefsStructureParser* p) : Parser(p) { reserve_vec(21); addParser( new VariableParser(p) ); addParser( new TriggerParser(p) ); @@ -247,7 +247,7 @@ class FamilyParser : public Parser { public: - FamilyParser(DefsStructureParser* p) : Parser(p) + explicit FamilyParser(DefsStructureParser* p) : Parser(p) { reserve_vec(21); addParser( new VariableParser(p) ); @@ -361,7 +361,7 @@ // See ECFLOW-106, and SUP-1198, why we don't allow time,today,date,day ate the suite level. class SuiteParser : public Parser { public: - SuiteParser(DefsStructureParser* p) : Parser(p), started_(false) + explicit SuiteParser(DefsStructureParser* p) : Parser(p), started_(false) { reserve_vec(18); addParser( new VariableParser(p) ); diff -Nru ecflow-4.9.0/ANode/parser/src/DefsParser.hpp ecflow-4.11.1/ANode/parser/src/DefsParser.hpp --- ecflow-4.9.0/ANode/parser/src/DefsParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DefsParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class DefsParser : public Parser { public: - DefsParser(DefsStructureParser* p); + explicit DefsParser(DefsStructureParser* p); DefsParser(DefsStructureParser* p, bool node_parser_only); virtual const char* keyword() const { return "DEFS" ;} }; diff -Nru ecflow-4.9.0/ANode/parser/src/DefsStateParser.hpp ecflow-4.11.1/ANode/parser/src/DefsStateParser.hpp --- ecflow-4.9.0/ANode/parser/src/DefsStateParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DefsStateParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,14 +20,14 @@ class DefsStateParser : public Parser { public: - DefsStateParser(DefsStructureParser* p) : Parser(p){} + explicit DefsStateParser(DefsStructureParser* p) : Parser(p){} virtual bool doParse(const std::string& line,std::vector& lineTokens); virtual const char* keyword() const { return "defs_state"; } }; class HistoryParser : public Parser { public: - HistoryParser(DefsStructureParser* p) : Parser(p){} + explicit HistoryParser(DefsStructureParser* p) : Parser(p){} virtual bool doParse(const std::string& line,std::vector& lineTokens); virtual const char* keyword() const { return "history"; } }; diff -Nru ecflow-4.9.0/ANode/parser/src/DefsStatusParser.hpp ecflow-4.11.1/ANode/parser/src/DefsStatusParser.hpp --- ecflow-4.9.0/ANode/parser/src/DefsStatusParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DefsStatusParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class DefsStatusParser : public Parser { public: - DefsStatusParser(DefsStructureParser* p) : Parser(p) {} + explicit DefsStatusParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "defstatus"; } virtual bool doParse(const std::string& line,std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/DefsStructureParser.hpp ecflow-4.11.1/ANode/parser/src/DefsStructureParser.hpp --- ecflow-4.9.0/ANode/parser/src/DefsStructureParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/DefsStructureParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -34,7 +34,7 @@ // This class is used get a line of defs format from a defs string class DefsString : private boost::noncopyable { public: - DefsString(const std::string& defs_as_string); + explicit DefsString(const std::string& defs_as_string); bool good() const; void getline(std::string& line); bool empty() const { return empty_; } @@ -54,7 +54,7 @@ public: DefsStructureParser(Defs* defsfile, const std::string& file_name); DefsStructureParser(Defs* defsfile, const std::string& def_str, bool); - DefsStructureParser(const std::string& defs_node_string); + explicit DefsStructureParser(const std::string& defs_node_string); ~DefsStructureParser(); /// Parse the definition file, *AND* check expressions and limits diff -Nru ecflow-4.9.0/ANode/parser/src/EventParser.hpp ecflow-4.11.1/ANode/parser/src/EventParser.hpp --- ecflow-4.9.0/ANode/parser/src/EventParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/EventParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class EventParser : public Parser { public: - EventParser(DefsStructureParser* p) : Parser(p) {} + explicit EventParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "event"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/ExternParser.hpp ecflow-4.11.1/ANode/parser/src/ExternParser.hpp --- ecflow-4.9.0/ANode/parser/src/ExternParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/ExternParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -33,7 +33,7 @@ // class ExternParser : public Parser { public: - ExternParser(DefsStructureParser* p) : Parser(p) {} + explicit ExternParser(DefsStructureParser* p) : Parser(p) {} virtual bool doParse(const std::string& line,std::vector& lineTokens); virtual const char* keyword() const { return "extern"; } }; diff -Nru ecflow-4.9.0/ANode/parser/src/InlimitParser.hpp ecflow-4.11.1/ANode/parser/src/InlimitParser.hpp --- ecflow-4.9.0/ANode/parser/src/InlimitParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/InlimitParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class InlimitParser : public Parser { public: - InlimitParser(DefsStructureParser* p) : Parser(p) {} + explicit InlimitParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "inlimit"; } virtual bool doParse(const std::string& line, std::vector& lineTokens) ; }; diff -Nru ecflow-4.9.0/ANode/parser/src/LabelParser.hpp ecflow-4.11.1/ANode/parser/src/LabelParser.hpp --- ecflow-4.9.0/ANode/parser/src/LabelParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/LabelParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class LabelParser : public Parser { public: - LabelParser(DefsStructureParser* p) : Parser(p) {} + explicit LabelParser(DefsStructureParser* p) : Parser(p) {} virtual bool doParse(const std::string& line, std::vector& lineTokens); virtual const char* keyword() const { return "label"; } }; diff -Nru ecflow-4.9.0/ANode/parser/src/LateParser.hpp ecflow-4.11.1/ANode/parser/src/LateParser.hpp --- ecflow-4.9.0/ANode/parser/src/LateParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/LateParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class LateParser : public Parser { public: - LateParser(DefsStructureParser* p) : Parser(p) {} + explicit LateParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "late"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/LimitParser.hpp ecflow-4.11.1/ANode/parser/src/LimitParser.hpp --- ecflow-4.9.0/ANode/parser/src/LimitParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/LimitParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class LimitParser : public Parser { public: - LimitParser(DefsStructureParser* p) : Parser(p) {} + explicit LimitParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "limit"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/MeterParser.hpp ecflow-4.11.1/ANode/parser/src/MeterParser.hpp --- ecflow-4.9.0/ANode/parser/src/MeterParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/MeterParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class MeterParser : public Parser { public: - MeterParser(DefsStructureParser* p) : Parser(p) {} + explicit MeterParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "meter"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/Parser.hpp ecflow-4.11.1/ANode/parser/src/Parser.hpp --- ecflow-4.9.0/ANode/parser/src/Parser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/Parser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ class Parser { public: - Parser(DefsStructureParser* p); + explicit Parser(DefsStructureParser* p); virtual ~Parser(); // if child does not recognise token try the parent diff -Nru ecflow-4.9.0/ANode/parser/src/RepeatParser.hpp ecflow-4.11.1/ANode/parser/src/RepeatParser.hpp --- ecflow-4.9.0/ANode/parser/src/RepeatParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/RepeatParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class RepeatParser : public Parser { public: - RepeatParser(DefsStructureParser* p) : Parser(p) {} + explicit RepeatParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "repeat"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); diff -Nru ecflow-4.9.0/ANode/parser/src/TimeParser.hpp ecflow-4.11.1/ANode/parser/src/TimeParser.hpp --- ecflow-4.9.0/ANode/parser/src/TimeParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/TimeParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class TimeParser : public Parser { public: - TimeParser(DefsStructureParser* p) : Parser(p) {} + explicit TimeParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "time"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/TodayParser.hpp ecflow-4.11.1/ANode/parser/src/TodayParser.hpp --- ecflow-4.9.0/ANode/parser/src/TodayParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/TodayParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class TodayParser : public Parser { public: - TodayParser(DefsStructureParser* p) : Parser(p) {} + explicit TodayParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "today"; } virtual bool doParse(const std::string& line, std::vector& lineTokens); }; diff -Nru ecflow-4.9.0/ANode/parser/src/TriggerParser.hpp ecflow-4.11.1/ANode/parser/src/TriggerParser.hpp --- ecflow-4.9.0/ANode/parser/src/TriggerParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/TriggerParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class TriggerCompleteParser : public Parser { protected: - TriggerCompleteParser(DefsStructureParser* p) : Parser(p) {} + explicit TriggerCompleteParser(DefsStructureParser* p) : Parser(p) {} void getExpression(const std::string& line, std::vector& lineTokens, std::string& expression, @@ -31,14 +31,14 @@ class TriggerParser : public TriggerCompleteParser { public: - TriggerParser(DefsStructureParser* p) : TriggerCompleteParser(p) {} + explicit TriggerParser(DefsStructureParser* p) : TriggerCompleteParser(p) {} virtual bool doParse(const std::string& line, std::vector& lineTokens); virtual const char* keyword() const { return "trigger"; } }; class CompleteParser : public TriggerCompleteParser { public: - CompleteParser(DefsStructureParser* p) : TriggerCompleteParser(p) {} + explicit CompleteParser(DefsStructureParser* p) : TriggerCompleteParser(p) {} virtual bool doParse(const std::string& line, std::vector& lineTokens) ; virtual const char* keyword() const { return "complete"; } }; diff -Nru ecflow-4.9.0/ANode/parser/src/VariableParser.hpp ecflow-4.11.1/ANode/parser/src/VariableParser.hpp --- ecflow-4.9.0/ANode/parser/src/VariableParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/VariableParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -38,7 +38,7 @@ class VariableParser : public Parser { public: - VariableParser(DefsStructureParser* p, bool parsing_defs = false) : Parser(p), parsing_defs_(parsing_defs) {} + explicit VariableParser(DefsStructureParser* p, bool parsing_defs = false) : Parser(p), parsing_defs_(parsing_defs) {} virtual const char* keyword() const { return "edit"; } virtual bool doParse( const std::string& line, std::vector& lineTokens ); private: diff -Nru ecflow-4.9.0/ANode/parser/src/VerifyParser.hpp ecflow-4.11.1/ANode/parser/src/VerifyParser.hpp --- ecflow-4.9.0/ANode/parser/src/VerifyParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/VerifyParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,7 @@ class VerifyParser : public Parser { public: - VerifyParser(DefsStructureParser* p) : Parser(p) {} + explicit VerifyParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "verify"; } virtual bool doParse( const std::string& line, std::vector& lineTokens ); }; diff -Nru ecflow-4.9.0/ANode/parser/src/ZombieAttrParser.hpp ecflow-4.11.1/ANode/parser/src/ZombieAttrParser.hpp --- ecflow-4.9.0/ANode/parser/src/ZombieAttrParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/src/ZombieAttrParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -19,7 +19,7 @@ class ZombieAttrParser : public Parser { public: - ZombieAttrParser(DefsStructureParser* p) : Parser(p) {} + explicit ZombieAttrParser(DefsStructureParser* p) : Parser(p) {} virtual const char* keyword() const { return "zombie"; } virtual bool doParse( const std::string& line, std::vector& lineTokens ); }; diff -Nru ecflow-4.9.0/ANode/parser/test/data/bad_defs/suite/family_and_task_same_name.def ecflow-4.11.1/ANode/parser/test/data/bad_defs/suite/family_and_task_same_name.def --- ecflow-4.9.0/ANode/parser/test/data/bad_defs/suite/family_and_task_same_name.def 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/test/data/bad_defs/suite/family_and_task_same_name.def 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,5 @@ +suite s1 + task t1 + family t1 + endfamily +endsuite \ No newline at end of file diff -Nru ecflow-4.9.0/ANode/parser/test/PersistHelper.hpp ecflow-4.11.1/ANode/parser/test/PersistHelper.hpp --- ecflow-4.9.0/ANode/parser/test/PersistHelper.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/test/PersistHelper.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -27,7 +27,7 @@ /// to ensure they are the same class PersistHelper : private boost::noncopyable { public: - PersistHelper(bool compare_edit_history = false) : file_size_(0),compare_edit_history_(compare_edit_history) {} + explicit PersistHelper(bool compare_edit_history = false) : file_size_(0),compare_edit_history_(compare_edit_history) {} bool test_persist_and_reload( const Defs& theInMemoryDefs, PrintStyle::Type_t file_type_on_disk,bool do_compare = true ); bool test_defs_checkpt_and_reload( const Defs& theInMemoryDefs, bool do_compare = true ); diff -Nru ecflow-4.9.0/ANode/parser/test/TestParser.cpp ecflow-4.11.1/ANode/parser/test/TestParser.cpp --- ecflow-4.9.0/ANode/parser/test/TestParser.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/parser/test/TestParser.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -92,6 +92,16 @@ "restore from string failed for file " << relPath << "\n" << errorMsg2); BOOST_CHECK_MESSAGE(defs2 == defs,"Parse by string != parse by filename for file:\n" << relPath); } + + // Make sure all nodes can be found + std::vector all_nodes; + defs.getAllNodes(all_nodes); + for(size_t i= 0; i < all_nodes.size(); i++) { + Node* node = all_nodes[i]; + node_ptr found_node = defs.findAbsNode(node->absNodePath()); + BOOST_CHECK_MESSAGE(found_node.get(),"Could not find node " << node->debugNodePath()); + BOOST_CHECK_MESSAGE(found_node.get() == node," Expected to find " << node->debugNodePath() << " but found " << found_node->debugNodePath()); + } } } else { diff -Nru ecflow-4.9.0/ANode/src/Alias.hpp ecflow-4.11.1/ANode/src/Alias.hpp --- ecflow-4.9.0/ANode/src/Alias.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Alias.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -18,7 +18,7 @@ class Alias : public Submittable { public: - Alias(const std::string& name); + explicit Alias(const std::string& name); Alias(); Alias(const Alias&); Alias& operator=(const Alias&); diff -Nru ecflow-4.9.0/ANode/src/Attr.cpp ecflow-4.11.1/ANode/src/Attr.cpp --- ecflow-4.9.0/ANode/src/Attr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Attr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -18,8 +18,6 @@ namespace ecf { -enum Type { EVENT =0, METER=1, LABEL=2, LIMIT=3, VARIABLE=4, }; - const char* Attr::to_string( Attr::Type s ) { switch ( s ) { case Attr::EVENT: @@ -37,6 +35,9 @@ case Attr::VARIABLE: return "variable"; break; + case Attr::ALL: + return "all"; + break; case Attr::UNKNOWN: return "unknown"; break; @@ -53,6 +54,7 @@ if ( str == "label" ) return Attr::LABEL; if ( str == "limit" ) return Attr::LIMIT; if ( str == "variable" ) return Attr::VARIABLE; + if ( str == "all" ) return Attr::ALL; return Attr::UNKNOWN; } @@ -62,25 +64,27 @@ std::vector< std::string > Attr::all_attrs() { std::vector vec; - vec.reserve( 5 ); + vec.reserve( 6 ); vec.push_back( "event" ); vec.push_back( "meter" ); vec.push_back( "label" ); vec.push_back( "limit" ); vec.push_back( "variable" ); + vec.push_back( "all" ); return vec; } std::vector Attr::attrs() { std::vector vec; - vec.reserve(5); + vec.reserve(6); vec.push_back( Attr::UNKNOWN ); vec.push_back( Attr::EVENT ); vec.push_back( Attr::METER ); vec.push_back( Attr::LABEL ); vec.push_back( Attr::LIMIT ); vec.push_back( Attr::VARIABLE ); + vec.push_back( Attr::ALL ); return vec; } } diff -Nru ecflow-4.9.0/ANode/src/Attr.hpp ecflow-4.11.1/ANode/src/Attr.hpp --- ecflow-4.9.0/ANode/src/Attr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Attr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -22,7 +22,7 @@ namespace ecf { class Attr : private boost::noncopyable { public: - enum Type { UNKNOWN=0, EVENT=1, METER=2, LABEL=3, LIMIT=4, VARIABLE=5 }; + enum Type { UNKNOWN=0, EVENT=1, METER=2, LABEL=3, LIMIT=4, VARIABLE=5, ALL=6 }; static const char* to_string(Attr::Type s); static Attr::Type to_attr(const std::string& attr); diff -Nru ecflow-4.9.0/ANode/src/ChildAttrs.cpp ecflow-4.11.1/ANode/src/ChildAttrs.cpp --- ecflow-4.9.0/ANode/src/ChildAttrs.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ChildAttrs.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -76,6 +76,17 @@ boost::bind(&Label::name,_1), boost::bind(&Label::name,_2))); break; + case Attr::ALL: + sort(events_.begin(),events_.end(),boost::bind(Str::caseInsLess, + boost::bind(&Event::name_or_number,_1), + boost::bind(&Event::name_or_number,_2))); + sort(meters_.begin(),meters_.end(),boost::bind(Str::caseInsLess, + boost::bind(&Meter::name,_1), + boost::bind(&Meter::name,_2))); + sort(labels_.begin(),labels_.end(),boost::bind(Str::caseInsLess, + boost::bind(&Label::name,_1), + boost::bind(&Label::name,_2))); + break; case Attr::LIMIT: break; case Attr::VARIABLE: break; case Attr::UNKNOWN: break; diff -Nru ecflow-4.9.0/ANode/src/ChildAttrs.hpp ecflow-4.11.1/ANode/src/ChildAttrs.hpp --- ecflow-4.9.0/ANode/src/ChildAttrs.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ChildAttrs.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ class ChildAttrs : private boost::noncopyable { public: - ChildAttrs(Node* node) : node_(node) {} + explicit ChildAttrs(Node* node) : node_(node) {} ChildAttrs(const ChildAttrs&); // users must call set_node() afterwards ChildAttrs() : node_(NULL) {} ~ChildAttrs() {} diff -Nru ecflow-4.9.0/ANode/src/ClientSuiteMgr.hpp ecflow-4.11.1/ANode/src/ClientSuiteMgr.hpp --- ecflow-4.9.0/ANode/src/ClientSuiteMgr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ClientSuiteMgr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -41,7 +41,7 @@ class ClientSuiteMgr : private boost::noncopyable { public: - ClientSuiteMgr(Defs*); + explicit ClientSuiteMgr(Defs*); /// Create a client suite, and return the handle associated with the created object unsigned int create_client_suite(bool auto_add_new_suites, const std::vector& suites, const std::string& user); diff -Nru ecflow-4.9.0/ANode/src/ClientSuites.hpp ecflow-4.11.1/ANode/src/ClientSuites.hpp --- ecflow-4.9.0/ANode/src/ClientSuites.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ClientSuites.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -47,7 +47,7 @@ HSuite(const std::string& name, weak_suite_ptr p, int index = std::numeric_limits::max()) : name_(name), weak_suite_ptr_(p),index_(index) {} - HSuite(const std::string& name ) + explicit HSuite(const std::string& name ) : name_(name),index_(std::numeric_limits::max()) {} std::string name_; // suite name diff -Nru ecflow-4.9.0/ANode/src/Defs.cpp ecflow-4.11.1/ANode/src/Defs.cpp --- ecflow-4.9.0/ANode/src/Defs.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Defs.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -547,7 +547,7 @@ void Defs::sort_attributes(ecf::Attr::Type attr,bool recursive) { - if (attr == ecf::Attr::VARIABLE) server_.sort_variables(); + if (attr == ecf::Attr::VARIABLE || attr == ecf::Attr::ALL) server_.sort_variables(); if (recursive) { size_t theSuiteVecSize = suiteVec_.size(); @@ -1009,6 +1009,12 @@ return false; } +void Defs::invalidate_trigger_references() const +{ + size_t theSuiteVecSize = suiteVec_.size(); + for(size_t s = 0; s < theSuiteVecSize; s++) { suiteVec_[s]->invalidate_trigger_references();} +} + node_ptr Defs::replaceChild(const std::string& path, const defs_ptr& clientDefs, bool createNodesAsNeeded, @@ -1046,6 +1052,9 @@ errorMsg += " does not exist on the server definition. Please use option"; return node_ptr(); } + + invalidate_trigger_references(); + // HAVE a FULL match in the server // Copy over begun and suspended states, otherwise preserve state of client node @@ -1072,6 +1081,7 @@ return client_node_to_add; } + invalidate_trigger_references(); // ADD ====================================================================== // Create/Add nodes as needed for a *PARTIAL* match diff -Nru ecflow-4.9.0/ANode/src/DefsDelta.hpp ecflow-4.11.1/ANode/src/DefsDelta.hpp --- ecflow-4.9.0/ANode/src/DefsDelta.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/DefsDelta.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -34,7 +34,7 @@ public: ///========================================================================= /// *Server side* - DefsDelta(unsigned int client_state_change_no) + explicit DefsDelta(unsigned int client_state_change_no) : client_state_change_no_(client_state_change_no), server_state_change_no_(0), server_modify_change_no_(0) {} diff -Nru ecflow-4.9.0/ANode/src/Defs.hpp ecflow-4.11.1/ANode/src/Defs.hpp --- ecflow-4.9.0/ANode/src/Defs.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Defs.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -57,7 +57,7 @@ static defs_ptr create(); static defs_ptr create(const std::string& port); Defs(); - Defs(const std::string& port); // used in test, to initialise server variables + explicit Defs(const std::string& port); // used in test, to initialise server variables Defs(const Defs&); Defs& operator=(const Defs&); @@ -234,6 +234,11 @@ /// This should always succeed else something is seriously wrong bool deleteChild(Node*); + /// This called during replace. If the trigger expression(AST) has been created, then + /// the references to the nodes, will be invalidated. + /// These get generated on the fly, when referenced. + void invalidate_trigger_references() const; + /// Adopt the child specified by 'path' from the clientDef. /// If node at path already exists in this instance, it is replaced. /// if createNodesAsNeeded = false, then the path must exist on this defs @@ -442,7 +447,7 @@ // Start notification. End notification automatically signalled, Even if exception raised. class ChangeStartNotification : private boost::noncopyable { public: - ChangeStartNotification(defs_ptr defs) : defs_ptr_(defs) { defs_ptr_->notify_start();} + explicit ChangeStartNotification(defs_ptr defs) : defs_ptr_(defs) { defs_ptr_->notify_start();} ~ChangeStartNotification() { defs_ptr_->notify_end();} private: defs_ptr defs_ptr_; diff -Nru ecflow-4.9.0/ANode/src/EcfFile.hpp ecflow-4.11.1/ANode/src/EcfFile.hpp --- ecflow-4.9.0/ANode/src/EcfFile.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/EcfFile.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ // This cache serves to open the include file only once. It halves the job processing time. class IncludeFileCache : private boost::noncopyable { public: - IncludeFileCache(const std::string& path); + explicit IncludeFileCache(const std::string& path); ~IncludeFileCache(); const std::string& path() const { return path_;} bool lines(std::vector&); @@ -155,7 +155,7 @@ // The pre-processing is done in a depth first fashion (ECFLOW-673) class PreProcessor : private boost::noncopyable { public: - PreProcessor(EcfFile*); + explicit PreProcessor(EcfFile*); ~PreProcessor(); bool preProcess(std::vector& script_lines ); diff -Nru ecflow-4.9.0/ANode/src/ExprAst.cpp ecflow-4.11.1/ANode/src/ExprAst.cpp --- ecflow-4.9.0/ANode/src/ExprAst.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprAst.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -45,7 +45,7 @@ theReasonWhy = "expression "; theReasonWhy += why_expression(html); // provide additional state - theReasonWhy += " does not evaluate"; + theReasonWhy += " is false"; #ifdef DEBUG_WHY std::cout << " Ast::why reason = " << theReasonWhy << "\n"; #endif @@ -150,6 +150,11 @@ if (root_) root_->setParentNode(p); } +void AstTop::invalidate_trigger_references() const +{ + if (root_) root_->invalidate_trigger_references(); +} + ////////////////////////////////////////////////////////////////////////////////////// AstRoot::~AstRoot() { @@ -258,6 +263,12 @@ if (right_) right_->setParentNode(p); } +void AstRoot::invalidate_trigger_references() const +{ + if (left_) left_->invalidate_trigger_references(); + if (right_) right_->invalidate_trigger_references(); +} + //////////////////////////////////////////////////////////////////////////////////// void AstNot::accept(ExprAstVisitor& v) diff -Nru ecflow-4.9.0/ANode/src/ExprAst.hpp ecflow-4.11.1/ANode/src/ExprAst.hpp --- ecflow-4.9.0/ANode/src/ExprAst.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprAst.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -66,6 +66,7 @@ virtual int plus(Ast* right) const { return (value() + right->value());} virtual void setParentNode(Node*){} // traverse and set for interested nodes + virtual void invalidate_trigger_references() const {} }; class AstTop : public Ast { @@ -94,6 +95,7 @@ virtual std::string expression() const; virtual std::string why_expression(bool html = false) const; virtual void setParentNode(Node*); + virtual void invalidate_trigger_references() const; private: Ast* root_; @@ -119,6 +121,8 @@ virtual void setParentNode(Node*); virtual void set_root_name(const std::string&) {} + virtual void invalidate_trigger_references() const; + protected: std::string do_why_expression(const std::string& root,bool html) const; std::string do_bracket_why_expression(const std::string& root,bool html) const; @@ -405,7 +409,7 @@ class AstInteger : public AstLeaf { public: - AstInteger(int value) : value_(value) {} + explicit AstInteger(int value) : value_(value) {} virtual bool is_evaluateable() const { return true; } virtual bool evaluate() const { return value_; } // -1 -2 1 2 3 evaluates to true, 0 returns false @@ -426,7 +430,7 @@ class AstNodeState : public AstLeaf { public: - AstNodeState(DState::State s) : state_(s) {} + explicit AstNodeState(DState::State s) : state_(s) {} virtual void accept(ecf::ExprAstVisitor&); virtual AstNodeState* clone() const; @@ -443,7 +447,7 @@ class AstEventState : public AstLeaf { public: - AstEventState(bool b) : state_(b) {} + explicit AstEventState(bool b) : state_(b) {} virtual void accept(ecf::ExprAstVisitor&); virtual AstEventState* clone() const; @@ -470,7 +474,7 @@ class AstNode : public AstLeaf { public: - AstNode(const std::string& n) : parentNode_(NULL), nodePath_(n) {} + explicit AstNode(const std::string& n) : parentNode_(NULL), nodePath_(n) {} virtual void accept(ecf::ExprAstVisitor&); virtual AstNode* clone() const; @@ -481,6 +485,7 @@ virtual std::string expression() const; virtual std::string why_expression(bool html = false) const; virtual void setParentNode(Node* n) { parentNode_ = n; } + virtual void invalidate_trigger_references() const { ref_node_.reset();} static std::string stype() { return "node";} const std::string& nodePath() const { return nodePath_;} @@ -518,6 +523,7 @@ virtual std::string expression() const; virtual std::string why_expression(bool html = false) const; virtual void setParentNode(Node* n) { parentNode_ = n; } + virtual void invalidate_trigger_references() const { ref_node_.reset();} static std::string stype() { return "flag";} const std::string& nodePath() const { return nodePath_;} @@ -565,6 +571,7 @@ virtual std::string expression() const; virtual std::string why_expression(bool html = false) const; virtual void setParentNode(Node* n) { parentNode_ = n; } + virtual void invalidate_trigger_references() const { ref_node_.reset();} virtual int minus(Ast* right) const; virtual int plus(Ast* right) const; @@ -595,7 +602,7 @@ // ** i.e "2 == (((:YMD / 100 ) % 100) % 3" class AstParentVariable : public AstLeaf { public: - AstParentVariable(const std::string& variablename) + explicit AstParentVariable(const std::string& variablename) : parentNode_(NULL), name_(variablename) {} virtual std::string name() const { return name_;} @@ -616,6 +623,7 @@ virtual std::string expression() const; virtual std::string why_expression(bool html = false) const; virtual void setParentNode(Node* n) { parentNode_ = n; } + virtual void invalidate_trigger_references() const { ref_node_.reset();} virtual int minus(Ast* right) const; virtual int plus(Ast* right) const; @@ -635,7 +643,7 @@ // Helper class class VariableHelper : private boost::noncopyable { public: - VariableHelper(const AstVariable* astVariable); + explicit VariableHelper(const AstVariable* astVariable); VariableHelper(const AstVariable* astVariable, std::string& errorMsg); int value() const; diff -Nru ecflow-4.9.0/ANode/src/ExprAstVisitor.hpp ecflow-4.11.1/ANode/src/ExprAstVisitor.hpp --- ecflow-4.9.0/ANode/src/ExprAstVisitor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprAstVisitor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -82,7 +82,7 @@ class AstResolveVisitor : public ExprAstVisitor { public: - AstResolveVisitor(const Node* ); + explicit AstResolveVisitor(const Node* ); virtual ~AstResolveVisitor(); const std::string& errorMsg() const { return errorMsg_;} @@ -120,7 +120,7 @@ class AstCollateNodesVisitor : public ExprAstVisitor { public: - AstCollateNodesVisitor( std::set& ); + explicit AstCollateNodesVisitor( std::set& ); virtual ~AstCollateNodesVisitor(); virtual void visitTop(AstTop*){} diff -Nru ecflow-4.9.0/ANode/src/ExprDuplicate.cpp ecflow-4.11.1/ANode/src/ExprDuplicate.cpp --- ecflow-4.9.0/ANode/src/ExprDuplicate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprDuplicate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -21,6 +21,7 @@ #include "ExprDuplicate.hpp" #include "ExprAst.hpp" +#include "Ecf.hpp" //////////////////////////////////////////////////////////////////////////// using namespace std; @@ -37,14 +38,23 @@ ExprDuplicate::~ExprDuplicate() { - // cout << "ExprDuplicate::~ExprDuplicate()\n"; + //cout << "ExprDuplicate::~ExprDuplicate: server(" << Ecf::server() << ") " << duplicate_expr.size() << " *****************************************************************\n"; BOOST_FOREACH(my_map::value_type i, duplicate_expr) { + //cout << " deleting: " << i.first << " :" << i.second << "\n"; delete i.second; i.second = NULL; } duplicate_expr.clear(); } +void ExprDuplicate::dump(const std::string& msg ) +{ + cout << "ExprDuplicate::dump server(" << Ecf::server() << ") " << msg << "\n"; + BOOST_FOREACH(const my_map::value_type& i, duplicate_expr) { + cout << " " << i.first << " :" << i.second << "\n"; + } +} + std::auto_ptr ExprDuplicate::find(const std::string& expr) { my_map::const_iterator it = duplicate_expr.find(expr); @@ -57,5 +67,8 @@ void ExprDuplicate::add(const std::string& expr,AstTop* ast) { assert(!expr.empty() && ast); - duplicate_expr.insert( std::make_pair(expr,ast->clone())); + AstTop* clone = ast->clone(); + duplicate_expr.insert( std::make_pair(expr,clone)); + + //cout << "ExprDuplicate::add: server(" << Ecf::server() << ") " << expr << " :" << clone << " " << duplicate_expr.size() << "\n"; } diff -Nru ecflow-4.9.0/ANode/src/ExprDuplicate.hpp ecflow-4.11.1/ANode/src/ExprDuplicate.hpp --- ecflow-4.9.0/ANode/src/ExprDuplicate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprDuplicate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -34,6 +34,9 @@ ExprDuplicate() {} ~ExprDuplicate(); + // for debug only + static void dump(const std::string& msg ); + // Find the expr in the map, if found returns a CLONED ast, else NULL static std::auto_ptr find(const std::string& expr); diff -Nru ecflow-4.9.0/ANode/src/Expression.hpp ecflow-4.11.1/ANode/src/Expression.hpp --- ecflow-4.9.0/ANode/src/Expression.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Expression.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -34,7 +34,7 @@ public: enum ExprType { FIRST, AND, OR }; - PartExpression(const std::string& expression) + explicit PartExpression(const std::string& expression) : exp_(expression), exp_type_(FIRST) {} PartExpression(const std::string& expression, bool and_type) @@ -84,8 +84,8 @@ // Use compiler , generated destructor, assignment, copy constructor class Expression { public: - Expression(const std::string& expression); - Expression(const PartExpression& ); + explicit Expression(const std::string& expression); + explicit Expression(const PartExpression& ); Expression(); Expression(const Expression& rhs); diff -Nru ecflow-4.9.0/ANode/src/ExprParser.cpp ecflow-4.11.1/ANode/src/ExprParser.cpp --- ecflow-4.9.0/ANode/src/ExprParser.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprParser.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -214,7 +214,7 @@ // ‘>>’ Sequence/concatenation // ‘|’ alternate // ‘-‘ not - definition(ExpressionGrammer const& /*self*/) + explicit definition(ExpressionGrammer const& /*self*/) { nodename = leaf_node_d[ diff -Nru ecflow-4.9.0/ANode/src/ExprParser.hpp ecflow-4.11.1/ANode/src/ExprParser.hpp --- ecflow-4.9.0/ANode/src/ExprParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ExprParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -26,7 +26,7 @@ /// It will own the AST unless specifically released calling ast(); class ExprParser : private boost::noncopyable { public: - ExprParser(const std::string& expression); + explicit ExprParser(const std::string& expression); /// Parse the expression, return true if parse OK false otherwise /// if false is returned, and error message is returned @@ -48,7 +48,7 @@ // But the simple expression do form a very large subset class SimpleExprParser : private boost::noncopyable { public: - SimpleExprParser(const std::string& expression) : expr_(expression) {} + explicit SimpleExprParser(const std::string& expression) : expr_(expression) {} /// Parse the expression, return true if parse OK false otherwise bool doParse(); diff -Nru ecflow-4.9.0/ANode/src/Family.hpp ecflow-4.11.1/ANode/src/Family.hpp --- ecflow-4.9.0/ANode/src/Family.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Family.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,7 @@ class Family : public NodeContainer { public: - Family( const std::string& name ) : NodeContainer(name),fam_gen_variables_(NULL) {} + explicit Family( const std::string& name ) : NodeContainer(name),fam_gen_variables_(NULL) {} Family() : fam_gen_variables_(NULL) {} Family(const Family& rhs) : NodeContainer(rhs), fam_gen_variables_(NULL) {} Family& operator=(const Family&); @@ -75,7 +75,7 @@ // This improves client->server down load times by avoiding thousands of string constructions class FamGenVariables : private boost::noncopyable { public: - FamGenVariables(const Family*); + explicit FamGenVariables(const Family*); void update_generated_variables() const; const Variable& findGenVariable(const std::string& name) const; diff -Nru ecflow-4.9.0/ANode/src/Flag.hpp ecflow-4.11.1/ANode/src/Flag.hpp --- ecflow-4.9.0/ANode/src/Flag.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Flag.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -63,7 +63,7 @@ QUEUELIMIT = 11, // Node ( NOT USED currently) WAIT = 12, // task* Set/cleared but never queried ? ( NOT USED currently ) LOCKED = 13, // Server ( NOT USED currently) - ZOMBIE = 14, // task* Set/cleared but never queried ? ( NOT USED currently ) + ZOMBIE = 14, // task* indicates zombie process NO_REQUE_IF_SINGLE_TIME_DEP = 15, // NOT_SET = 16 }; diff -Nru ecflow-4.9.0/ANode/src/InLimit.hpp ecflow-4.11.1/ANode/src/InLimit.hpp --- ecflow-4.9.0/ANode/src/InLimit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/InLimit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -35,7 +35,7 @@ // Inlimit of the same name specified on a task take priority over the family class InLimit { public: - InLimit(const std::string& name, + explicit InLimit(const std::string& name, const std::string& pathToNode = std::string(), int tokens = 1); InLimit() : tokens_(1) {} diff -Nru ecflow-4.9.0/ANode/src/InLimitMgr.hpp ecflow-4.11.1/ANode/src/InLimitMgr.hpp --- ecflow-4.9.0/ANode/src/InLimitMgr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/InLimitMgr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -38,7 +38,7 @@ // class InLimitMgr { public: - InLimitMgr(Node* n) : node_(n) {} + explicit InLimitMgr(Node* n) : node_(n) {} InLimitMgr(const InLimitMgr& rhs) : node_(NULL),inLimitVec_(rhs.inLimitVec_){} InLimitMgr() : node_(NULL) {} diff -Nru ecflow-4.9.0/ANode/src/Jobs.hpp ecflow-4.11.1/ANode/src/Jobs.hpp --- ecflow-4.9.0/ANode/src/Jobs.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Jobs.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -55,9 +55,9 @@ /// Note: in real life test 99% of job generation is done after child command class Jobs : private boost::noncopyable { public: - Jobs(const defs_ptr& d) : defs_(d.get()), node_(NULL) {} - Jobs(Defs* d) : defs_(d), node_(NULL) {} - Jobs(Node* d) : defs_(NULL), node_(d) {} + explicit Jobs(const defs_ptr& d) : defs_(d.get()), node_(NULL) {} + explicit Jobs(Defs* d) : defs_(d), node_(NULL) {} + explicit Jobs(Node* d) : defs_(NULL), node_(d) {} bool generate( JobsParam& ) const; bool generate() const; diff -Nru ecflow-4.9.0/ANode/src/JobsParam.hpp ecflow-4.11.1/ANode/src/JobsParam.hpp --- ecflow-4.9.0/ANode/src/JobsParam.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/JobsParam.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -26,7 +26,7 @@ class JobsParam : private boost::noncopyable { public: // This constructor is used in test - JobsParam(bool createJobs = false) + explicit JobsParam(bool createJobs = false) : timed_out_of_job_generation_(false), createJobs_(createJobs), spawnJobs_(false), submitJobsInterval_(60){} diff -Nru ecflow-4.9.0/ANode/src/Memento.hpp ecflow-4.11.1/ANode/src/Memento.hpp --- ecflow-4.9.0/ANode/src/Memento.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Memento.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -77,7 +77,7 @@ // The mementos are then applied to this single node. class CompoundMemento { public: - CompoundMemento(const std::string& absNodePath) + explicit CompoundMemento(const std::string& absNodePath) : clear_attributes_(false),absNodePath_(absNodePath) {} CompoundMemento() : clear_attributes_(false) {} // for serialization @@ -107,7 +107,7 @@ class StateMemento : public Memento { public: - StateMemento(NState::State state) : state_(state) {} + explicit StateMemento(NState::State state) : state_(state) {} StateMemento() : state_(NState::UNKNOWN) {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -127,7 +127,7 @@ class OrderMemento : public Memento { public: - OrderMemento(const std::vector& order) : order_(order) {} + explicit OrderMemento(const std::vector& order) : order_(order) {} OrderMemento() {} private: virtual void do_incremental_defs_sync(Defs* defs,std::vector& aspects,bool f) const { defs->set_memento(this,aspects,f);} @@ -150,7 +150,7 @@ class ChildrenMemento : public Memento { public: - ChildrenMemento(const std::vector& children) : children_(children) {} + explicit ChildrenMemento(const std::vector& children) : children_(children) {} ChildrenMemento() {} private: virtual void do_incremental_suite_sync(Suite* s,std::vector& aspects,bool f) const { s->set_memento(this,aspects,f);} @@ -173,7 +173,7 @@ class AliasChildrenMemento : public Memento { public: - AliasChildrenMemento(const std::vector& children) : children_(children) {} + explicit AliasChildrenMemento(const std::vector& children) : children_(children) {} AliasChildrenMemento() {} private: virtual void do_incremental_task_sync(Task* t,std::vector& aspects,bool f) const { t->set_memento(this,aspects,f);} @@ -194,7 +194,7 @@ class AliasNumberMemento : public Memento { public: - AliasNumberMemento(unsigned int alias_no ) : alias_no_(alias_no) {} + explicit AliasNumberMemento(unsigned int alias_no ) : alias_no_(alias_no) {} AliasNumberMemento() : alias_no_(0) {} private: virtual void do_incremental_task_sync(Task* t,std::vector& aspects,bool f) const { t->set_memento(this,aspects,f);} @@ -213,7 +213,7 @@ class SuspendedMemento : public Memento { public: - SuspendedMemento(bool suspended) : suspended_(suspended) {} + explicit SuspendedMemento(bool suspended) : suspended_(suspended) {} SuspendedMemento() : suspended_(false) {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -232,7 +232,7 @@ class ServerStateMemento : public Memento { public: - ServerStateMemento(SState::State s) : state_(s) {} + explicit ServerStateMemento(SState::State s) : state_(s) {} ServerStateMemento() : state_(SState::HALTED) {} private: virtual void do_incremental_defs_sync(Defs* defs,std::vector& aspects,bool f) const { defs->set_memento(this,aspects,f);} @@ -250,7 +250,7 @@ class ServerVariableMemento : public Memento { public: - ServerVariableMemento(const std::vector& vec) : serverEnv_(vec) {} + explicit ServerVariableMemento(const std::vector& vec) : serverEnv_(vec) {} ServerVariableMemento() {} private: virtual void do_incremental_defs_sync(Defs* defs,std::vector& aspects,bool f) const { defs->set_memento(this,aspects,f);} @@ -268,7 +268,7 @@ class NodeDefStatusDeltaMemento : public Memento { public: - NodeDefStatusDeltaMemento(DState::State state) : state_(state) {} + explicit NodeDefStatusDeltaMemento(DState::State state) : state_(state) {} NodeDefStatusDeltaMemento() : state_(DState::UNKNOWN) {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -286,7 +286,7 @@ class NodeEventMemento : public Memento { public: - NodeEventMemento( const Event& e) : event_(e) {} + explicit NodeEventMemento( const Event& e) : event_(e) {} NodeEventMemento(){} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -305,7 +305,7 @@ class NodeMeterMemento : public Memento { public: - NodeMeterMemento(const Meter& e) : meter_(e) {} + explicit NodeMeterMemento(const Meter& e) : meter_(e) {} NodeMeterMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -324,7 +324,7 @@ class NodeLabelMemento : public Memento { public: - NodeLabelMemento( const Label& e) : label_(e) {} + explicit NodeLabelMemento( const Label& e) : label_(e) {} NodeLabelMemento(){} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -344,7 +344,7 @@ class NodeTriggerMemento : public Memento { public: - NodeTriggerMemento(const Expression& e) : exp_(e) {} + explicit NodeTriggerMemento(const Expression& e) : exp_(e) {} NodeTriggerMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -362,7 +362,7 @@ class NodeCompleteMemento : public Memento { public: - NodeCompleteMemento(const Expression& e) : exp_(e) {} + explicit NodeCompleteMemento(const Expression& e) : exp_(e) {} NodeCompleteMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -380,7 +380,7 @@ class NodeRepeatMemento : public Memento { public: - NodeRepeatMemento( const Repeat& e ) : repeat_(e) {} + explicit NodeRepeatMemento( const Repeat& e ) : repeat_(e) {} NodeRepeatMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -398,7 +398,7 @@ class NodeLimitMemento : public Memento { public: - NodeLimitMemento( const Limit& e) : limit_( e ) {} + explicit NodeLimitMemento( const Limit& e) : limit_( e ) {} NodeLimitMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -416,7 +416,7 @@ class NodeInLimitMemento : public Memento { public: - NodeInLimitMemento( const InLimit& e) : inlimit_( e ) {} + explicit NodeInLimitMemento( const InLimit& e) : inlimit_( e ) {} NodeInLimitMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -435,7 +435,7 @@ class NodeVariableMemento : public Memento { public: - NodeVariableMemento( const Variable& e) : var_(e) {} + explicit NodeVariableMemento( const Variable& e) : var_(e) {} NodeVariableMemento(){} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -453,7 +453,7 @@ class NodeLateMemento : public Memento { public: - NodeLateMemento( const ecf::LateAttr& e) : late_(e) {} + explicit NodeLateMemento( const ecf::LateAttr& e) : late_(e) {} NodeLateMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -471,7 +471,7 @@ class FlagMemento : public Memento { public: - FlagMemento( const ecf::Flag& e) : flag_(e) {} + explicit FlagMemento( const ecf::Flag& e) : flag_(e) {} FlagMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -491,7 +491,7 @@ class NodeTodayMemento : public Memento { public: - NodeTodayMemento( const ecf::TodayAttr& attr) : attr_(attr) {} + explicit NodeTodayMemento( const ecf::TodayAttr& attr) : attr_(attr) {} NodeTodayMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -510,7 +510,7 @@ class NodeTimeMemento : public Memento { public: - NodeTimeMemento( const ecf::TimeAttr& attr) : attr_(attr) {} + explicit NodeTimeMemento( const ecf::TimeAttr& attr) : attr_(attr) {} NodeTimeMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -529,7 +529,7 @@ class NodeDayMemento : public Memento { public: - NodeDayMemento( const DayAttr& attr) : attr_(attr) {} + explicit NodeDayMemento( const DayAttr& attr) : attr_(attr) {} NodeDayMemento(){} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -548,7 +548,7 @@ class NodeCronMemento : public Memento { public: - NodeCronMemento( const ecf::CronAttr& attr) : attr_(attr) {} + explicit NodeCronMemento( const ecf::CronAttr& attr) : attr_(attr) {} NodeCronMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -567,7 +567,7 @@ class NodeDateMemento : public Memento { public: - NodeDateMemento( const DateAttr& attr) : attr_(attr) {} + explicit NodeDateMemento( const DateAttr& attr) : attr_(attr) {} NodeDateMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -586,7 +586,7 @@ class NodeZombieMemento : public Memento { public: - NodeZombieMemento(const ZombieAttr& attr) : attr_(attr) {} + explicit NodeZombieMemento(const ZombieAttr& attr) : attr_(attr) {} NodeZombieMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -605,7 +605,7 @@ class NodeVerifyMemento : public Memento { public: - NodeVerifyMemento(const std::vector& attr) : verifys_(attr) {} + explicit NodeVerifyMemento(const std::vector& attr) : verifys_(attr) {} NodeVerifyMemento() {} private: virtual void do_incremental_node_sync(Node* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -657,7 +657,7 @@ class SuiteClockMemento : public Memento { public: - SuiteClockMemento( const ClockAttr& c ) : clockAttr_(c) {} + explicit SuiteClockMemento( const ClockAttr& c ) : clockAttr_(c) {} SuiteClockMemento() {} private: virtual void do_incremental_suite_sync(Suite* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -675,7 +675,7 @@ class SuiteBeginDeltaMemento : public Memento { public: - SuiteBeginDeltaMemento(bool begun) : begun_(begun) {} + explicit SuiteBeginDeltaMemento(bool begun) : begun_(begun) {} SuiteBeginDeltaMemento() : begun_(false) {} private: virtual void do_incremental_suite_sync(Suite* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} @@ -693,7 +693,7 @@ class SuiteCalendarMemento : public Memento { public: - SuiteCalendarMemento(const ecf::Calendar& cal) : calendar_(cal) {} + explicit SuiteCalendarMemento(const ecf::Calendar& cal) : calendar_(cal) {} SuiteCalendarMemento() {} private: virtual void do_incremental_suite_sync(Suite* n,std::vector& aspects,bool f) const { n->set_memento(this,aspects,f);} diff -Nru ecflow-4.9.0/ANode/src/MiscAttrs.hpp ecflow-4.11.1/ANode/src/MiscAttrs.hpp --- ecflow-4.9.0/ANode/src/MiscAttrs.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/MiscAttrs.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -27,7 +27,7 @@ class MiscAttrs : private boost::noncopyable { public: - MiscAttrs(Node* node) : node_(node) {} + explicit MiscAttrs(Node* node) : node_(node) {} MiscAttrs(const MiscAttrs& rhs) : node_(NULL),zombies_(rhs.zombies_),verifys_(rhs.verifys_) {} MiscAttrs() : node_(NULL) {} diff -Nru ecflow-4.9.0/ANode/src/NodeContainer.cpp ecflow-4.11.1/ANode/src/NodeContainer.cpp --- ecflow-4.9.0/ANode/src/NodeContainer.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/NodeContainer.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -419,6 +419,13 @@ return false; } +void NodeContainer::invalidate_trigger_references() const +{ + Node::invalidate_trigger_references(); + size_t node_vec_size = nodeVec_.size(); + for(size_t t = 0; t < node_vec_size; t++) { nodeVec_[t]->invalidate_trigger_references(); } +} + bool NodeContainer::resolveDependencies(JobsParam& jobsParam) { // Don't evaluate children unless parent is free. BOMB out early for this case. @@ -522,11 +529,11 @@ Task* theTaskChild = theChild->isTask(); if ( theTaskChild ) { - task_ptr theTask = findTask(theChild->name()); + node_ptr theTask = find_by_name(theChild->name()); if (!theTask.get()) return true; std::stringstream ss; - ss << "Task of name " << theChild->name() << " already exist in container node " << name() ; + ss << "Task/Family of name " << theChild->name() << " already exist in container node " << name() ; errorMsg += ss.str(); return false; } @@ -534,11 +541,11 @@ Family* theFamilyChild = theChild->isFamily(); if ( theFamilyChild ) { - family_ptr theFamily = findFamily(theChild->name()); + node_ptr theFamily = find_by_name(theChild->name()); if (!theFamily.get()) return true; std::stringstream ss; - ss << "Family of name " << theChild->name() << " already exist in container node " << name() ; + ss << "Family/Task of name " << theChild->name() << " already exist in container node " << name() ; errorMsg += ss.str(); return false; } @@ -572,9 +579,9 @@ task_ptr NodeContainer::add_task(const std::string& task_name) { - if (findTask(task_name).get()) { + if (find_by_name(task_name).get()) { std::stringstream ss; - ss << "Add Task failed: A task of name '" << task_name << "' already exist on node " << debugNodePath(); + ss << "Add Task failed: A task/family of name '" << task_name << "' already exist on node " << debugNodePath(); throw std::runtime_error( ss.str() ); } task_ptr the_task = Task::create(task_name); @@ -584,9 +591,9 @@ family_ptr NodeContainer::add_family(const std::string& family_name) { - if (findFamily(family_name).get()) { + if (find_by_name(family_name).get()) { std::stringstream ss; - ss << "Add Family failed: A Family of name '" << family_name << "' already exist on node " << debugNodePath(); + ss << "Add Family failed: A Family/Task of name '" << family_name << "' already exist on node " << debugNodePath(); throw std::runtime_error( ss.str() ); } family_ptr the_family = Family::create(family_name); @@ -596,9 +603,9 @@ void NodeContainer::addTask(task_ptr t,size_t position) { - if (findTask(t->name()).get()) { + if (find_by_name(t->name()).get()) { std::stringstream ss; - ss << "Add Task failed: A task of name '" << t->name() << "' already exist on node " << debugNodePath(); + ss << "Add Task failed: A Task/Family of name '" << t->name() << "' already exist on node " << debugNodePath(); throw std::runtime_error( ss.str() ); } add_task_only( t, position); @@ -643,9 +650,9 @@ void NodeContainer::addFamily(family_ptr f,size_t position) { - if (findFamily(f->name()).get()) { + if (find_by_name(f->name()).get()) { std::stringstream ss; - ss << "Add Family failed: A Family of name '" << f->name() << "' already exist on node " << debugNodePath(); + ss << "Add Family failed: A Family/Task of name '" << f->name() << "' already exist on node " << debugNodePath(); throw std::runtime_error( ss.str() ); } add_family_only( f, position ); @@ -776,6 +783,17 @@ } } +node_ptr NodeContainer::find_by_name(const std::string& name) const +{ + size_t node_vec_size = nodeVec_.size(); + for(size_t t = 0; t < node_vec_size; t++) { + if (nodeVec_[t]->name() == name) { + return nodeVec_[t]; + } + } + return node_ptr(); +} + family_ptr NodeContainer::findFamily(const std::string& familyName) const { size_t node_vec_size = nodeVec_.size(); diff -Nru ecflow-4.9.0/ANode/src/NodeContainer.hpp ecflow-4.11.1/ANode/src/NodeContainer.hpp --- ecflow-4.9.0/ANode/src/NodeContainer.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/NodeContainer.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ protected: NodeContainer& operator=(const NodeContainer&); public: - NodeContainer( const std::string& name ); + explicit NodeContainer( const std::string& name ); NodeContainer(const NodeContainer& ); NodeContainer(); virtual ~NodeContainer(); @@ -49,6 +49,7 @@ virtual void calendarChanged(const ecf::Calendar&,std::vector& auto_cancelled_nodes,const ecf::LateAttr* inherited_late); virtual bool resolveDependencies(JobsParam& ); virtual bool check(std::string& errorMsg, std::string& warningMsg) const; + virtual void invalidate_trigger_references() const; task_ptr add_task(const std::string& task_name); family_ptr add_family(const std::string& family_name); @@ -61,10 +62,10 @@ virtual node_ptr findImmediateChild(const std::string& name,size_t& child_pos) const; virtual node_ptr find_node_up_the_tree(const std::string& name) const; - virtual node_ptr - find_relative_node(const std::vector& pathToNode); + virtual node_ptr find_relative_node(const std::vector& pathToNode); void find_closest_matching_node( const std::vector< std::string >& pathToNode, int indexIntoPathNode, node_ptr& closest_matching_node ); + node_ptr find_by_name(const std::string& name) const; family_ptr findFamily(const std::string& familyName) const; task_ptr findTask(const std::string& taskName) const; void getAllFamilies(std::vector&) const; diff -Nru ecflow-4.9.0/ANode/src/Node.cpp ecflow-4.11.1/ANode/src/Node.cpp --- ecflow-4.9.0/ANode/src/Node.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Node.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -1460,8 +1460,10 @@ void Node::read_state(const std::string& line,const std::vector& lineTokens) { + // 0 1 2 + // task name # std::string token; - for(size_t i = 0; i < lineTokens.size(); i++) { + for(size_t i = 3; i < lineTokens.size(); i++) { token.clear(); if (lineTokens[i].find("state:") != std::string::npos ) { if (!Extract::split_get_second(lineTokens[i],token)) throw std::runtime_error( "Node::read_state Invalid state specified for suite " + name()); @@ -2051,6 +2053,20 @@ return NULL; } +void Node::invalidate_trigger_references() const +{ + if (triggerExpr_) { + if (triggerExpr_->get_ast()) { + triggerExpr_->get_ast()->invalidate_trigger_references(); + } + } + if (completeExpr_ ) { + if (completeExpr_->get_ast()) { + completeExpr_->get_ast()->invalidate_trigger_references(); + } + } +} + node_ptr Node::remove() { SuiteChanged0 changed(shared_from_this()); @@ -2200,6 +2216,15 @@ boost::bind(&Variable::name,_1), boost::bind(&Variable::name,_2))); break; + case Attr::ALL: + if (child_attrs_) child_attrs_->sort_attributes(attr); + sort(limitVec_.begin(),limitVec_.end(),boost::bind(Str::caseInsLess, + boost::bind(&Limit::name,_1), + boost::bind(&Limit::name,_2))); + sort(varVec_.begin(),varVec_.end(),boost::bind(Str::caseInsLess, + boost::bind(&Variable::name,_1), + boost::bind(&Variable::name,_2))); + break; case Attr::UNKNOWN: break; default: break; } diff -Nru ecflow-4.9.0/ANode/src/Node.hpp ecflow-4.11.1/ANode/src/Node.hpp --- ecflow-4.9.0/ANode/src/Node.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Node.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -333,9 +333,9 @@ void get_time_resolution_for_simulation(boost::posix_time::time_duration& resol) const; void get_max_simulation_duration(boost::posix_time::time_duration& resol) const; - /// A hierarchical function + /// hierarchical function virtual bool hasAutoCancel() const { return (autoCancel_) ? true : false;} - + virtual void invalidate_trigger_references() const; // Access functions: ====================================================== const std::string& name() const { return name_; } diff -Nru ecflow-4.9.0/ANode/src/ResolveExternsVisitor.hpp ecflow-4.11.1/ANode/src/ResolveExternsVisitor.hpp --- ecflow-4.9.0/ANode/src/ResolveExternsVisitor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ResolveExternsVisitor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -26,7 +26,7 @@ class ResolveExternsVisitor : public NodeTreeVisitor { public: - ResolveExternsVisitor(Defs*); + explicit ResolveExternsVisitor(Defs*); virtual bool traverseObjectStructureViaVisitors() const { return true;} virtual void visitDefs(Defs*); diff -Nru ecflow-4.9.0/ANode/src/ServerState.cpp ecflow-4.11.1/ANode/src/ServerState.cpp --- ecflow-4.9.0/ANode/src/ServerState.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ServerState.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -270,16 +270,16 @@ } } -const std::string& ServerState::find_user_variable(const std::string& theVarName) const +bool ServerState::find_user_variable(const std::string& name,std::string& value) const { std::vector::const_iterator user_var_end = user_variables_.end(); for(std::vector::const_iterator i = user_variables_.begin(); i!=user_var_end; ++i) { - if ((*i).name() == theVarName) { - LOG_ASSERT(!(*i).theValue().empty(),""); - return (*i).theValue(); + if ((*i).name() == name) { + value = (*i).theValue(); + return true; } } - return Str::EMPTY(); + return false; } const std::string& ServerState::find_variable(const std::string& theVarName) const @@ -289,7 +289,6 @@ for(std::vector::const_iterator i = user_variables_.begin(); i!=user_var_end; ++i) { if ((*i).name() == theVarName) { // cerr << "FOUND '" << (*i).first << "' '" << (*i).second << "'\n"; - LOG_ASSERT(!(*i).theValue().empty(),""); return (*i).theValue(); } } @@ -314,7 +313,6 @@ std::vector::const_iterator var_end = user_variables_.end(); for(std::vector::const_iterator i = user_variables_.begin(); i!=var_end; ++i) { if ((*i).name() == name) { - LOG_ASSERT(!(*i).theValue().empty(),""); // if ((*i).theValue().empty() ) std::cout << (*i).name() << " has a empty value\n"; return (*i); } diff -Nru ecflow-4.9.0/ANode/src/ServerState.hpp ecflow-4.11.1/ANode/src/ServerState.hpp --- ecflow-4.9.0/ANode/src/ServerState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/ServerState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -35,7 +35,7 @@ class ServerState { public: ServerState(); - ServerState(const std::string& port); // used in test to init server variables + explicit ServerState(const std::string& port); // used in test to init server variables ServerState(const ServerState&); /// Check pointing, SAVES server variables, since they are visualised by client like ecflow_ui @@ -72,7 +72,7 @@ void set_user_variables(const std::vector& e); const std::vector& user_variables() const { return user_variables_; } - const std::string& find_user_variable(const std::string& name) const; + bool find_user_variable(const std::string& name, std::string& value ) const; // Search user variables, and then server variables const std::string& find_variable(const std::string& name) const; diff -Nru ecflow-4.9.0/ANode/src/Submittable.cpp ecflow-4.11.1/ANode/src/Submittable.cpp --- ecflow-4.9.0/ANode/src/Submittable.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Submittable.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -602,7 +602,7 @@ class JobCreationTimer : private boost::noncopyable { public: - JobCreationTimer(Submittable* sub) : enabled_(false),failed_(false), sub_(sub) {} + explicit JobCreationTimer(Submittable* sub) : enabled_(false),failed_(false), sub_(sub) {} ~JobCreationTimer() { if (enabled_) { std::cout << " " << sub_->absNodePath(); diff -Nru ecflow-4.9.0/ANode/src/Submittable.hpp ecflow-4.11.1/ANode/src/Submittable.hpp --- ecflow-4.9.0/ANode/src/Submittable.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Submittable.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -181,7 +181,7 @@ // This improves client->server down load times by avoiding thousands of string constructions class SubGenVariables : private boost::noncopyable { public: - SubGenVariables(const Submittable*); + explicit SubGenVariables(const Submittable*); void update_generated_variables() const; diff -Nru ecflow-4.9.0/ANode/src/SuiteChanged.hpp ecflow-4.11.1/ANode/src/SuiteChanged.hpp --- ecflow-4.9.0/ANode/src/SuiteChanged.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/SuiteChanged.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -34,7 +34,7 @@ class SuiteChanged : private boost::noncopyable { public: - SuiteChanged(suite_ptr s); + explicit SuiteChanged(suite_ptr s); ~SuiteChanged(); private: weak_suite_ptr suite_; @@ -44,7 +44,7 @@ class SuiteChanged0 : private boost::noncopyable { public: - SuiteChanged0(node_ptr s); + explicit SuiteChanged0(node_ptr s); ~SuiteChanged0(); private: weak_node_ptr node_; @@ -56,7 +56,7 @@ class SuiteChanged1 : private boost::noncopyable { public: - SuiteChanged1(Suite* s); + explicit SuiteChanged1(Suite* s); ~SuiteChanged1(); private: Suite* suite_; diff -Nru ecflow-4.9.0/ANode/src/Suite.cpp ecflow-4.11.1/ANode/src/Suite.cpp --- ecflow-4.9.0/ANode/src/Suite.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Suite.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -298,7 +298,7 @@ std::ostream& Suite::print(std::ostream& os) const { - Indentor::indent(os) << "suite " << name(); + Indentor::indent(os) << "suite " << name(); if (!PrintStyle::defsStyle()) { std::string st = write_state(); if (!st.empty()) os << " #" << st; diff -Nru ecflow-4.9.0/ANode/src/Suite.hpp ecflow-4.11.1/ANode/src/Suite.hpp --- ecflow-4.9.0/ANode/src/Suite.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Suite.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -24,7 +24,7 @@ class Suite : public NodeContainer { public: - Suite( const std::string& name ) + explicit Suite( const std::string& name ) : NodeContainer(name), defs_(NULL), begun_(false), @@ -156,7 +156,7 @@ // This improves client->server down load times by avoiding thousands of string constructions class SuiteGenVariables : private boost::noncopyable { public: - SuiteGenVariables(const Suite*); + explicit SuiteGenVariables(const Suite*); void force_update() { force_update_ = true;} void update_generated_variables() const; diff -Nru ecflow-4.9.0/ANode/src/Task.cpp ecflow-4.11.1/ANode/src/Task.cpp --- ecflow-4.9.0/ANode/src/Task.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Task.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -224,10 +224,10 @@ } } - // copy over events, meters, labels - BOOST_FOREACH(const Meter& meter, meters()) { alias->addMeter(meter); } - BOOST_FOREACH(const Event& event, events()) { alias->addEvent(event); } - BOOST_FOREACH(const Label& label, labels()) { alias->addLabel(label); } + // copy over events, meters, labels, but clear state (ECFLOW-1278) + BOOST_FOREACH(Meter meter, meters()) { meter.reset(); alias->addMeter(meter); } + BOOST_FOREACH(Event event, events()) { event.reset(); alias->addEvent(event); } + BOOST_FOREACH(Label label, labels()) { label.reset(); alias->addLabel(label); } // Add user_variables as variables. Note: to reduce memory we could choose // to only add those variable that have been changed/added. However this diff -Nru ecflow-4.9.0/ANode/src/Task.hpp ecflow-4.11.1/ANode/src/Task.hpp --- ecflow-4.9.0/ANode/src/Task.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/Task.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -18,7 +18,7 @@ class Task : public Submittable { public: - Task( const std::string& name ) + explicit Task( const std::string& name ) : Submittable(name), order_state_change_no_(0), add_remove_state_change_no_(0), diff -Nru ecflow-4.9.0/ANode/src/TaskScriptGenerator.hpp ecflow-4.11.1/ANode/src/TaskScriptGenerator.hpp --- ecflow-4.9.0/ANode/src/TaskScriptGenerator.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/TaskScriptGenerator.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ class TaskScriptGenerator : private boost::noncopyable { public: - TaskScriptGenerator(const Task*); + explicit TaskScriptGenerator(const Task*); void generate(const std::map& override); diff -Nru ecflow-4.9.0/ANode/src/TimeDepAttrs.hpp ecflow-4.11.1/ANode/src/TimeDepAttrs.hpp --- ecflow-4.9.0/ANode/src/TimeDepAttrs.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/src/TimeDepAttrs.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -30,7 +30,7 @@ class TimeDepAttrs { public: - TimeDepAttrs(Node* node) : node_(node) {} + explicit TimeDepAttrs(Node* node) : node_(node) {} TimeDepAttrs(const TimeDepAttrs&); // users must call set_node() afterwards TimeDepAttrs() : node_(NULL) {} diff -Nru ecflow-4.9.0/ANode/test/MyDefsFixture.hpp ecflow-4.11.1/ANode/test/MyDefsFixture.hpp --- ecflow-4.9.0/ANode/test/MyDefsFixture.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/MyDefsFixture.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -33,7 +33,7 @@ // ======================================================================= struct MyDefsFixture { - MyDefsFixture(const std::string& port = ecf::Str::DEFAULT_PORT_NUMBER()) : defsfile_(port) + explicit MyDefsFixture(const std::string& port = ecf::Str::DEFAULT_PORT_NUMBER()) : defsfile_(port) { suite_ptr suite = create_suite(); diff -Nru ecflow-4.9.0/ANode/test/TestAdd.cpp ecflow-4.11.1/ANode/test/TestAdd.cpp --- ecflow-4.9.0/ANode/test/TestAdd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/TestAdd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -48,6 +48,20 @@ BOOST_CHECK_THROW( defs2->addSuite(s1),std::runtime_error); } +BOOST_AUTO_TEST_CASE( test_add_error ) +{ + cout << "ANode:: ...test_add_error\n"; + + defs_ptr defs = Defs::create(); + suite_ptr s1 = defs->add_suite("s1"); + s1->add_task("t1"); + s1->add_family("f1"); + BOOST_CHECK_THROW( defs->add_suite("s1"),std::runtime_error); // duplicate suite + BOOST_CHECK_THROW( s1->add_task("t1"),std::runtime_error); // duplicate task + BOOST_CHECK_THROW( s1->add_family("t1"),std::runtime_error); // duplicate name + BOOST_CHECK_THROW( s1->add_task("f1"),std::runtime_error); // duplicate name +} + BOOST_AUTO_TEST_CASE( test_add_delete_time ) { cout << "ANode:: ...test_add_delete_time\n"; // ECFLOW-1260 diff -Nru ecflow-4.9.0/ANode/test/TestAlias.cpp ecflow-4.11.1/ANode/test/TestAlias.cpp --- ecflow-4.9.0/ANode/test/TestAlias.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/TestAlias.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -40,12 +40,19 @@ suite_ptr s = theDefs.add_suite("test_alias_create"); family_ptr f = s->add_family("f"); t = f->add_task("t"); - t->addMeter(Meter("meter",0,100,100)); - t->addMeter(Meter("meter1",0,100,100)); - t->addEvent(Event(1,"event1")); - t->addEvent(Event(2,"event2")); - t->addLabel(Label("label_name","value")); - t->addLabel(Label("label_name1","value")); + // ECFLOW-1278 set values for event,meter,labels, then ensure alias creation clears them + Meter meter1("met",0,100,100); meter1.set_value(10); + Meter meter2("met2",0,100,100); meter2.set_value(10); + Event event1(1,"event1"); event1.set_value(true); + Event event2(1,"event2"); event2.set_value(true); + Label label1("name","value"); label1.set_new_value("new_value"); + Label label2("name2","value"); label2.set_new_value("new_value"); + t->addMeter(meter1); + t->addMeter(meter2); + t->addEvent(event1); + t->addEvent(event2); + t->addLabel(label1); + t->addLabel(label2); s->add_variable(Str::ECF_HOME(),ecf_home); } @@ -64,10 +71,23 @@ // Test that default node state is QUEUED BOOST_CHECK_MESSAGE(alias->state() == NState::QUEUED,"Expected initial state of QUEUED"); - // Test that CHILD specific attributes event, meter, labels are copied over from the task. + // Test that CHILD specific attributes event, meter, labels are copied over from the task AND that they are reset. BOOST_CHECK_MESSAGE(alias->meters().size() == 2,"Expected 2 meter to be copied from task but found " << alias->meters().size()); BOOST_CHECK_MESSAGE(alias->events().size() == 2,"Expected 2 events to be copied from task but found " << alias->events().size()); BOOST_CHECK_MESSAGE(alias->labels().size() == 2,"Expected 2 labels to be copied from task but found " << alias->labels().size()); + BOOST_FOREACH(const Meter& meter, alias->meters()){BOOST_CHECK_MESSAGE(meter.value() == meter.min(),"Expected meter value " << meter.min() << " but found " << meter.value() << " for " << meter.dump());} + BOOST_FOREACH(const Event& event, alias->events()){BOOST_CHECK_MESSAGE(!event.value(),"Expected " << event.dump() << " to be clear");} + BOOST_FOREACH(const Label& label, alias->labels()){BOOST_CHECK_MESSAGE(label.new_value().empty(),"Expected " << label.dump() << " to be clear");} + + + // Ensure Task state was not changed + BOOST_CHECK_MESSAGE(t->meters().size() == 2,"Did not expect task state to change, expected 2 meter but found: " << t->meters().size()); + BOOST_CHECK_MESSAGE(t->events().size() == 2,"Did not expect task state to change, expected 2 events but found:" << t->events().size()); + BOOST_CHECK_MESSAGE(t->labels().size() == 2,"Did not expect task state to change, expected 2 labels but found:" << t->labels().size()); + BOOST_FOREACH(const Meter& meter, t->meters()){BOOST_CHECK_MESSAGE(meter.value() == 10,"Expected meter value 10 but found " << meter.value() << " for " << meter.dump());} + BOOST_FOREACH(const Event& event, t->events()){BOOST_CHECK_MESSAGE(event.value(),"Expected " << event.dump() << " to be set");} + BOOST_FOREACH(const Label& label, t->labels()){BOOST_CHECK_MESSAGE(label.new_value() == "new_value","Expected label with 'new_value' but found " << label.new_value());} + // test that user variables passed in got added as Variables BOOST_CHECK_MESSAGE(alias->variables().size() == 2,"Expected 2 variables to be create from input args but found " << alias->variables().size()); diff -Nru ecflow-4.9.0/ANode/test/TestChangeMgrSingleton.cpp ecflow-4.11.1/ANode/test/TestChangeMgrSingleton.cpp --- ecflow-4.9.0/ANode/test/TestChangeMgrSingleton.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/TestChangeMgrSingleton.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,8 +25,8 @@ class MyObserver : public AbstractObserver { public: - MyObserver(Defs* defs) : update_count_(0),defs_(defs),node_(NULL) { defs->attach(this); } - MyObserver(Node* node) : update_count_(0),defs_(NULL),node_(node) { node->attach(this); } + explicit MyObserver(Defs* defs) : update_count_(0),defs_(defs),node_(NULL) { defs->attach(this); } + explicit MyObserver(Node* node) : update_count_(0),defs_(NULL),node_(node) { node->attach(this); } virtual ~MyObserver() { /* std::cout << "~MyObserver()\n"; */ diff -Nru ecflow-4.9.0/ANode/test/TestReplace.cpp ecflow-4.11.1/ANode/test/TestReplace.cpp --- ecflow-4.9.0/ANode/test/TestReplace.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/TestReplace.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -17,6 +17,13 @@ #include "Family.hpp" #include "Task.hpp" #include "Ecf.hpp" +#include "Str.hpp" + +#include "JobsParam.hpp" +#include "Jobs.hpp" +#include "System.hpp" +#include "File.hpp" +#include "PrintStyle.hpp" using namespace std; using namespace ecf; @@ -691,10 +698,97 @@ BOOST_REQUIRE_MESSAGE( !replaced_node, "Expected replace to fail" ); BOOST_REQUIRE_MESSAGE( !errorMsg.empty() , "Expected error message" ); } +} + +BOOST_AUTO_TEST_CASE( test_trigger_references_during_replace ) +{ + cout << "ANode:: ...test_trigger_references_during_replace\n"; // ECFLOW-1319 + + // This is used to check that the trigger references in AST, are invalidated after replace. + + Defs serverDefs; + suite_ptr server_suite; + { + server_suite = serverDefs.add_suite( "suite" ); + server_suite->addVariable( Variable( Str::ECF_INCLUDE(), "$ECF_HOME/../includes" ) ); + server_suite->addVariable( Variable( "SLEEPTIME", "1" ) ); + server_suite->addVariable( Variable( "ECF_CLIENT_EXE_PATH", "a/made/up/path" ) ); + family_ptr fam = server_suite->add_family( "family" ); + fam->add_task( "t1" )->add_trigger("/suite1/family/suite1_task1 == complete"); + fam->add_task( "t2" )->add_trigger("/suite1/family/suite1_task2 == complete"); + fam->add_task( "t3" )->add_trigger("/suite1/family/suite1_task3 == complete"); + } + task_ptr suite1_task1,suite1_task2,suite1_task3; + { + suite_ptr suite = serverDefs.add_suite( "suite1" ); + family_ptr fam = suite->add_family( "family" ); + suite1_task1 = fam->add_task( "suite1_task1" ); + suite1_task2 = fam->add_task( "suite1_task2" ); + suite1_task3 = fam->add_task( "suite1_task3" ); + } + + // Override ECF_HOME. ECF_HOME is need to locate to the .ecf files + std::string ecf_home = File::test_data("ANode/test/data/SMSHOME","ANode"); + serverDefs.set_server().add_or_update_user_variables(Str::ECF_HOME(),ecf_home); + + /// begin , will cause creation of generated variables. The generated variables + /// are use in client scripts and used to locate the ecf files + serverDefs.beginAll(); + + suite1_task1->set_state(NState::COMPLETE); + suite1_task2->set_state(NState::COMPLETE); + suite1_task3->set_state(NState::COMPLETE); + + // We need JOB generation to *FORCE* the creation of the trigger AST, and hence references + { + JobsParam jobsParam(true/*create jobs*/); // spawn_jobs = false + Jobs jobs(&serverDefs); + jobs.generate(jobsParam); + BOOST_REQUIRE_MESSAGE( jobsParam.submitted().size() == 3 , "expected 3 jobs but found " << jobsParam.submitted().size() << "\n" << jobsParam.errorMsg()); + } + { + // Now replace the suite1/family thereby, invalidating the trigger reference on suite/family/t1,t2,t3 + defs_ptr clientDef = Defs::create(); + suite_ptr suite = clientDef->add_suite( "suite1" ); + family_ptr fam = suite->add_family( "family" ); + fam->add_task( "suite1_task1" ); + fam->add_task( "suite1_task2" ); + fam->add_task( "suite1_task3" ); + fam->add_task( "dummy" ); + + std::string errorMsg; + serverDefs.replaceChild("/suite1/family",clientDef,true/*create nodes as needed*/, false/*force*/, errorMsg); + BOOST_REQUIRE_MESSAGE( errorMsg.empty() , "Expected no message " << errorMsg ); + } + { + std::vector all_server_nodes; + serverDefs.getAllNodes(all_server_nodes); + + // Now check the Trigger reference. The old reference to nodes in the trigger expressions should have been removed + std::vector theTasks; + server_suite->getAllTasks(theTasks); + BOOST_REQUIRE_MESSAGE(theTasks.size() == 3, "Expected 3 tasks but found, " << theTasks.size()); + for(size_t i = 0; i < theTasks.size(); i++) { + + std::set referenced_nodes; + theTasks[i]->getAllAstNodes(referenced_nodes); + BOOST_REQUIRE_MESSAGE(referenced_nodes.size() ==1," expected 1 referenced node" ); + + // The reference nodes must exist in the server. Otherwise replace has still kept references to old nodes in the triggers + bool found_reference = false; + for(size_t n = 0; n < all_server_nodes.size(); n++) { + if (all_server_nodes[n] == (*referenced_nodes.begin())) { + found_reference = true; break; + } + } + BOOST_CHECK_MESSAGE(found_reference,"Could not find trigger reference " << (*referenced_nodes.begin())->absNodePath() << " in the server"); + } + } // reset, to avoid effecting downstream tests Ecf::set_state_change_no(0); Ecf::set_modify_change_no(0); } + BOOST_AUTO_TEST_SUITE_END() diff -Nru ecflow-4.9.0/ANode/test/TestSingleExprParse.cpp ecflow-4.11.1/ANode/test/TestSingleExprParse.cpp --- ecflow-4.9.0/ANode/test/TestSingleExprParse.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/ANode/test/TestSingleExprParse.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,10 +39,13 @@ { std::cout << "ANode:: ...test_single_expression\n"; - // The map key = trigger expression, + // Duplicate AST are held in a static map. Delete them, to avoid ASAN complaining + ExprDuplicate reclaim_cloned_ast_memory; + + // The map key = trigger expression, // value.first = type of expected root abstract syntax tree // value.second = result of expected evaluation - map > exprMap; + map > exprMap; exprMap[":var == 0"] = std::make_pair(AstEqual::stype(),true); exprMap[":var != 1"] = std::make_pair(AstNotEqual::stype(),true); diff -Nru ecflow-4.9.0/Base/src/Connection.hpp ecflow-4.11.1/Base/src/Connection.hpp --- ecflow-4.9.0/Base/src/Connection.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/Connection.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -75,7 +75,7 @@ ssl_socket::lowest_layer_type& socket_ll() { return socket_.lowest_layer();} boost::asio::ssl::stream& socket() { return socket_;} #else - connection(boost::asio::io_service& io_service); + explicit connection(boost::asio::io_service& io_service); boost::asio::ip::tcp::socket& socket_ll() { return socket_; } boost::asio::ip::tcp::socket& socket() { return socket_; } #endif diff -Nru ecflow-4.9.0/Base/src/cts/AlterCmd.cpp ecflow-4.11.1/Base/src/cts/AlterCmd.cpp --- ecflow-4.9.0/Base/src/cts/AlterCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/cts/AlterCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -382,8 +382,8 @@ try { switch (add_attr_type_) { - case AlterCmd::ADD_TIME: node->addTime( TimeSeries::create(name_ ) ); break; - case AlterCmd::ADD_TODAY: node->addToday( TimeSeries::create(name_) ); break; + case AlterCmd::ADD_TIME: node->addTime( TimeAttr(TimeSeries::create(name_ )) ); break; + case AlterCmd::ADD_TODAY: node->addToday( TodayAttr(TimeSeries::create(name_)) ); break; case AlterCmd::ADD_DATE: node->addDate( DateAttr::create(name_) ); break; case AlterCmd::ADD_DAY: node->addDay( DayAttr::create(name_) ); break; case AlterCmd::ADD_ZOMBIE: node->addZombie( ZombieAttr::create(name_) ); break; @@ -478,7 +478,7 @@ " ecfcmd_failed | no_script | killed | migrated | late |\n" " message | complete | queue_limit | task_waiting | locked | zombie ]\n" " For sort:\n" - " [ event | meter | label | variable| limit ]\n" + " [ event | meter | label | variable| limit | all ]\n" "arg3 = name/value\n" " when changing, attributes like variable,meter,event,label,limits,late\n" " we expect arguments to be quoted. For sort this argument can be called 'recursive'\n" @@ -890,14 +890,14 @@ options.push_back(paths[0]); paths.erase(paths.begin()); // remove first path, since it has been added to options } - if (options.size() != 4 ) { - ss << "AlterCmd: change: expected 5 args : change variable "; - ss << " but found only " << (options.size() + paths.size()) << " arguments. The value should be quoted if there are spaces\n"; - ss << dump_args(options,paths) << "\n"; - throw std::runtime_error( ss.str() ); - } + if (options.size() < 3 || options.size() > 4) { + ss << "AlterCmd: change: expected 5 args : change variable "; + ss << " but found only " << (options.size() + paths.size()) << " arguments.\nThe value should be quoted if there are spaces\n"; + ss << dump_args(options,paths) << "\n"; + throw std::runtime_error( ss.str() ); + } name = options[2]; - value = options[3]; + if (options.size() == 4 ) value = options[3]; break;} case AlterCmd::CLOCK_TYPE: { @@ -1173,7 +1173,7 @@ void AlterCmd::create_sort_attributes(Cmd_ptr& cmd,const std::vector& options,const std::vector& paths) const { // options[0] - sort - // options[1] - [ event | meter | label | limit | variable ] + // options[1] - [ event | meter | label | limit | variable | all ] // options[2] - recursive std::stringstream ss; diff -Nru ecflow-4.9.0/Base/src/cts/ClientToServerCmd.hpp ecflow-4.11.1/Base/src/cts/ClientToServerCmd.hpp --- ecflow-4.9.0/Base/src/cts/ClientToServerCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/cts/ClientToServerCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -598,7 +598,7 @@ ,RELOAD_PASSWD_FILE }; - CtsCmd(Api a) : api_(a) {} + explicit CtsCmd(Api a) : api_(a) {} CtsCmd() : api_(NO_CMD) {} Api api() const { return api_;} @@ -680,7 +680,7 @@ client_handle_(client_handle), client_state_change_no_(client_state_change_no), client_modify_change_no_(client_modify_change_no) {} - CSyncCmd(unsigned int client_handle) + explicit CSyncCmd(unsigned int client_handle) : api_(SYNC_FULL), client_handle_(client_handle), client_state_change_no_(0), @@ -733,7 +733,7 @@ public: enum Api { REGISTER, DROP, DROP_USER, ADD, REMOVE, AUTO_ADD , SUITES }; - ClientHandleCmd(Api api = AUTO_ADD) + explicit ClientHandleCmd(Api api = AUTO_ADD) : api_(api), client_handle_(0), auto_add_new_suites_(false) {} @@ -744,12 +744,12 @@ auto_add_new_suites_(add_add_new_suites), suites_(suites) {} - ClientHandleCmd(int client_handle) + explicit ClientHandleCmd(int client_handle) : api_(DROP), client_handle_(client_handle), auto_add_new_suites_(false) {} - ClientHandleCmd(const std::string& drop_user) + explicit ClientHandleCmd(const std::string& drop_user) : api_(DROP_USER), client_handle_(0), auto_add_new_suites_(false), @@ -815,7 +815,7 @@ public: enum Api { NO_CMD, JOB_GEN, CHECK_JOB_GEN_ONLY, GET, WHY, GET_STATE, MIGRATE }; CtsNodeCmd(Api a, const std::string& absNodePath) : api_(a),absNodePath_(absNodePath) {} - CtsNodeCmd(Api a) : api_(a) { assert(a != NO_CMD); } + explicit CtsNodeCmd(Api a) : api_(a) { assert(a != NO_CMD); } CtsNodeCmd() : api_(NO_CMD) {} Api api() const { return api_;} @@ -862,7 +862,7 @@ PathsCmd(Api api,const std::vector& paths, bool force = false) : api_(api),force_(force),paths_(paths){} PathsCmd(Api api,const std::string& absNodePath, bool force = false); - PathsCmd(Api api) + explicit PathsCmd(Api api) : api_(api), force_(false) { assert(api != NO_CMD); } PathsCmd() : api_(NO_CMD),force_(false) {} @@ -914,7 +914,7 @@ public: enum LogApi { GET, CLEAR, FLUSH, NEW , PATH, ENABLE_AUTO_FLUSH, DISABLE_AUTO_FLUSH, QUERY_AUTO_FLUSH}; LogCmd(LogApi a, int get_last_n_lines = 0); // for zero we take default from log. Avoid adding dependency on log.hpp - LogCmd(const std::string& path); // NEW + explicit LogCmd(const std::string& path); // NEW LogCmd(); LogApi api() const { return api_;} @@ -953,7 +953,7 @@ /// Simply writes the message to the log file class LogMessageCmd : public UserCmd { public: - LogMessageCmd(const std::string& msg) : msg_(msg) {} + explicit LogMessageCmd(const std::string& msg) : msg_(msg) {} LogMessageCmd() {} const std::string& msg() const { return msg_;} @@ -1025,7 +1025,7 @@ public: ZombieCmd(ecf::User::Action uc, const std::string& path, const std::string& process_id, const std::string& password) : user_action_(uc), path_(path), process_id_(process_id), password_(password) {} - ZombieCmd(ecf::User::Action uc = ecf::User::BLOCK) : user_action_(uc) {} + explicit ZombieCmd(ecf::User::Action uc = ecf::User::BLOCK) : user_action_(uc) {} const std::string& path_to_task() const { return path_;} const std::string& process_or_remote_id() const { return process_id_;} @@ -1196,7 +1196,7 @@ // This class has no need for persistence, i.e client side only class ShowCmd : public UserCmd { public: - ShowCmd(PrintStyle::Type_t s = PrintStyle::DEFS) : style_(s) {} + explicit ShowCmd(PrintStyle::Type_t s = PrintStyle::DEFS) : style_(s) {} // returns the showStyle virtual bool show_cmd() const { return true ;} @@ -1758,6 +1758,7 @@ virtual STC_Cmd_ptr doHandleRequest(AbstractServer*) const; bool check_source() const; + void delete_source() const; // should *ONLY* be called in doHandleRequest() as that is in server private: mutable Suite* sourceSuite_; // only one is set, gets round un-registered class exception @@ -1772,9 +1773,9 @@ template void serialize( Archive & ar, const unsigned int /*version*/ ) { ar & boost::serialization::base_object< UserCmd >( *this ); - ar & sourceSuite_; // only one is serialised - ar & sourceFamily_; // only one is serialised - ar & sourceTask_; // only one is serialised + ar & sourceSuite_; // only one is serialised, these are new allocated an need to deleted if errors + ar & sourceFamily_; // only one is serialised, these are new allocated an need to deleted if errors + ar & sourceTask_; // only one is serialised, these are new allocated an need to deleted if errors ar & src_host_; ar & src_port_; ar & src_path_; diff -Nru ecflow-4.9.0/Base/src/cts/CtsCmdRegistry.hpp ecflow-4.11.1/Base/src/cts/CtsCmdRegistry.hpp --- ecflow-4.9.0/Base/src/cts/CtsCmdRegistry.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/cts/CtsCmdRegistry.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ class CtsCmdRegistry : private boost::noncopyable { public: - CtsCmdRegistry(bool addGroupCmd = true ); + explicit CtsCmdRegistry(bool addGroupCmd = true ); /// These option describe the arguments for each of the commands /// They also can be presented to the user via --help option. diff -Nru ecflow-4.9.0/Base/src/cts/ForceCmd.cpp ecflow-4.11.1/Base/src/cts/ForceCmd.cpp --- ecflow-4.9.0/Base/src/cts/ForceCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/cts/ForceCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -63,7 +63,7 @@ if (!is_node_state && !is_event_state) { std::stringstream ss; ss << "ForceCmd: failed. Invalid node state or event " << stateOrEvent_ << " expected one of " - << "[ unknown | complete | queued | submitted | active | aborted | clear | set]"; + << "[ unknown | complete | queued | submitted | active | aborted | clear | set ]"; throw std::runtime_error( ss.str() ) ; } @@ -76,22 +76,14 @@ if ( is_event_state ) { Extract::pathAndName(paths_[i],the_path, the_event); if ( the_path.empty() || the_event.empty() ) { - std::stringstream ss; - ss << "ForceCmd: When 'set' or 'clear' is specified the path needs to include name of the event i.e --force=/path/to_task:event_name set"; - std::string error_msg = ss.str(); - ecf::log(Log::ERR, error_msg); - error_ss << error_msg << "\n"; + error_ss << "ForceCmd: When 'set' or 'clear' is specified the path needs to include name of the event i.e --force=/path/to_task:event_name set\n"; continue; } } node_ptr node = find_node_for_edit_no_throw(as,the_path); if (!node.get()) { - std::stringstream ss; - ss << "ForceCmd: Could not find node at path " << the_path; - std::string error_msg = ss.str(); - ecf::log(Log::ERR, error_msg); - error_ss << error_msg << "\n"; + error_ss << "ForceCmd: Could not find node at path " << the_path << "\n"; continue; } SuiteChanged0 changed(node); // Cater for suites in handles @@ -116,25 +108,17 @@ // The recursive option is *NOT* applicable to events, hence ignore. According to Axel !!!! if ( stateOrEvent_ == Event::SET() ) { if (!node->set_event(the_event)) { - std::stringstream ss; - ss << "ForceCmd: force set: failed for node(" << node->absNodePath() << ") can not find event(" << the_event << ")"; - std::string error_msg = ss.str(); - ecf::log(Log::ERR, error_msg); - error_ss << error_msg << "\n"; + error_ss << "ForceCmd: force set: failed for node(" << node->absNodePath() << ") can not find event(" << the_event << ")\n"; continue; } } else if ( stateOrEvent_ == Event::CLEAR() ) { if (!node->clear_event(the_event)) { - std::stringstream ss; - ss << "ForceCmd: force clear: failed for node(" << node->absNodePath() << ") can not find event(" << the_event << ")"; - std::string error_msg = ss.str(); - ecf::log(Log::ERR, error_msg); - error_ss << error_msg << "\n"; + error_ss << "ForceCmd: force clear: failed for node(" << node->absNodePath() << ") can not find event(" << the_event << ")\n"; continue; } } - else throw std::runtime_error("ForceCmd: Invalid parameter") ; + else throw std::runtime_error("ForceCmd: Invalid parameter") ; } if ( recursive_ && setRepeatToLastValue_) { diff -Nru ecflow-4.9.0/Base/src/cts/PlugCmd.cpp ecflow-4.11.1/Base/src/cts/PlugCmd.cpp --- ecflow-4.9.0/Base/src/cts/PlugCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/cts/PlugCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -295,16 +295,26 @@ return false; } +void MoveCmd::delete_source() const +{ + // See: ECFLOW-1292 + delete sourceSuite_; + delete sourceFamily_; + delete sourceTask_ ; +} + STC_Cmd_ptr MoveCmd::doHandleRequest(AbstractServer* as) const { Lock lock(user(),as); if (!lock.ok()) { + delete_source(); std::string errorMsg = "Plug(Move) command failed. User "; errorMsg += as->lockedUser(); errorMsg += " already has an exclusive lock"; throw std::runtime_error( errorMsg); } if (!check_source()) { + delete_source(); throw std::runtime_error("Plug(Move) command failed. No source specified"); } @@ -314,6 +324,7 @@ destNode = as->defs()->findAbsNode(dest_); if (!destNode.get()) { + delete_source(); std::string errorMsg = "Plug(Move) command failed. The destination path "; errorMsg += dest_; errorMsg += " does not exist on server"; throw std::runtime_error( errorMsg); @@ -321,6 +332,7 @@ } else { if (!source()->isSuite()) { + delete_source(); throw std::runtime_error("::Destination path can only be empty when moving a whole suite to a new server"); } } @@ -337,6 +349,7 @@ // check its ok to add std::string errorMsg; if (!thedestNode->isAddChildOk(source(),errorMsg) ) { + delete_source(); std::string msg = "Plug(Move) command failed. "; msg += errorMsg; throw std::runtime_error( msg) ; } @@ -344,6 +357,7 @@ // pass ownership if (!thedestNode->addChild( node_ptr( source() ) )) { // This should never fail !!!! else we have lost/ and leaked source node !!!! + delete_source(); throw std::runtime_error("Fatal error plug(move) command failed.") ; } @@ -367,7 +381,7 @@ // Updated defs state as->defs()->set_most_significant_state(); - // Ownership for sourceSuite_ has been passed on. + // Ownership for sourceSuite_ has been passed on or we have deleted it in delete_source() ECFLOW-1292 sourceSuite_ = NULL; sourceFamily_ = NULL; sourceTask_ = NULL; diff -Nru ecflow-4.9.0/Base/src/Gnuplot.hpp ecflow-4.11.1/Base/src/Gnuplot.hpp --- ecflow-4.9.0/Base/src/Gnuplot.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/Gnuplot.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -45,7 +45,7 @@ private: struct SuiteLoad { - SuiteLoad(const std::string& name) : suite_name_(name), request_per_second_(1), total_request_per_second_(1) {} + explicit SuiteLoad(const std::string& name) : suite_name_(name), request_per_second_(1), total_request_per_second_(1) {} std::string suite_name_; size_t request_per_second_; diff -Nru ecflow-4.9.0/Base/src/ServerToClientResponse.hpp ecflow-4.11.1/Base/src/ServerToClientResponse.hpp --- ecflow-4.9.0/Base/src/ServerToClientResponse.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/ServerToClientResponse.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -23,7 +23,7 @@ class ServerToClientResponse : private boost::noncopyable { public: ServerToClientResponse() {} - ServerToClientResponse(const STC_Cmd_ptr& cmd) : stc_cmd_(cmd) {} + explicit ServerToClientResponse(const STC_Cmd_ptr& cmd) : stc_cmd_(cmd) {} ~ServerToClientResponse() {} STC_Cmd_ptr get_cmd() const { return stc_cmd_; } diff -Nru ecflow-4.9.0/Base/src/stc/ErrorCmd.hpp ecflow-4.11.1/Base/src/stc/ErrorCmd.hpp --- ecflow-4.9.0/Base/src/stc/ErrorCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/ErrorCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -18,7 +18,7 @@ class ErrorCmd : public ServerToClientCmd { public: - ErrorCmd(const std::string& errorMsg); + explicit ErrorCmd(const std::string& errorMsg); ErrorCmd() : ServerToClientCmd() {} void init( const std::string& errorMsg); diff -Nru ecflow-4.9.0/Base/src/stc/SClientHandleCmd.hpp ecflow-4.11.1/Base/src/stc/SClientHandleCmd.hpp --- ecflow-4.9.0/Base/src/stc/SClientHandleCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SClientHandleCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -18,7 +18,7 @@ class SClientHandleCmd : public ServerToClientCmd { public: - SClientHandleCmd(int handle) : handle_(handle) {} + explicit SClientHandleCmd(int handle) : handle_(handle) {} SClientHandleCmd() : ServerToClientCmd() , handle_(0) {} void init(int handle) { handle_ = handle; } diff -Nru ecflow-4.9.0/Base/src/stc/SClientHandleSuitesCmd.hpp ecflow-4.11.1/Base/src/stc/SClientHandleSuitesCmd.hpp --- ecflow-4.9.0/Base/src/stc/SClientHandleSuitesCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SClientHandleSuitesCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,7 @@ //================================================================================ class SClientHandleSuitesCmd : public ServerToClientCmd { public: - SClientHandleSuitesCmd(AbstractServer* as ); + explicit SClientHandleSuitesCmd(AbstractServer* as ); SClientHandleSuitesCmd() : ServerToClientCmd() {} void init(AbstractServer* as); diff -Nru ecflow-4.9.0/Base/src/stc/SServerLoadCmd.hpp ecflow-4.11.1/Base/src/stc/SServerLoadCmd.hpp --- ecflow-4.9.0/Base/src/stc/SServerLoadCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SServerLoadCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -23,7 +23,7 @@ ///================================================================================ class SServerLoadCmd : public ServerToClientCmd { public: - SServerLoadCmd(const std::string& log_file_path) : log_file_path_(log_file_path) {} + explicit SServerLoadCmd(const std::string& log_file_path) : log_file_path_(log_file_path) {} SServerLoadCmd() : ServerToClientCmd() {} void init(const std::string& s) { log_file_path_ = s;} diff -Nru ecflow-4.9.0/Base/src/stc/SStatsCmd.hpp ecflow-4.11.1/Base/src/stc/SStatsCmd.hpp --- ecflow-4.9.0/Base/src/stc/SStatsCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SStatsCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ //================================================================================ class SStatsCmd : public ServerToClientCmd { public: - SStatsCmd(AbstractServer* as ); + explicit SStatsCmd(AbstractServer* as ); SStatsCmd() : ServerToClientCmd() {} void init(AbstractServer* as); diff -Nru ecflow-4.9.0/Base/src/stc/SStringCmd.hpp ecflow-4.11.1/Base/src/stc/SStringCmd.hpp --- ecflow-4.9.0/Base/src/stc/SStringCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SStringCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ ///================================================================================ class SStringCmd : public ServerToClientCmd { public: - SStringCmd(const std::string& s) : str_(s) {} + explicit SStringCmd(const std::string& s) : str_(s) {} SStringCmd() : ServerToClientCmd() {} void init(const std::string& s) { str_ = s;} diff -Nru ecflow-4.9.0/Base/src/stc/SStringVecCmd.hpp ecflow-4.11.1/Base/src/stc/SStringVecCmd.hpp --- ecflow-4.9.0/Base/src/stc/SStringVecCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SStringVecCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ ///================================================================================ class SStringVecCmd : public ServerToClientCmd { public: - SStringVecCmd(const std::vector& s) : vec_(s) {} + explicit SStringVecCmd(const std::vector& s) : vec_(s) {} SStringVecCmd() : ServerToClientCmd() {} void init(const std::vector& s) { vec_ = s;} diff -Nru ecflow-4.9.0/Base/src/stc/SSuitesCmd.hpp ecflow-4.11.1/Base/src/stc/SSuitesCmd.hpp --- ecflow-4.9.0/Base/src/stc/SSuitesCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/SSuitesCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -24,7 +24,7 @@ //================================================================================ class SSuitesCmd : public ServerToClientCmd { public: - SSuitesCmd(AbstractServer* as ); + explicit SSuitesCmd(AbstractServer* as ); SSuitesCmd() : ServerToClientCmd() {} void init(AbstractServer* as); diff -Nru ecflow-4.9.0/Base/src/stc/StcCmd.hpp ecflow-4.11.1/Base/src/stc/StcCmd.hpp --- ecflow-4.9.0/Base/src/stc/StcCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/StcCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -28,7 +28,7 @@ BLOCK_CLIENT_ON_HOME_SERVER, BLOCK_CLIENT_ZOMBIE }; - StcCmd(Api a) : api_(a) {} + explicit StcCmd(Api a) : api_(a) {} StcCmd() : api_(OK) {} void init(Api a) { api_ = a;} diff -Nru ecflow-4.9.0/Base/src/stc/ZombieGetCmd.hpp ecflow-4.11.1/Base/src/stc/ZombieGetCmd.hpp --- ecflow-4.9.0/Base/src/stc/ZombieGetCmd.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/src/stc/ZombieGetCmd.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,7 +25,7 @@ //================================================================================ class ZombieGetCmd : public ServerToClientCmd { public: - ZombieGetCmd(AbstractServer*); + explicit ZombieGetCmd(AbstractServer*); ZombieGetCmd() : ServerToClientCmd() {} void init(AbstractServer*); diff -Nru ecflow-4.9.0/Base/test/MockServer.hpp ecflow-4.11.1/Base/test/MockServer.hpp --- ecflow-4.9.0/Base/test/MockServer.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/test/MockServer.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -33,8 +33,8 @@ }; // Only in server side do we increment state/modify numbers, controlled by: Ecf::set_server(true) - MockServer(Defs* defs) : defs_(defs_ptr(defs,MockServer::null_deleter())) { Ecf::set_server(true); } - MockServer(defs_ptr defs) : defs_(defs) { Ecf::set_server(true); } + explicit MockServer(Defs* defs) : defs_(defs_ptr(defs,MockServer::null_deleter())) { Ecf::set_server(true); } + explicit MockServer(defs_ptr defs) : defs_(defs) { Ecf::set_server(true); } ~MockServer() { Ecf::set_server(false); } virtual SState::State state() const { return SState::RUNNING;} @@ -95,7 +95,7 @@ /// o Update Suite state/modify change number class MockSuiteChangedServer : private boost::noncopyable { public: - MockSuiteChangedServer(suite_ptr suite) : suiteChanged_(suite) { Ecf::set_server(true);} + explicit MockSuiteChangedServer(suite_ptr suite) : suiteChanged_(suite) { Ecf::set_server(true);} ~MockSuiteChangedServer() { Ecf::set_server(false); } private: ecf::SuiteChanged suiteChanged_; diff -Nru ecflow-4.9.0/Base/test/TestAlterCmd.cpp ecflow-4.11.1/Base/test/TestAlterCmd.cpp --- ecflow-4.9.0/Base/test/TestAlterCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/test/TestAlterCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -32,7 +32,7 @@ class TestStateChanged { public: - TestStateChanged(suite_ptr s) + explicit TestStateChanged(suite_ptr s) : suite_(s), initial_suite_state_change_no_(s->state_change_no()), initial_suite_modify_change_no_(s->modify_change_no()) { Ecf::set_server(true);} @@ -57,7 +57,7 @@ class TestDefsStateChanged { public: - TestDefsStateChanged(Defs* s) + explicit TestDefsStateChanged(Defs* s) : defs_(s), initial_state_change_no_(s->defs_only_max_state_change_no()) {} @@ -458,9 +458,16 @@ // test change variable s->add_variable("FRED1","BILL"); - TestHelper::invokeRequest(&defs,Cmd_ptr( new AlterCmd(s->absNodePath(),AlterCmd::VARIABLE,"FRED1","BILL1"))); - const Variable& v = s->findVariable("FRED1"); - BOOST_CHECK_MESSAGE( !v.empty() && v.theValue() == "BILL1", "expected to find variable FRED1, with value BILL1"); + { + TestHelper::invokeRequest(&defs,Cmd_ptr( new AlterCmd(s->absNodePath(),AlterCmd::VARIABLE,"FRED1","BILL1"))); + const Variable& v = s->findVariable("FRED1"); + BOOST_CHECK_MESSAGE( !v.empty() && v.theValue() == "BILL1", "expected to find variable FRED1, with value BILL1"); + } + { + TestHelper::invokeRequest(&defs,Cmd_ptr( new AlterCmd(s->absNodePath(),AlterCmd::VARIABLE,"FRED1"))); + const Variable& v = s->findVariable("FRED1"); + BOOST_CHECK_MESSAGE( !v.empty() && v.theValue() == "", "expected to find variable FRED1, with empty value"); + } } { // test add event diff -Nru ecflow-4.9.0/Base/test/TestLogCmd.cpp ecflow-4.11.1/Base/test/TestLogCmd.cpp --- ecflow-4.9.0/Base/test/TestLogCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Base/test/TestLogCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -71,7 +71,8 @@ Defs defs; TestHelper::invokeRequest(&defs,Cmd_ptr( new LogCmd(new_log_file)), false /* check_change_numbers */); BOOST_CHECK_MESSAGE( defs.server().find_variable("ECF_LOG") == expected_new_log_file , "expected to find ECF_LOG with value '" << expected_new_log_file << "' but found '" << defs.server().find_variable("ECF_LOG") << "'"); - BOOST_CHECK_MESSAGE( defs.server().find_user_variable("ECF_LOG") == expected_new_log_file , "expected to find ECF_LOG in the *USER* variables '" << expected_new_log_file << "' but found '" << defs.server().find_user_variable("ECF_LOG") << "'"); + std::string value; + BOOST_CHECK_MESSAGE( defs.server().find_user_variable("ECF_LOG",value) && (value == expected_new_log_file) , "expected to find ECF_LOG in the *USER* variables '" << expected_new_log_file << "' but found '" << value << "'"); BOOST_CHECK_MESSAGE( Log::instance()->path() == expected_new_log_file , "expected to find ECF_LOG with value '" << expected_new_log_file << "' but found '" << defs.server().find_variable("ECF_LOG") << "'"); diff -Nru ecflow-4.9.0/build_scripts/boost_build.sh ecflow-4.11.1/build_scripts/boost_build.sh --- ecflow-4.9.0/build_scripts/boost_build.sh 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/build_scripts/boost_build.sh 2018-10-23 11:41:34.000000000 +0000 @@ -147,14 +147,14 @@ # ======================================================================== # Note: boost thread *ONLY* need to test multi-threaded server See: define ECFLOW_MT # ======================================================================== -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-system variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-date_time variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-filesystem variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-program_options variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-serialization variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-test variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-thread variant=debug -j2 -./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-regex variant=debug -j2 # ecflowUi +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-system variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-date_time variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-filesystem variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-program_options variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-serialization variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-test variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-thread variant=debug -j2 +#./bjam --build-dir=./tmpBuildDir toolset=$tool "$CXXFLAGS" stage link=static --layout=$layout --with-regex variant=debug -j2 # ecflowUi @@ -211,22 +211,31 @@ # # ========================================================================================== # PYTHON3: - # Build: - # 1/ module load python3, this update the $PATH + # To build BOTH python2 and Python 3 libraries, the order is important. + # - First build python3 and then python2. This is because in boost 1.53 not all python libs have the 3 tag. + # + # Python 3: + # 0/ ./b2 --with-python --clean # Clean previous build + # 1/ module unload python; module load python3, this update the $PATH # 2/ ./bootstrap.sh --with-python=/usr/local/apps/python3/3.5.1-01/bin/python3 - # 3/ Need to manually edit $BOOST_ROOT/project-config.jam, make sure file '$BOOST_ROOT/project-config.jam' has: + # 3/ Comment out any other 'using python' then + # manually edit $BOOST_ROOT/project-config.jam, make sure file '$BOOST_ROOT/project-config.jam' has: # # using python # : 3.5 - # : /usr/local/apps/python3/3.5.1-01/bin/python3 # ***** If this is left as python3, includes get messed up, have mix of python2 & 3 - # : /usr/local/apps/python3/3.5.1-01/include/python3.5m # include directory + # : /usr/local/apps/python3/3.6.5-01/bin/python3 # ***** If this is left as python3, includes get messed up, have mix of python2 & 3 + # : /usr/local/apps/python3/3.6.5-01/include/python3.6m # include directory # ; - # ... - # option.set includedir : /usr/local/apps/python3/3.5.1-01/include/python3.5m ; # ***MAKE*** sure this is set # # ***** cmd/prefix must be path to python3, otherwise compilation include files has a mixture of # python 2.7 and 3.5, YUK, took ages to debug # + # Python 2: + # 0/ ./b2 --with-python --clean # Clean previous build + # 1/ module unload python; module load python2 + # 2/ ./bootstrap.sh --with-python=/path/to/python2.7 + # 3/ invoke this script + # # Check: # To check the build make sure we don't have symbol pulled in from python2 libs # cd $BOOST_ROOT/stage/lib @@ -234,8 +243,6 @@ # nm -D /tmp/ma0/workspace/bdir/release/ecflow/Pyext/ecflow.so | grep PyClass_Type # check ecflow.so # =============================================================================== - ./bjam toolset=$tool link=shared variant=debug "$CXXFLAGS" stage --layout=$layout threading=multi --with-python -d2 -j2 ./bjam toolset=$tool link=shared variant=release "$CXXFLAGS" stage --layout=$layout threading=multi --with-python -d2 -j2 - ./bjam toolset=$tool link=static variant=debug "$CXXFLAGS" stage --layout=$layout threading=multi --with-python -d2 -j2 ./bjam toolset=$tool link=static variant=release "$CXXFLAGS" stage --layout=$layout threading=multi --with-python -d2 -j2 fi diff -Nru ecflow-4.9.0/build_scripts/ecflow_asan.supp ecflow-4.11.1/build_scripts/ecflow_asan.supp --- ecflow-4.9.0/build_scripts/ecflow_asan.supp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/build_scripts/ecflow_asan.supp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,3 @@ +#interceptor_via_fun:NameOfCFunctionToSuppress +#interceptor_via_fun:-[ClassName objCMethodToSuppress:] +#interceptor_via_lib:NameOfTheLibraryToSuppress diff -Nru ecflow-4.9.0/build_scripts/ecflow_lsan.supp ecflow-4.11.1/build_scripts/ecflow_lsan.supp --- ecflow-4.9.0/build_scripts/ecflow_lsan.supp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/build_scripts/ecflow_lsan.supp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,11 @@ +# Needed for python ONLY +# https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#suppressions +leak:__interceptor_fopen64 +leak:list_resize +leak:_PyString_Resize +leak:type_new +leak:__interceptor_calloc +leak:__interceptor_malloc +leak:boost::python::objects::function_object +leak:boost::python::api::object +leak:init_module_ecflow diff -Nru ecflow-4.9.0/build_scripts/run-clang-tidy.py ecflow-4.11.1/build_scripts/run-clang-tidy.py --- ecflow-4.9.0/build_scripts/run-clang-tidy.py 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/build_scripts/run-clang-tidy.py 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,310 @@ +#!/usr/bin/env python +# https://github.com/llvm-mirror/clang-tools-extra/blob/master/clang-tidy/tool/run-clang-tidy.py +#===- run-clang-tidy.py - Parallel clang-tidy runner ---------*- python -*--===# +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +#===------------------------------------------------------------------------===# +# FIXME: Integrate with clang-tidy-diff.py + +""" +Parallel clang-tidy runner +========================== +Runs clang-tidy over all files in a compilation database. Requires clang-tidy +and clang-apply-replacements in $PATH. +Example invocations. +- Run clang-tidy on all files in the current working directory with a default + set of checks and show warnings in the cpp files and all project headers. + run-clang-tidy.py $PWD +- Fix all header guards. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard +- Fix all header guards included from clang-tidy and header guards + for clang-tidy headers. + run-clang-tidy.py -fix -checks=-*,llvm-header-guard extra/clang-tidy \ + -header-filter=extra/clang-tidy +Compilation database setup: +http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html +""" + +from __future__ import print_function + +import argparse +import glob +import json +import multiprocessing +import os +import re +import shutil +import subprocess +import sys +import tempfile +import threading +import traceback +import yaml + +is_py2 = sys.version[0] == '2' + +if is_py2: + import Queue as queue +else: + import queue as queue + +def find_compilation_database(path): + """Adjusts the directory until a compilation database is found.""" + result = './' + while not os.path.isfile(os.path.join(result, path)): + if os.path.realpath(result) == '/': + print('Error: could not find compilation database.') + sys.exit(1) + result += '../' + return os.path.realpath(result) + + +def make_absolute(f, directory): + if os.path.isabs(f): + return f + return os.path.normpath(os.path.join(directory, f)) + + +def get_tidy_invocation(f, clang_tidy_binary, checks, tmpdir, build_path, + header_filter, extra_arg, extra_arg_before, quiet, + config): + """Gets a command line for clang-tidy.""" + start = [clang_tidy_binary] + if header_filter is not None: + start.append('-header-filter=' + header_filter) + else: + # Show warnings in all in-project headers by default. + start.append('-header-filter=^' + build_path + '/.*') + if checks: + start.append('-checks=' + checks) + if tmpdir is not None: + start.append('-export-fixes') + # Get a temporary file. We immediately close the handle so clang-tidy can + # overwrite it. + (handle, name) = tempfile.mkstemp(suffix='.yaml', dir=tmpdir) + os.close(handle) + start.append(name) + for arg in extra_arg: + start.append('-extra-arg=%s' % arg) + for arg in extra_arg_before: + start.append('-extra-arg-before=%s' % arg) + start.append('-p=' + build_path) + if quiet: + start.append('-quiet') + if config: + start.append('-config=' + config) + start.append(f) + return start + + +def merge_replacement_files(tmpdir, mergefile): + """Merge all replacement files in a directory into a single file""" + # The fixes suggested by clang-tidy >= 4.0.0 are given under + # the top level key 'Diagnostics' in the output yaml files + mergekey="Diagnostics" + merged=[] + for replacefile in glob.iglob(os.path.join(tmpdir, '*.yaml')): + content = yaml.safe_load(open(replacefile, 'r')) + if not content: + continue # Skip empty files. + merged.extend(content.get(mergekey, [])) + + if merged: + # MainSourceFile: The key is required by the definition inside + # include/clang/Tooling/ReplacementsYaml.h, but the value + # is actually never used inside clang-apply-replacements, + # so we set it to '' here. + output = { 'MainSourceFile': '', mergekey: merged } + with open(mergefile, 'w') as out: + yaml.safe_dump(output, out) + else: + # Empty the file: + open(mergefile, 'w').close() + + +def check_clang_apply_replacements_binary(args): + """Checks if invoking supplied clang-apply-replacements binary works.""" + try: + subprocess.check_call([args.clang_apply_replacements_binary, '--version']) + except: + print('Unable to run clang-apply-replacements. Is clang-apply-replacements ' + 'binary correctly specified?', file=sys.stderr) + traceback.print_exc() + sys.exit(1) + + +def apply_fixes(args, tmpdir): + """Calls clang-apply-fixes on a given directory.""" + invocation = [args.clang_apply_replacements_binary] + if args.format: + invocation.append('-format') + if args.style: + invocation.append('-style=' + args.style) + invocation.append(tmpdir) + subprocess.call(invocation) + + +def run_tidy(args, tmpdir, build_path, queue, failed_files): + """Takes filenames out of queue and runs clang-tidy on them.""" + while True: + name = queue.get() + invocation = get_tidy_invocation(name, args.clang_tidy_binary, args.checks, + tmpdir, build_path, args.header_filter, + args.extra_arg, args.extra_arg_before, + args.quiet, args.config) + sys.stdout.write(' '.join(invocation) + '\n') + return_code = subprocess.call(invocation) + if return_code != 0: + failed_files.append(name) + queue.task_done() + + +def main(): + parser = argparse.ArgumentParser(description='Runs clang-tidy over all files ' + 'in a compilation database. Requires ' + 'clang-tidy and clang-apply-replacements in ' + '$PATH.') + parser.add_argument('-clang-tidy-binary', metavar='PATH', + default='clang-tidy', + help='path to clang-tidy binary') + parser.add_argument('-clang-apply-replacements-binary', metavar='PATH', + default='clang-apply-replacements', + help='path to clang-apply-replacements binary') + parser.add_argument('-checks', default=None, + help='checks filter, when not specified, use clang-tidy ' + 'default') + parser.add_argument('-config', default=None, + help='Specifies a configuration in YAML/JSON format: ' + ' -config="{Checks: \'*\', ' + ' CheckOptions: [{key: x, ' + ' value: y}]}" ' + 'When the value is empty, clang-tidy will ' + 'attempt to find a file named .clang-tidy for ' + 'each source file in its parent directories.') + parser.add_argument('-header-filter', default=None, + help='regular expression matching the names of the ' + 'headers to output diagnostics from. Diagnostics from ' + 'the main file of each translation unit are always ' + 'displayed.') + parser.add_argument('-export-fixes', metavar='filename', dest='export_fixes', + help='Create a yaml file to store suggested fixes in, ' + 'which can be applied with clang-apply-replacements.') + parser.add_argument('-j', type=int, default=0, + help='number of tidy instances to be run in parallel.') + parser.add_argument('files', nargs='*', default=['.*'], + help='files to be processed (regex on path)') + parser.add_argument('-fix', action='store_true', help='apply fix-its') + parser.add_argument('-format', action='store_true', help='Reformat code ' + 'after applying fixes') + parser.add_argument('-style', default='file', help='The style of reformat ' + 'code after applying fixes') + parser.add_argument('-p', dest='build_path', + help='Path used to read a compile command database.') + parser.add_argument('-extra-arg', dest='extra_arg', + action='append', default=[], + help='Additional argument to append to the compiler ' + 'command line.') + parser.add_argument('-extra-arg-before', dest='extra_arg_before', + action='append', default=[], + help='Additional argument to prepend to the compiler ' + 'command line.') + parser.add_argument('-quiet', action='store_true', + help='Run clang-tidy in quiet mode') + args = parser.parse_args() + + db_path = 'compile_commands.json' + + if args.build_path is not None: + build_path = args.build_path + else: + # Find our database + build_path = find_compilation_database(db_path) + + try: + invocation = [args.clang_tidy_binary, '-list-checks'] + invocation.append('-p=' + build_path) + if args.checks: + invocation.append('-checks=' + args.checks) + invocation.append('-') + subprocess.check_call(invocation) + except: + print("Unable to run clang-tidy.", file=sys.stderr) + sys.exit(1) + + # Load the database and extract all files. + database = json.load(open(os.path.join(build_path, db_path))) + files = [make_absolute(entry['file'], entry['directory']) + for entry in database] + + max_task = args.j + if max_task == 0: + max_task = multiprocessing.cpu_count() + + tmpdir = None + if args.fix or args.export_fixes: + check_clang_apply_replacements_binary(args) + tmpdir = tempfile.mkdtemp() + + # Build up a big regexy filter from all command line arguments. + file_name_re = re.compile('|'.join(args.files)) + + return_code = 0 + try: + # Spin up a bunch of tidy-launching threads. + task_queue = queue.Queue(max_task) + # List of files with a non-zero return code. + failed_files = [] + for _ in range(max_task): + t = threading.Thread(target=run_tidy, + args=(args, tmpdir, build_path, task_queue, failed_files)) + t.daemon = True + t.start() + + # Fill the queue with files. + for name in files: + if file_name_re.search(name): + task_queue.put(name) + + # Wait for all threads to be done. + task_queue.join() + if len(failed_files): + return_code = 1 + + except KeyboardInterrupt: + # This is a sad hack. Unfortunately subprocess goes + # bonkers with ctrl-c and we start forking merrily. + print('\nCtrl-C detected, goodbye.') + if tmpdir: + shutil.rmtree(tmpdir) + os.kill(0, 9) + + if args.export_fixes: + print('Writing fixes to ' + args.export_fixes + ' ...') + try: + merge_replacement_files(tmpdir, args.export_fixes) + except: + print('Error exporting fixes.\n', file=sys.stderr) + traceback.print_exc() + return_code=1 + + if args.fix: + print('Applying fixes ...') + try: + apply_fixes(args, tmpdir) + except: + print('Error applying fixes.\n', file=sys.stderr) + traceback.print_exc() + return_code=1 + + if tmpdir: + shutil.rmtree(tmpdir) + sys.exit(return_code) + +if __name__ == '__main__': + main() + + diff -Nru ecflow-4.9.0/build_scripts/site_config/site-config-Linux64.jam ecflow-4.11.1/build_scripts/site_config/site-config-Linux64.jam --- ecflow-4.9.0/build_scripts/site_config/site-config-Linux64.jam 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/build_scripts/site_config/site-config-Linux64.jam 2018-10-23 11:41:34.000000000 +0000 @@ -24,16 +24,28 @@ # Boost libraries referenced in client/server programs # assumes --layout=tagged for the debug release and profile variant # -lib boost_serialization : : debug $(BOOST_ROOT)/stage/lib/libboost_serialization-mt-d.a ; -lib boost_system : : debug $(BOOST_ROOT)/stage/lib/libboost_system-mt-d.a ; -lib boost_thread : : debug $(BOOST_ROOT)/stage/lib/libboost_thread-mt-d.a ; -lib boost_test : : debug $(BOOST_ROOT)/stage/lib/libboost_unit_test_framework-mt-d.a ; -lib boost_test_monitor : : debug $(BOOST_ROOT)/stage/lib/libboost_test_exec_monitor-mt-d.a ; -lib boost_program_options : : debug $(BOOST_ROOT)/stage/lib/libboost_program_options-mt-d.a ; -lib boost_filesystem : : debug $(BOOST_ROOT)/stage/lib/libboost_filesystem-mt-d.a ; -lib boost_datetime : : debug $(BOOST_ROOT)/stage/lib/libboost_date_time-mt-d.a ; -lib boost_python : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt-d.so ; -lib boost_python_static : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt-d.a ; +#lib boost_serialization : : debug $(BOOST_ROOT)/stage/lib/libboost_serialization-mt-d.a ; +#lib boost_system : : debug $(BOOST_ROOT)/stage/lib/libboost_system-mt-d.a ; +#lib boost_thread : : debug $(BOOST_ROOT)/stage/lib/libboost_thread-mt-d.a ; +#lib boost_test : : debug $(BOOST_ROOT)/stage/lib/libboost_unit_test_framework-mt-d.a ; +#lib boost_test_monitor : : debug $(BOOST_ROOT)/stage/lib/libboost_test_exec_monitor-mt-d.a ; +#lib boost_program_options : : debug $(BOOST_ROOT)/stage/lib/libboost_program_options-mt-d.a ; +#lib boost_filesystem : : debug $(BOOST_ROOT)/stage/lib/libboost_filesystem-mt-d.a ; +#lib boost_datetime : : debug $(BOOST_ROOT)/stage/lib/libboost_date_time-mt-d.a ; +#lib boost_python : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt-d.so ; +#lib boost_python_static : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt-d.a ; + +lib boost_serialization : : debug $(BOOST_ROOT)/stage/lib/libboost_serialization-mt.a ; +lib boost_system : : debug $(BOOST_ROOT)/stage/lib/libboost_system-mt.a ; +lib boost_thread : : debug $(BOOST_ROOT)/stage/lib/libboost_thread-mt.a ; +lib boost_test : : debug $(BOOST_ROOT)/stage/lib/libboost_unit_test_framework-mt.a ; +lib boost_test_monitor : : debug $(BOOST_ROOT)/stage/lib/libboost_test_exec_monitor-mt.a ; +lib boost_program_options : : debug $(BOOST_ROOT)/stage/lib/libboost_program_options-mt.a ; +lib boost_filesystem : : debug $(BOOST_ROOT)/stage/lib/libboost_filesystem-mt.a ; +lib boost_datetime : : debug $(BOOST_ROOT)/stage/lib/libboost_date_time-mt.a ; +lib boost_python : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt.so ; +lib boost_python_static : : debug $(BOOST_ROOT)/stage/lib/libboost_python-mt.a ; + # profile uses release libs lib boost_serialization : : profile $(BOOST_ROOT)/stage/lib/libboost_serialization-mt.a ; diff -Nru ecflow-4.9.0/Client/src/ClientEnvironment.cpp ecflow-4.11.1/Client/src/ClientEnvironment.cpp --- ecflow-4.9.0/Client/src/ClientEnvironment.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/src/ClientEnvironment.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -281,7 +281,7 @@ host_vec_.clear(); // remove previous setting if any host_vec_.push_back(std::make_pair(host,port)); } - if (getenv(Str::ECF_NODE().c_str())) + if (!getenv(Str::ECF_HOST().c_str()) && getenv(Str::ECF_NODE().c_str())) cerr << "Warning: ECF_NODE has been replaced with ECF_HOST. In future ecflow releases, ECF_NODE will be deprecated\n"; if (getenv("ECF_ALLOW_NEW_CLIENT_OLD_SERVER")) { diff -Nru ecflow-4.9.0/Client/src/ClientInvoker.hpp ecflow-4.11.1/Client/src/ClientInvoker.hpp --- ecflow-4.9.0/Client/src/ClientInvoker.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/src/ClientInvoker.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,7 +39,7 @@ /// Will create the *ClientEnvironment* once on construction /// By default will throw exception std::runtime_error for errors ClientInvoker(); - ClientInvoker(const std::string& host_port); + explicit ClientInvoker(const std::string& host_port); ClientInvoker(const std::string& host, const std::string& port); ClientInvoker(const std::string& host, int port); @@ -359,7 +359,7 @@ // Allow logging and debug output of request round trip times class RequestLogger : private boost::noncopyable { public: - RequestLogger(const ClientInvoker* ci); + explicit RequestLogger(const ClientInvoker* ci); ~RequestLogger(); void set_cts_cmd(Cmd_ptr cmd) { cmd_ = cmd;} private: @@ -369,7 +369,7 @@ class RoundTripRecorder : private boost::noncopyable { public: - RoundTripRecorder(const ClientInvoker* ci); + explicit RoundTripRecorder(const ClientInvoker* ci); ~RoundTripRecorder(); private: const ClientInvoker* ci_; diff -Nru ecflow-4.9.0/Client/src/Rtt.hpp ecflow-4.11.1/Client/src/Rtt.hpp --- ecflow-4.9.0/Client/src/Rtt.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/src/Rtt.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,7 +39,7 @@ private: ~Rtt(); - Rtt(const std::string& filename); + explicit Rtt(const std::string& filename); static Rtt* instance_; mutable std::ofstream file_; }; diff -Nru ecflow-4.9.0/Client/test/TestCheckPtDefsCmd.cpp ecflow-4.11.1/Client/test/TestCheckPtDefsCmd.cpp --- ecflow-4.9.0/Client/test/TestCheckPtDefsCmd.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/test/TestCheckPtDefsCmd.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -206,6 +206,7 @@ BOOST_CHECK_MESSAGE( *theClient.defs() == *defs_to_be_check_pointed, "expected defs to be the same.\nServer defs:\n" << *theClient.defs() << "\nExpected defs:\n" << *defs_to_be_check_pointed); + } BOOST_AUTO_TEST_CASE( test_check_pt_edit_history ) diff -Nru ecflow-4.9.0/Client/test/TestClientInterface.cpp ecflow-4.11.1/Client/test/TestClientInterface.cpp --- ecflow-4.9.0/Client/test/TestClientInterface.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/test/TestClientInterface.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -362,6 +362,7 @@ BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","variable","name","newValue") == 0,"--alter should return 0\n" << theClient.errorMsg()); BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","variable","name","/new/value/with/path") == 0,"--alter should return 0\n" << theClient.errorMsg()); + BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","variable","name") == 0,"--alter should return 0\n" << theClient.errorMsg()); BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","clock_type","hybrid") == 0,"--alter should return 0\n" << theClient.errorMsg()); BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","clock_type","real") == 0,"--alter should return 0\n" << theClient.errorMsg()); BOOST_REQUIRE_MESSAGE( theClient.alter("/s1","change","clock_date","12.6.2013") == 0,"--alter should return 0\n" << theClient.errorMsg()); diff -Nru ecflow-4.9.0/Client/test/TestSignalSIGTERM.cpp ecflow-4.11.1/Client/test/TestSignalSIGTERM.cpp --- ecflow-4.9.0/Client/test/TestSignalSIGTERM.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Client/test/TestSignalSIGTERM.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -59,7 +59,7 @@ // Send a SIGTERM to the server and ensure that a check point file is created std::string sigterm = "kill -15 " + ecf_pid ; system(sigterm.c_str()); - sleep(2); // allow time for system call + sleep(3); // allow time for system call // We expect a check point file to be save to disk, but *no* backup BOOST_REQUIRE_MESSAGE(fs::exists(invokeServer.ecf_checkpt_file()),CtsApi::checkPtDefs() << " failed file(" << invokeServer.ecf_checkpt_file() << ") not saved"); @@ -71,7 +71,7 @@ // Send a SIGTERM again. This time we expect the backup check point file to be created. system(sigterm.c_str()); - sleep(2); // allow time for system call + sleep(3); // allow time for system call BOOST_REQUIRE_MESSAGE(fs::exists(invokeServer.ecf_checkpt_file()),CtsApi::checkPtDefs() << " failed No check pt file(" << invokeServer.ecf_checkpt_file() << ") saved"); BOOST_REQUIRE_MESSAGE(fs::file_size(invokeServer.ecf_checkpt_file()) !=0,"Expected check point file(" << invokeServer.ecf_checkpt_file() << ") to have file size > 0 "); diff -Nru ecflow-4.9.0/cmake/ecbuild_add_large_file_support.cmake ecflow-4.11.1/cmake/ecbuild_add_large_file_support.cmake --- ecflow-4.9.0/cmake/ecbuild_add_large_file_support.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_add_large_file_support.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,40 @@ +# (C) Copyright 2011- ECMWF. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation nor +# does it submit to any jurisdiction. + +############################################################################################ + +# ensure we use 64bit access to files even on 32bit OS's -- aka Large File Support +# by making off_t 64 bit and stat behave as stat64 + +macro(ecbuild_add_large_file_support) + + check_type_size( off_t EC_SIZEOF_OFF_T ) + + if( EC_SIZEOF_OFF_T LESS "8" ) + + if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) + add_definitions( -D_FILE_OFFSET_BITS=64 ) + elseif( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" ) + add_definitions( -D_LARGE_FILES=64 ) + else() + ecbuild_warn("ENABLE_LARGE_FILE_SUPPORT active, sizeof off_t is ${EC_SIZEOF_OFF_T} < 8 " + "but ecbuild does not know how to enable large files in this operating system") + endif() + + get_directory_property( __compile_defs COMPILE_DEFINITIONS ) + + if( __compile_defs ) + foreach( def ${__compile_defs} ) + list( APPEND CMAKE_REQUIRED_DEFINITIONS -D${def} ) + endforeach() + endif() + + endif() + +endmacro(ecbuild_add_large_file_support) + diff -Nru ecflow-4.9.0/cmake/ecbuild_add_library.cmake ecflow-4.11.1/cmake/ecbuild_add_library.cmake --- ecflow-4.9.0/cmake/ecbuild_add_library.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_add_library.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -419,7 +419,7 @@ endif() endif() - if( ECBUILD_IMPLICIT_LINK_LIBRARIES ) + if( NOT _PAR_TYPE MATCHES "OBJECT" AND ECBUILD_IMPLICIT_LINK_LIBRARIES ) target_link_libraries( ${_PAR_TARGET} ${ECBUILD_IMPLICIT_LINK_LIBRARIES} ) endif() diff -Nru ecflow-4.9.0/cmake/ecbuild_add_test.cmake ecflow-4.11.1/cmake/ecbuild_add_test.cmake --- ecflow-4.9.0/cmake/ecbuild_add_test.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_add_test.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -177,8 +177,14 @@ set( _PAR_ENABLED 0 ) elseif( _PAR_MPI ) # Check for MPIEXEC if it not set - find_program( MPIEXEC NAMES mpiexec mpirun lamexec srun - DOC "Executable for running MPI programs." ) + if( MPIEXEC_EXECUTABLE ) + set( MPIEXEC ${MPIEXEC_EXECUTABLE} ) + endif() + if( NOT MPIEXEC ) + find_program( MPIEXEC NAMES mpiexec mpirun lamexec srun + DOC "Executable for running MPI programs." ) + endif() + if( MPIEXEC ) set(MPIEXEC_NUMPROC_FLAG "-np" CACHE STRING "Flag used by MPI to specify the number of processes for MPIEXEC") ecbuild_debug("ecbuild_add_test(${_PAR_TARGET}): Running using ${MPIEXEC} on ${_PAR_MPI} MPI rank(s)") diff -Nru ecflow-4.9.0/cmake/ecbuild_check_compiler.cmake ecflow-4.11.1/cmake/ecbuild_check_compiler.cmake --- ecflow-4.9.0/cmake/ecbuild_check_compiler.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_check_compiler.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -9,7 +9,7 @@ ################################################################################################### # enable C to use in system introspection -if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS ) +if( NOT CMAKE_C_COMPILER_LOADED ) enable_language( C ) ecbuild_compiler_flags( C ) endif() @@ -60,47 +60,6 @@ endif() -############################################################################################ -# c compiler tests - -if( CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS ) - - ecbuild_cache_check_c_source_compiles( - " typedef int foo_t; - static inline foo_t static_foo(){return 0;} - foo_t foo(){return 0;} - int main(int argc, char *argv[]){return 0;} - " EC_HAVE_C_INLINE ) - -endif() - -############################################################################################ -# c++ compiler tests - -if( CMAKE_CXX_COMPILER_LOADED AND ENABLE_OS_TESTS ) - - # check for __FUNCTION__ - ecbuild_cache_check_cxx_source_compiles( "#include \nint main(int argc, char* argv[]) { std::cout << __FUNCTION__ << std::endl; }" - EC_HAVE_FUNCTION_DEF ) - - # check for c++ abi, usually present in GNU compilers - ecbuild_cache_check_cxx_source_compiles( "#include \n int main() { char * type; int status; char * r = abi::__cxa_demangle(type, 0, 0, &status); }" - EC_HAVE_CXXABI_H ) - - # check for bool - ecbuild_cache_check_cxx_source_compiles( "int main() { bool aflag = true; }" - EC_HAVE_CXX_BOOL ) - - # check for sstream - ecbuild_cache_check_cxx_source_compiles( "#include \nint main() { std::stringstream s; }" - EC_HAVE_CXX_SSTREAM ) - - # test c++ __int128 - ecbuild_cache_check_cxx_source_compiles( "int main(){ __int128 i = 0; return 0;}\n" - EC_HAVE_CXX_INT_128 ) - -endif() - ############################################################################################ # enable warnings diff -Nru ecflow-4.9.0/cmake/ecbuild_check_functions.cmake ecflow-4.11.1/cmake/ecbuild_check_functions.cmake --- ecflow-4.9.0/cmake/ecbuild_check_functions.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_check_functions.cmake 1970-01-01 00:00:00.000000000 +0000 @@ -1,188 +0,0 @@ -# (C) Copyright 2011- ECMWF. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation nor -# does it submit to any jurisdiction. - -############################################################################################ -# os capability checks - -if( ENABLE_OS_FUNCTIONS_TEST ) - - ### symbol checks ################## - - ecbuild_cache_check_symbol_exists( fseek "stdio.h" EC_HAVE_FSEEK ) - ecbuild_cache_check_symbol_exists( fseeko "stdio.h" EC_HAVE_FSEEKO ) - ecbuild_cache_check_symbol_exists( ftello "stdio.h" EC_HAVE_FTELLO ) - ecbuild_cache_check_symbol_exists( lseek "sys/types.h;unistd.h" EC_HAVE_LSEEK ) - ecbuild_cache_check_symbol_exists( ftruncate "sys/types.h;unistd.h" EC_HAVE_FTRUNCATE ) - ecbuild_cache_check_symbol_exists( open "sys/types.h;sys/stat.h;fcntl.h" EC_HAVE_OPEN ) - ecbuild_cache_check_symbol_exists( fopen "stdio.h" EC_HAVE_FOPEN ) - ecbuild_cache_check_symbol_exists( fmemopen "stdio.h" EC_HAVE_FMEMOPEN ) - ecbuild_cache_check_symbol_exists( funopen "stdio.h" EC_HAVE_FUNOPEN ) - ecbuild_cache_check_symbol_exists( flock "sys/file.h" EC_HAVE_FLOCK ) - ecbuild_cache_check_symbol_exists( mmap "sys/mman.h" EC_HAVE_MMAP ) - - ecbuild_cache_check_symbol_exists( posix_memalign "stdlib.h" EC_HAVE_POSIX_MEMALIGN ) - - ecbuild_cache_check_symbol_exists( F_GETLK "fcntl.h" EC_HAVE_F_GETLK ) - ecbuild_cache_check_symbol_exists( F_SETLK "fcntl.h" EC_HAVE_F_SETLK ) - ecbuild_cache_check_symbol_exists( F_SETLKW "fcntl.h" EC_HAVE_F_SETLKW ) - - ecbuild_cache_check_symbol_exists( F_GETLK64 "fcntl.h" EC_HAVE_F_GETLK64 ) - ecbuild_cache_check_symbol_exists( F_SETLK64 "fcntl.h" EC_HAVE_F_SETLK64 ) - ecbuild_cache_check_symbol_exists( F_SETLKW64 "fcntl.h" EC_HAVE_F_SETLKW64 ) - - ecbuild_cache_check_symbol_exists( MAP_ANONYMOUS "sys/mman.h" EC_HAVE_MAP_ANONYMOUS ) - ecbuild_cache_check_symbol_exists( MAP_ANON "sys/mman.h" EC_HAVE_MAP_ANON ) - - ### include files checks ################## - - ecbuild_cache_check_include_files( assert.h EC_HAVE_ASSERT_H ) - ecbuild_cache_check_include_files( stdlib.h EC_HAVE_STDLIB_H ) - ecbuild_cache_check_include_files( unistd.h EC_HAVE_UNISTD_H ) - ecbuild_cache_check_include_files( string.h EC_HAVE_STRING_H ) - ecbuild_cache_check_include_files( strings.h EC_HAVE_STRINGS_H ) - ecbuild_cache_check_include_files( sys/stat.h EC_HAVE_SYS_STAT_H ) - ecbuild_cache_check_include_files( sys/time.h EC_HAVE_SYS_TIME_H ) - ecbuild_cache_check_include_files( sys/types.h EC_HAVE_SYS_TYPES_H ) - ecbuild_cache_check_include_files( malloc.h EC_HAVE_MALLOC_H ) - ecbuild_cache_check_include_files( sys/malloc.h EC_HAVE_SYS_MALLOC_H ) - - ecbuild_cache_check_include_files( sys/param.h EC_HAVE_SYS_PARAM_H ) - ecbuild_cache_check_include_files( sys/mount.h EC_HAVE_SYS_MOUNT_H ) - ecbuild_cache_check_include_files( sys/vfs.h EC_HAVE_SYS_VFS_H ) - - ### capability checks ################## - - # test off_t - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ off_t l=0; return 0;}\n" EC_HAVE_OFFT ) - # test off64_t - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \n#include \nint main(){ off64_t l=0; return 0;}\n" EC_HAVE_OFF64T ) - # test struct stat - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ struct stat s; return 0; }" EC_HAVE_STRUCT_STAT ) - # test struct stat64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){ struct stat64 s; return 0; }" EC_HAVE_STRUCT_STAT64 ) - # test stat - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ struct stat s; stat(\"\",&s); return 0; }" EC_HAVE_STAT ) - # test stat64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){ struct stat64 s; stat64(\"\",&s); return 0; }" EC_HAVE_STAT64 ) - # test fstat - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ struct stat s; fstat(1,&s); return 0; }" EC_HAVE_FSTAT ) - # test fstat64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){ struct stat64 s; fstat64(1,&s); return 0; }" EC_HAVE_FSTAT64 ) - # test fseeko64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \n#include \nint main(){FILE* file;off64_t l=0;fseeko64(file,l,SEEK_CUR);return 0;}\n" EC_HAVE_FSEEKO64 ) - - # test for ftello64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \n#include \nint main(){FILE* file;off64_t l = ftello64(file);return 0;}\n" EC_HAVE_FTELLO64 ) - - # test for lseek64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \n#include \nint main(){off64_t h = lseek64(0,0,SEEK_SET);return 0;}\n" EC_HAVE_LSEEK64 ) - # test for open64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){int fd = open64(\"name\",O_RDWR|O_CREAT,0777);return 0;}\n" EC_HAVE_OPEN64 ) - # test for fopen64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){FILE* file = fopen64(\"name\",\"w\");return 0;}\n" EC_HAVE_FOPEN64 ) - # test for ftruncate64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \n#include \nint main(){ftruncate64(0,(off64_t)0);return 0;}\n" EC_HAVE_FTRUNCATE64 ) - # test for flock64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){struct flock64 l;return 0;}\n" EC_HAVE_FLOCK64 ) - # test for mmap64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){void* addr = mmap64(0,10,PROT_READ|PROT_WRITE,MAP_PRIVATE,10,0); return 0;}\n" EC_HAVE_MMAP64 ) - # test for struct statvfs - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ struct statvfs v; }" EC_HAVE_STRUCT_STATVFS ) - # test for struct statvfs64 - ecbuild_cache_check_c_source_compiles( "#define _LARGEFILE64_SOURCE\n#include \nint main(){ struct statvfs64 v; }" EC_HAVE_STRUCT_STATVFS64 ) - - # test for fopencookie - ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include \nint main(){ void* cookie; const char* mode; cookie_io_functions_t iof; FILE* fopencookie(void *cookie, const char *mode, cookie_io_functions_t iof); }" EC_HAVE_FOPENCOOKIE ) - - # test for fsync - ecbuild_cache_check_symbol_exists(fsync "unistd.h" EC_HAVE_FSYNC) - # test for fdatasync - ecbuild_cache_check_symbol_exists(fdatasync "unistd.h" EC_HAVE_FDATASYNC) - # test for dirfd - ecbuild_cache_check_c_source_compiles( "#include \n#include \nint main(){ DIR *dirp; int i = dirfd(dirp); }\n" EC_HAVE_DIRFD ) - # test for sys/proc.h - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ return 0; }\n" EC_HAVE_SYSPROC ) - # test for procfs - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ return 0; }\n" EC_HAVE_SYSPROCFS ) - # test for backtrace - ecbuild_cache_check_c_source_compiles( "#include \n#include \n int main(){ void ** buffer; int i = backtrace(buffer, 256); }\n" EC_HAVE_EXECINFO_BACKTRACE ) - - #### reentrant funtions support ############# - - # test for gmtime_r - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ time_t now; time(&now); struct tm t; gmtime_r(&now,&t); }\n" EC_HAVE_GMTIME_R ) - # test for getpwuid_r - ecbuild_cache_check_c_source_compiles( "#include \n#include \n#include \nint main(){ char buf[4096]; struct passwd pwbuf; struct passwd *pwbufp = 0; getpwuid_r(getuid(), &pwbuf, buf, sizeof(buf), &pwbufp); }\n" EC_HAVE_GETPWUID_R ) - # test for getpwnam_r - ecbuild_cache_check_c_source_compiles( "#include \n#include \nint main(){ struct passwd p; char line[1024]; int n = getpwnam_r(\"user\",&p,line,sizeof(line),0); }\n" EC_HAVE_GETPWNAM_R ) - # test for readdir_r - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ DIR *dirp; struct dirent *entry; struct dirent **result; int i = readdir_r(dirp, entry, result); }\n" EC_HAVE_READDIR_R ) - # test for d_type in dirent.h - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ DIR *dirp; struct dirent *entry; if(entry->d_type) { dirp = 0; } }\n" EC_HAVE_DIRENT_D_TYPE ) - # test for gethostbyname_r - ecbuild_cache_check_c_source_compiles( "#include \nint main(){ const char *name; struct hostent *ret; char *buf; struct hostent **result; size_t buflen; int *h_errnop; int i = gethostbyname_r(name,ret,buf,buflen,result,h_errnop); }\n" EC_HAVE_GETHOSTBYNAME_R ) - - #### special compiler __atributes__ ############# - - # test for __attribute__ ((__constructor__)) -- usually present in GCC, Clang, Intel on Linux, Solaris, MacOSX; not present in AIX XLC - ecbuild_cache_check_c_source_compiles( "#include \nstatic int argc_;static char** argv_;static char** envp_;\nint main(){printf(\"%d\", argc_);}\n__attribute__ ((__constructor__)) static void before_main(int argc, char* argv[], char* envp[]){argc_ = argc;argv_ = argv;envp_ = envp;}\n" EC_HAVE_ATTRIBUTE_CONSTRUCTOR ) - - if( NOT DEFINED EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV ) - ecbuild_check_c_source_return(" - #include - #include - int main(){return 0;} - __attribute__ ((__constructor__)) - static void before_main(int argc, char* argv[], char* envp[]) - { - printf(\"%d:%d\",argc, strstr(argv[0],\"cmTryCompileExec\")?1:0); - }" - VAR EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV - OUTPUT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT ) - - if( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV AND NOT EC_ATTRIBUTE_CONSTRUCTOR_INITS_OUTPUT STREQUAL "1:1" ) - set(EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV 0 CACHE INTERNAL "ATTRIBUTE_CONSTRUCTOR doesnt init argv correctly") - endif() - endif() - ecbuild_cache_var( EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV ) - - - #### check for some Linux stuff ############# - - if( NOT DEFINED EC_HAVE_PROCFS ) - ecbuild_check_c_source_return(" - #include - #include - int main() - { - DIR* d = opendir(\"/proc\"); - if(d) - return 0; - else - return -1; - }" - VAR EC_HAVE_PROCFS - OUTPUT EC_HAVE_PROCFS_OUTPUT ) - endif() - ecbuild_cache_var( EC_HAVE_PROCFS ) - -# ecbuild_debug_var(EC_HAVE_PROCFS) -# ecbuild_debug_var(EC_HAVE_PROCFS_OUTPUT) - - #### check support for DL library ############# - - ecbuild_cache_check_include_files( dlfcn.h EC_HAVE_DLFCN_H ) - - cmake_push_check_state(RESET) - set(CMAKE_REQUIRED_LIBRARIES ${CMAKE_DL_LIBS} ) - ecbuild_cache_check_c_source_compiles( "#define _GNU_SOURCE\n#include \nint main(){ void* addr; Dl_info info; dladdr(addr, &info); }\n" EC_HAVE_DLADDR ) - cmake_pop_check_state() - -endif() - - diff -Nru ecflow-4.9.0/cmake/ecbuild_check_os.cmake ecflow-4.11.1/cmake/ecbuild_check_os.cmake --- ecflow-4.9.0/cmake/ecbuild_check_os.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_check_os.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -10,9 +10,9 @@ # check size of pointer # Re-check size of void pointer since for some compiler combinations this is not properly set -ecbuild_cache_check_type_size( "void*" CMAKE_SIZEOF_VOID_P ) +check_type_size( "void*" CMAKE_SIZEOF_VOID_P ) -if( NOT CMAKE_C_COMPILER_LOADED AND ENABLE_OS_TESTS ) +if( NOT CMAKE_C_COMPILER_LOADED ) enable_language( C ) ecbuild_compiler_flags( C ) @@ -27,6 +27,14 @@ endif() ############################################################################################ + +### this will be deprecated in ecbuild 3.x + +include(ecbuild_test_endiness) + +ecbuild_test_endiness() + +############################################################################################ # For 64 bit architectures enable PIC (position-independent code) # Allow overriding the position independent code setting (ECBUILD-220) @@ -36,173 +44,15 @@ set( CMAKE_POSITION_INDEPENDENT_CODE ON ) endif() - -############################################################################################ -# check architecture - -if( ENABLE_OS_TYPES_TEST ) - - set( EC_SIZEOF_PTR ${CMAKE_SIZEOF_VOID_P} ) - ecbuild_cache_var( EC_SIZEOF_PTR ) - - ecbuild_cache_check_type_size( char EC_SIZEOF_CHAR ) - ecbuild_cache_check_type_size( short EC_SIZEOF_SHORT ) - ecbuild_cache_check_type_size( int EC_SIZEOF_INT ) - ecbuild_cache_check_type_size( long EC_SIZEOF_LONG ) - ecbuild_cache_check_type_size( "long long" EC_SIZEOF_LONG_LONG ) - ecbuild_cache_check_type_size( float EC_SIZEOF_FLOAT ) - ecbuild_cache_check_type_size( double EC_SIZEOF_DOUBLE ) - ecbuild_cache_check_type_size( "long double" EC_SIZEOF_LONG_DOUBLE ) - ecbuild_cache_check_type_size( size_t EC_SIZEOF_SIZE_T ) - ecbuild_cache_check_type_size( ssize_t EC_SIZEOF_SSIZE_T ) - ecbuild_cache_check_type_size( off_t EC_SIZEOF_OFF_T ) - - ecbuild_debug( "sizeof void* [${EC_SIZEOF_PTR}]" ) - ecbuild_debug( "sizeof char [${EC_SIZEOF_CHAR}]" ) - ecbuild_debug( "sizeof short [${EC_SIZEOF_SHORT}]" ) - ecbuild_debug( "sizeof int [${EC_SIZEOF_INT}]" ) - ecbuild_debug( "sizeof long [${EC_SIZEOF_LONG}]" ) - ecbuild_debug( "sizeof long long [${EC_SIZEOF_LONG_LONG}]" ) - ecbuild_debug( "sizeof float [${EC_SIZEOF_FLOAT}]" ) - ecbuild_debug( "sizeof double [${EC_SIZEOF_DOUBLE}]" ) - ecbuild_debug( "sizeof long double [${EC_SIZEOF_LONG_DOUBLE}]" ) - ecbuild_debug( "sizeof size_t [${EC_SIZEOF_SIZE_T}]" ) - ecbuild_debug( "sizeof ssize_t [${EC_SIZEOF_SSIZE_T}]" ) - ecbuild_debug( "sizeof off_t [${EC_SIZEOF_OFF_T}]" ) - -endif() - ############################################################################################ # check for large file support -# ensure we use 64bit access to files even on 32bit os -- aka Large File Support -# by making off_t 64bit and stat behave as stat64 - -if( ENABLE_LARGE_FILE_SUPPORT ) - - ecbuild_cache_check_type_size( off_t EC_SIZEOF_OFF_T ) - - if( EC_SIZEOF_OFF_T LESS "8" ) - - if( ${CMAKE_SYSTEM_NAME} MATCHES "Linux" OR ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" ) - add_definitions( -D_FILE_OFFSET_BITS=64 ) - endif() - - if( ${CMAKE_SYSTEM_NAME} MATCHES "AIX" ) - add_definitions( -D_LARGE_FILES=64 ) - endif() - - get_directory_property( __compile_defs COMPILE_DEFINITIONS ) - - if( __compile_defs ) - foreach( def ${__compile_defs} ) - list( APPEND CMAKE_REQUIRED_DEFINITIONS -D${def} ) - endforeach() - endif() - - endif() - -endif() - -############################################################################################ -# check endiness - -if( ENABLE_OS_ENDINESS_TEST ) +### this will be deprecated in ecbuild 3.x - if( NOT DEFINED EC_BIG_ENDIAN AND NOT DEFINED EC_LITTLE_ENDIAN ) - - test_big_endian( _BIG_ENDIAN ) - - if( _BIG_ENDIAN ) - set( EC_BIG_ENDIAN 1 ) - set( EC_LITTLE_ENDIAN 0 ) - else() - set( EC_BIG_ENDIAN 0 ) - set( EC_LITTLE_ENDIAN 1 ) - endif() - - endif() - - ecbuild_cache_var( EC_BIG_ENDIAN ) - ecbuild_cache_var( EC_LITTLE_ENDIAN ) - - if( NOT DEFINED IEEE_BE ) - check_c_source_runs( - "int compare(unsigned char* a,unsigned char* b) { - while(*a != 0) if (*(b++)!=*(a++)) return 1; - return 0; - } - int main(int argc,char** argv) { - unsigned char dc[]={0x30,0x61,0xDE,0x80,0x93,0x67,0xCC,0xD9,0}; - double da=1.23456789e-75; - unsigned char* ca; - - unsigned char fc[]={0x05,0x83,0x48,0x22,0}; - float fa=1.23456789e-35; - - if (sizeof(double)!=8) return 1; - - ca=(unsigned char*)&da; - if (compare(dc,ca)) return 1; - - if (sizeof(float)!=4) return 1; - - ca=(unsigned char*)&fa; - if (compare(fc,ca)) return 1; - - return 0; - }" IEEE_BE ) - - if( "${IEEE_BE}" STREQUAL "" ) - set( IEEE_BE 0 CACHE INTERNAL "Test IEEE_BE") - endif() - - endif() - - ecbuild_cache_var( IEEE_BE ) - - if( EC_BIG_ENDIAN AND NOT IEEE_BE ) - ecbuild_critical("Failed to sanity check on endiness: OS should be Big-Endian but compiled code runs differently -- to ignore this pass -DIEEE_BE=0 to CMake/ecBuild") - endif() - - if( NOT DEFINED IEEE_LE ) - check_c_source_runs( - "int compare(unsigned char* a,unsigned char* b) { - while(*a != 0) if (*(b++)!=*(a++)) return 1; - return 0; - } - int main(int argc,char** argv) { - unsigned char dc[]={0xD9,0xCC,0x67,0x93,0x80,0xDE,0x61,0x30,0}; - double da=1.23456789e-75; - unsigned char* ca; - - unsigned char fc[]={0x22,0x48,0x83,0x05,0}; - float fa=1.23456789e-35; - - if (sizeof(double)!=8) return 1; - - ca=(unsigned char*)&da; - if (compare(dc,ca)) return 1; - - if (sizeof(float)!=4) return 1; - - ca=(unsigned char*)&fa; - if (compare(fc,ca)) return 1; - - return 0; - }" IEEE_LE ) - - if( "${IEEE_LE}" STREQUAL "" ) - set( IEEE_LE 0 CACHE INTERNAL "Test IEEE_LE") - endif() - endif() - - ecbuild_cache_var( IEEE_LE ) - - if( EC_LITTLE_ENDIAN AND NOT IEEE_LE ) - ecbuild_critical("Failed to sanity check on endiness: OS should be Little-Endian but compiled code runs differently -- to ignore this pass -DIEEE_LE=0 to CMake/ecBuild") - endif() +include(ecbuild_add_large_file_support) +if( ENABLE_LARGE_FILE_SUPPORT ) + ecbuild_add_large_file_support() endif() ############################################################################################ diff -Nru ecflow-4.9.0/cmake/ecbuild-config.cmake ecflow-4.11.1/cmake/ecbuild-config.cmake --- ecflow-4.9.0/cmake/ecbuild-config.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild-config.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -28,9 +28,9 @@ set( ECBUILD_TPL_DEFINITIONS "" ) set( ECBUILD_TPL_LIBRARIES "" ) -set( ECBUILD_VERSION "2.8.1" ) -set( ECBUILD_GIT_SHA1 "744beba8250a81795240df224eeddf794561aac3" ) -set( ECBUILD_GIT_SHA1_SHORT "744beba" ) +set( ECBUILD_VERSION "2.9.1" ) +set( ECBUILD_GIT_SHA1 "d8c1b6ca7554f0a823c25fdc1f375c3d88c21a5c" ) +set( ECBUILD_GIT_SHA1_SHORT "d8c1b6c" ) ### export include paths as absolute paths diff -Nru ecflow-4.9.0/cmake/ecbuild_config.h.in ecflow-4.11.1/cmake/ecbuild_config.h.in --- ecflow-4.9.0/cmake/ecbuild_config.h.in 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_config.h.in 2018-10-23 11:41:34.000000000 +0000 @@ -20,132 +20,6 @@ #define ECBUILD_MACROS_DIR "@ECBUILD_MACROS_DIR@" #endif -/* cpu arch info */ - -#cmakedefine EC_BIG_ENDIAN @EC_BIG_ENDIAN@ -#cmakedefine EC_LITTLE_ENDIAN @EC_LITTLE_ENDIAN@ - -/* compiler support */ - -#cmakedefine EC_HAVE_FUNCTION_DEF - -/* os capability checks */ - -/* --- symbols --- */ - -#cmakedefine EC_HAVE_FSEEK -#cmakedefine EC_HAVE_FSEEKO -#cmakedefine EC_HAVE_FTELLO -#cmakedefine EC_HAVE_LSEEK -#cmakedefine EC_HAVE_FTRUNCATE -#cmakedefine EC_HAVE_OPEN -#cmakedefine EC_HAVE_FOPEN -#cmakedefine EC_HAVE_FMEMOPEN -#cmakedefine EC_HAVE_FUNOPEN -#cmakedefine EC_HAVE_FLOCK -#cmakedefine EC_HAVE_MMAP -#cmakedefine EC_HAVE_FOPENCOOKIE - -#cmakedefine EC_HAVE_POSIX_MEMALIGN - -#cmakedefine EC_HAVE_F_GETLK -#cmakedefine EC_HAVE_F_SETLKW -#cmakedefine EC_HAVE_F_SETLK - -#cmakedefine EC_HAVE_F_GETLK64 -#cmakedefine EC_HAVE_F_SETLKW64 -#cmakedefine EC_HAVE_F_SETLK64 - -#cmakedefine EC_HAVE_MAP_ANONYMOUS -#cmakedefine EC_HAVE_MAP_ANON - -/* --- include files --- */ - -#cmakedefine EC_HAVE_ASSERT_H -#cmakedefine EC_HAVE_STDLIB_H -#cmakedefine EC_HAVE_UNISTD_H -#cmakedefine EC_HAVE_STRING_H -#cmakedefine EC_HAVE_STRINGS_H -#cmakedefine EC_HAVE_SYS_STAT_H -#cmakedefine EC_HAVE_SYS_TIME_H -#cmakedefine EC_HAVE_SYS_TYPES_H - -#cmakedefine EC_HAVE_MALLOC_H -#cmakedefine EC_HAVE_SYS_MALLOC_H - -#cmakedefine EC_HAVE_SYS_PARAM_H -#cmakedefine EC_HAVE_SYS_MOUNT_H -#cmakedefine EC_HAVE_SYS_VFS_H - -/* --- capabilities --- */ - -#cmakedefine EC_HAVE_OFFT -#cmakedefine EC_HAVE_OFF64T - -#cmakedefine EC_HAVE_STRUCT_STAT -#cmakedefine EC_HAVE_STRUCT_STAT64 -#cmakedefine EC_HAVE_STAT -#cmakedefine EC_HAVE_STAT64 -#cmakedefine EC_HAVE_FSTAT -#cmakedefine EC_HAVE_FSTAT64 - -#cmakedefine EC_HAVE_FSEEKO64 -#cmakedefine EC_HAVE_FTELLO64 -#cmakedefine EC_HAVE_LSEEK64 -#cmakedefine EC_HAVE_OPEN64 -#cmakedefine EC_HAVE_FOPEN64 -#cmakedefine EC_HAVE_FTRUNCATE64 -#cmakedefine EC_HAVE_FLOCK64 -#cmakedefine EC_HAVE_MMAP64 - -#cmakedefine EC_HAVE_STRUCT_STATVFS -#cmakedefine EC_HAVE_STRUCT_STATVFS64 -#cmakedefine EC_HAVE_STATVFS -#cmakedefine EC_HAVE_STATVFS64 - -#cmakedefine EC_HAVE_FSYNC -#cmakedefine EC_HAVE_FDATASYNC -#cmakedefine EC_HAVE_DIRFD -#cmakedefine EC_HAVE_SYSPROC -#cmakedefine EC_HAVE_SYSPROCFS - -#cmakedefine EC_HAVE_EXECINFO_BACKTRACE - -/* --- reentrant funtions support --- */ - -#cmakedefine EC_HAVE_GMTIME_R -#cmakedefine EC_HAVE_GETPWUID_R -#cmakedefine EC_HAVE_GETPWNAM_R -#cmakedefine EC_HAVE_READDIR_R -#cmakedefine EC_HAVE_DIRENT_D_TYPE -#cmakedefine EC_HAVE_GETHOSTBYNAME_R - -/* --- compiler __attribute__ support --- */ - -#cmakedefine EC_HAVE_ATTRIBUTE_CONSTRUCTOR -#cmakedefine EC_ATTRIBUTE_CONSTRUCTOR_INITS_ARGV -#cmakedefine EC_HAVE_PROCFS - - -/* --- dl library support --- */ - -#cmakedefine EC_HAVE_DLFCN_H -#cmakedefine EC_HAVE_DLADDR - -/* --- c compiler support --- */ - -#cmakedefine EC_HAVE_C_INLINE - -/* --- c++ compiler support --- */ - -#cmakedefine EC_HAVE_FUNCTION_DEF - -#cmakedefine EC_HAVE_CXXABI_H -#cmakedefine EC_HAVE_CXX_BOOL -#cmakedefine EC_HAVE_CXX_INT_128 - -#cmakedefine EC_HAVE_CXX_SSTREAM - /* config info */ #define @PNAME@_OS_NAME "@CMAKE_SYSTEM@" diff -Nru ecflow-4.9.0/cmake/ecbuild-config-version.cmake ecflow-4.11.1/cmake/ecbuild-config-version.cmake --- ecflow-4.9.0/cmake/ecbuild-config-version.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild-config-version.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -1,4 +1,4 @@ -set(PACKAGE_VERSION "2.8.1") +set(PACKAGE_VERSION "2.9.1") # check whether the requested PACKAGE_FIND_VERSION is compatible diff -Nru ecflow-4.9.0/cmake/ecbuild_define_options.cmake ecflow-4.11.1/cmake/ecbuild_define_options.cmake --- ecflow-4.9.0/cmake/ecbuild_define_options.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_define_options.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -20,10 +20,6 @@ mark_as_advanced( ENABLE_LARGE_FILE_SUPPORT ) -option( ENABLE_OS_TESTS "Run all OS tests" ON ) - -mark_as_advanced( ENABLE_OS_TESTS ) - option( ENABLE_FORTRAN_C_INTERFACE "Enable Fortran/C Interface" OFF ) mark_as_advanced( ENABLE_FORTRAN_C_INTERFACE ) @@ -41,12 +37,6 @@ include( CMakeDependentOption ) # make options depend on one another -cmake_dependent_option( ENABLE_OS_TYPES_TEST "Run sizeof tests on C types" ON "ENABLE_OS_TESTS" OFF) -cmake_dependent_option( ENABLE_OS_ENDINESS_TEST "Run OS endiness tests" ON "ENABLE_OS_TESTS" OFF) -cmake_dependent_option( ENABLE_OS_FUNCTIONS_TEST "Run OS functions tests" ON "ENABLE_OS_TESTS" OFF) - -mark_as_advanced( ENABLE_OS_TYPES_TEST ENABLE_OS_ENDINESS_TEST ENABLE_OS_FUNCTIONS_TEST ) - option( ECBUILD_USE_INCLUDE_DIRECTORIES "Forces to use global include_directories() instead of target specific. Adverse effect on PkgConfig generation." OFF ) mark_as_advanced( ECBUILD_USE_INCLUDE_DIRECTORIES ) diff -Nru ecflow-4.9.0/cmake/ecbuild_dont_pack.cmake ecflow-4.11.1/cmake/ecbuild_dont_pack.cmake --- ecflow-4.9.0/cmake/ecbuild_dont_pack.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_dont_pack.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -76,6 +76,7 @@ # save cache if we added any files not to pack if( LOCAL_FILES_NOT_TO_PACK ) + list(REMOVE_DUPLICATES ECBUILD_DONT_PACK_FILES) set( ECBUILD_DONT_PACK_FILES ${ECBUILD_DONT_PACK_FILES} CACHE INTERNAL "" ) endif() diff -Nru ecflow-4.9.0/cmake/ecbuild_generate_fortran_interfaces.cmake ecflow-4.11.1/cmake/ecbuild_generate_fortran_interfaces.cmake --- ecflow-4.9.0/cmake/ecbuild_generate_fortran_interfaces.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_generate_fortran_interfaces.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -33,7 +33,7 @@ endif() set( options ) - set( single_value_args TARGET DESTINATION PARALLEL INCLUDE_DIRS GENERATED SOURCE_DIR FCM_CONFIG_FILE ) + set( single_value_args TARGET DESTINATION PARALLEL INCLUDE_DIRS GENERATED SOURCE_DIR SUFFIX FCM_CONFIG_FILE ) set( multi_value_args DIRECTORIES ) cmake_parse_arguments( P "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} ) @@ -57,7 +57,11 @@ ecbuild_debug_var( P_PARALLEL ) if( NOT DEFINED P_SOURCE_DIR ) - ecbuild_error( "ecbuild_generate_fortran_interfaces: SOURCE_DIR argument missing") + set( P_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}" ) + endif() + + if( NOT DEFINED P_SUFFIX ) + set( P_SUFFIX ".intfb.h" ) endif() if( DEFINED P_FCM_CONFIG_FILE ) @@ -75,13 +79,9 @@ endif() if( NOT FCM_CONFIG_FILE ) - set( DEFAULT_FCM_CONFIG_FILE "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg" ) - if( EXISTS ${DEFAULT_FCM_CONFIG_FILE} ) - set( FCM_CONFIG_FILE ${DEFAULT_FCM_CONFIG_FILE} ) - ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration found in ${DEFAULT_FCM_CONFIG_FILE}" ) - else() - ecbuild_debug( "ecbuild_generate_fortran_interfaces: fcm configuration not found in ${DEFAULT_FCM_CONFIG_FILE}" ) - endif() + set( FCM_CONFIG_FILE "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg" ) + set( FCM_CONFIG_FILE "${CMAKE_CURRENT_BINARY_DIR}/fcm-make-interfaces.${P_TARGET}.cfg" ) + configure_file( "${ECBUILD_MACROS_DIR}/fcm-make-interfaces.cfg.in" "${FCM_CONFIG_FILE}" @ONLY ) endif() ecbuild_debug_var( FCM_CONFIG_FILE ) @@ -94,7 +94,8 @@ if( _srcdir MATCHES "/$" ) ecbuild_critical("ecbuild_generate_fortran_interfaces: directory ${_srcdir} must not end with /") endif() - ecbuild_list_add_pattern( LIST fortran_files SOURCE_DIR ${P_SOURCE_DIR} GLOB ${_srcdir}/*.F* ) + ecbuild_list_add_pattern( LIST fortran_files SOURCE_DIR ${P_SOURCE_DIR} + GLOB ${_srcdir}/*.[fF] ${_srcdir}/*.[fF]90 ${_srcdir}/*.[fF]03 ${_srcdir}/*.[fF]08 QUIET ) endforeach() string( REPLACE ";" " " _srcdirs "${P_DIRECTORIES}" ) @@ -109,7 +110,7 @@ foreach( fortran_file ${fortran_files} ) #list( APPEND fullpath_fortran_files ${CMAKE_CURRENT_SOURCE_DIR}/${fortran_file} ) get_filename_component(base ${fortran_file} NAME_WE) - set( interface_file "${CMAKE_CURRENT_BINARY_DIR}/interfaces/include/${base}.intfb.h" ) + set( interface_file "${CMAKE_CURRENT_BINARY_DIR}/interfaces/include/${base}${P_SUFFIX}" ) list( APPEND interface_files ${interface_file} ) set_source_files_properties( ${interface_file} PROPERTIES GENERATED TRUE ) math(EXPR _cnt "${_cnt}+1") diff -Nru ecflow-4.9.0/cmake/ecbuild_get_test_data.cmake ecflow-4.11.1/cmake/ecbuild_get_test_data.cmake --- ecflow-4.9.0/cmake/ecbuild_get_test_data.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_get_test_data.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -405,12 +405,16 @@ TARGET __get_data_${_p_TARGET}_${_name} NAME ${_file} ${_dirname} ${_md5} ${_extract} ${_nocheck} ) - # The option /fast disables dependency checking on a target, see - # https://cmake.org/Wiki/CMake_FAQ#Is_there_a_way_to_skip_checking_of_dependent_libraries_when_compiling.3F - if( WIN32 ) - set( _fast "\fast" ) + if ( ${CMAKE_GENERATOR} MATCHES Ninja ) + set( _fast "" ) else() - set( _fast "/fast" ) + # The option /fast disables dependency checking on a target, see + # https://cmake.org/Wiki/CMake_FAQ#Is_there_a_way_to_skip_checking_of_dependent_libraries_when_compiling.3F + if( WIN32 ) + set( _fast "\fast" ) + else() + set( _fast "/fast" ) + endif() endif() file( APPEND ${_script} "exec_check( \"${CMAKE_COMMAND}\" --build \"${CMAKE_BINARY_DIR}\" --target __get_data_${_p_TARGET}_${_name}${_fast} )\n" ) diff -Nru ecflow-4.9.0/cmake/ecbuild_system.cmake ecflow-4.11.1/cmake/ecbuild_system.cmake --- ecflow-4.9.0/cmake/ecbuild_system.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_system.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -234,7 +234,6 @@ include( ecbuild_compiler_flags ) # compiler flags include( ecbuild_check_compiler ) # check for compiler characteristics include( ecbuild_check_os ) # check for os characteristics - include( ecbuild_check_functions ) # check for available functions include( ecbuild_define_paths ) # defines installation paths include( ecbuild_define_libs_and_execs_target ) # defines the top level execs and libs include( ecbuild_define_links_target ) # defines the links target diff -Nru ecflow-4.9.0/cmake/ecbuild_test_endiness.cmake ecflow-4.11.1/cmake/ecbuild_test_endiness.cmake --- ecflow-4.9.0/cmake/ecbuild_test_endiness.cmake 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_test_endiness.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,28 @@ +# (C) Copyright 2011- ECMWF. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation nor +# does it submit to any jurisdiction. + +############################################################################################ +# check endiness + +function(ecbuild_test_endiness) + + test_big_endian( _BIG_ENDIAN ) + + if( _BIG_ENDIAN ) + set( EC_BIG_ENDIAN 1 ) + set( EC_LITTLE_ENDIAN 0 ) + else() + set( EC_BIG_ENDIAN 0 ) + set( EC_LITTLE_ENDIAN 1 ) + endif() + + set( EC_BIG_ENDIAN ${EC_BIG_ENDIAN} PARENT_SCOPE ) + set( EC_LITTLE_ENDIAN ${EC_LITTLE_ENDIAN} PARENT_SCOPE ) + +endfunction(ecbuild_test_endiness) + diff -Nru ecflow-4.9.0/cmake/ecbuild_try_run.cmake ecflow-4.11.1/cmake/ecbuild_try_run.cmake --- ecflow-4.9.0/cmake/ecbuild_try_run.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/ecbuild_try_run.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -127,6 +127,10 @@ ecbuild_critical("Unknown keywords given to ecbuild_try_run(): \"${_p_UNPARSED_ARGUMENTS}\"") endif() + if( CMAKE_EXE_LINKER_FLAGS ) + set( _p_LINK_LIBRARIES "${_p_LINK_LIBRARIES} ${CMAKE_EXE_LINKER_FLAGS}" ) + endif() + # Build argument list for try_compile foreach( _opt CMAKE_FLAGS COMPILE_DEFINITIONS LINK_LIBRARIES ) if( _p_${_opt} ) diff -Nru ecflow-4.9.0/cmake/fcm-make-interfaces.cfg ecflow-4.11.1/cmake/fcm-make-interfaces.cfg --- ecflow-4.9.0/cmake/fcm-make-interfaces.cfg 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/fcm-make-interfaces.cfg 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -# (C) Copyright 2011- ECMWF. -# -# This software is licensed under the terms of the Apache Licence Version 2.0 -# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -# In applying this licence, ECMWF does not waive the privileges and immunities -# granted to it by virtue of its status as an intergovernmental organisation -# nor does it submit to any jurisdiction. - -# FCM configuration file used to auto-generate interface files -# for F77 and F90 files. -# Interface files will have the extention ".intfb.h" -# Results will be in a directory "interfaces/include" relative to cwd - -# Usage: fcm make --config-file= \ -# interfaces.ns-incl="" - -$SRC{?} = $HERE - -step.class[interfaces] = build -steps = interfaces - -interfaces.target{task} = ext-iface -interfaces.target{category} = include - -interfaces.source = $SRC - -# Exclude all -interfaces.ns-excl = / - -# Include some -# interfaces.ns-incl = - -# Extention of interface files -interfaces.prop{file-ext.f90-interface} = .intfb.h - -# Do not follow includes -interfaces.prop{no-dep.f.module} = * -interfaces.prop{no-dep.include} = * - diff -Nru ecflow-4.9.0/cmake/fcm-make-interfaces.cfg.in ecflow-4.11.1/cmake/fcm-make-interfaces.cfg.in --- ecflow-4.9.0/cmake/fcm-make-interfaces.cfg.in 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/cmake/fcm-make-interfaces.cfg.in 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,39 @@ +# (C) Copyright 2011- ECMWF. +# +# This software is licensed under the terms of the Apache Licence Version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. + +# FCM configuration file used to auto-generate interface files +# for F77 and F90 files. +# Interface files will have the extention "@P_SUFFIX@" +# Results will be in a directory "interfaces/include" relative to cwd + +# Usage: fcm make --config-file= \ +# interfaces.ns-incl="" + +$SRC{?} = $HERE + +step.class[interfaces] = build +steps = interfaces + +interfaces.target{task} = ext-iface +interfaces.target{category} = include + +interfaces.source = $SRC + +# Exclude all +interfaces.ns-excl = / + +# Include some +# interfaces.ns-incl = + +# Extention of interface files +interfaces.prop{file-ext.f90-interface} = @P_SUFFIX@ + +# Do not follow includes +interfaces.prop{no-dep.f.module} = * +interfaces.prop{no-dep.include} = * + diff -Nru ecflow-4.9.0/cmake/FindFFTW.cmake ecflow-4.11.1/cmake/FindFFTW.cmake --- ecflow-4.9.0/cmake/FindFFTW.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/FindFFTW.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -51,8 +51,8 @@ # :FFTW_ROOT: if set, this path is exclusively searched # :FFTW_DIR: equivalent to FFTW_ROOT # :FFTW_PATH: equivalent to FFTW_ROOT -# :FFTW_LIBRARY: FFTW library to use -# :FFTW_INCLUDE_DIR: FFTW include directory +# :FFTW_LIBRARIES: User overriden FFTW libraries +# :FFTW_INCLUDES: User overriden FFTW includes directories # ############################################################################## @@ -104,6 +104,7 @@ set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_SHARED_LIBRARY_SUFFIX} ) endif() + if( FFTW_FIND_COMPONENTS ) ecbuild_debug( "FindFFTW: looking for components: ${FFTW_FIND_COMPONENTS}" ) foreach( _component ${FFTW_FIND_COMPONENTS} ) @@ -136,71 +137,94 @@ set( _include_paths ${PKG_FFTW_INCLUDE_DIRS} ${INCLUDE_INSTALL_DIR} ) endif() -#find libs +# find includes -if( _require_dp ) - find_library( - FFTW_LIB - NAMES "fftw3" - PATHS ${_lib_paths} - PATH_SUFFIXES "lib" "lib64" - ${_default_paths} - ) - if( NOT FFTW_LIB ) - ecbuild_warn("FindFFTW: double precision required, but fftw3 was not found") - endif() -endif() +if( NOT FFTW_INCLUDES ) # allow user to override with FFTW_INCLUDES -if( _require_sp ) - find_library( - FFTWF_LIB - NAMES "fftw3f" - PATHS ${_lib_paths} - PATH_SUFFIXES "lib" "lib64" - ${_default_paths} - ) - if( NOT FFTWF_LIB ) - ecbuild_warn("FindFFTW: single precision required, but fftw3f was not found") - endif() -endif() + find_path( + FFTW_INCLUDES + NAMES "fftw3.h" + PATHS ${_include_paths} + PATH_SUFFIXES "include" + ${_default_paths} + ) -if( _require_lp ) - find_library( - FFTWL_LIB - NAMES "fftw3l" - PATHS ${_lib_paths} - PATH_SUFFIXES "lib" "lib64" - ${_default_paths} - ) - if( NOT FFTWL_LIB ) - ecbuild_warn("FindFFTW: long double precision required, but fftw3l was not found") - endif() -endif() + if( NOT FFTW_INCLUDES ) + ecbuild_warn("FindFFTW: fftw include headers not found") + endif() -if( _require_qp ) - find_library( - FFTWQ_LIB - NAMES "fftw3q" - PATHS ${_lib_paths} - PATH_SUFFIXES "lib" "lib64" - ${_default_paths} - ) - if( NOT FFTWQ_LIB ) - ecbuild_warn("FindFFTW: quad precision required, but fftw3q was not found") - endif() endif() -#find includes +# find libs + +if( NOT FFTW_LIBRARIES ) # allow user to override with FFTW_LIBRARIES (e.g. for MKL implementation) -find_path( - FFTW_INCLUDES - NAMES "fftw3.h" - PATHS ${_include_paths} - PATH_SUFFIXES "include" - ${_default_paths} -) + if( _require_dp ) + find_library( + FFTW_LIB + NAMES "fftw3" + PATHS ${_lib_paths} + PATH_SUFFIXES "lib" "lib64" + ${_default_paths} + ) + if( NOT FFTW_LIB ) + ecbuild_warn("FindFFTW: double precision required, but fftw3 was not found") + else() + ecbuild_info("FFTW double precision: ${FFTW_LIB}") + endif() + endif() + + if( _require_sp ) + find_library( + FFTWF_LIB + NAMES "fftw3f" + PATHS ${_lib_paths} + PATH_SUFFIXES "lib" "lib64" + ${_default_paths} + ) + if( NOT FFTWF_LIB ) + ecbuild_warn("FindFFTW: single precision required, but fftw3f was not found") + else() + ecbuild_info("FFTW single precision: ${FFTWF_LIB}") + endif() + endif() + + if( _require_lp ) + find_library( + FFTWL_LIB + NAMES "fftw3l" + PATHS ${_lib_paths} + PATH_SUFFIXES "lib" "lib64" + ${_default_paths} + ) + if( NOT FFTWL_LIB ) + ecbuild_warn("FindFFTW: long double precision required, but fftw3l was not found") + else() + ecbuild_info("FFTW long double precision: ${FFTWL_LIB}") + endif() + endif() + + if( _require_qp ) + find_library( + FFTWQ_LIB + NAMES "fftw3q" + PATHS ${_lib_paths} + PATH_SUFFIXES "lib" "lib64" + ${_default_paths} + ) + if( NOT FFTWQ_LIB ) + ecbuild_warn("FindFFTW: quad precision required, but fftw3q was not found") + else() + ecbuild_info("FFTW quad precision: ${FFTWQ_LIB}") + endif() + endif() + + set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB} ${FFTWL_LIB} ${FFTWQ_LIB}) + +endif() -set(FFTW_LIBRARIES ${FFTW_LIB} ${FFTWF_LIB} ${FFTWL_LIB} ${FFTWQ_LIB}) +ecbuild_info("FFTW includes : ${FFTW_INCLUDES}") +ecbuild_info("FFTW libraries: ${FFTW_LIBRARIES}") set( CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES_SAV} ) @@ -208,4 +232,4 @@ find_package_handle_standard_args(FFTW DEFAULT_MSG FFTW_INCLUDES FFTW_LIBRARIES) -mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB) +mark_as_advanced(FFTW_INCLUDES FFTW_LIBRARIES FFTW_LIB FFTWF_LIB FFTWL_LIB FFTWQ_LIB) diff -Nru ecflow-4.9.0/cmake/FindNAG.cmake ecflow-4.11.1/cmake/FindNAG.cmake --- ecflow-4.9.0/cmake/FindNAG.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/FindNAG.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -18,13 +18,13 @@ # NAG_DIR - root folder of the NAG installation # NAG_PATH - root folder of the NAG installation -find_path( NAG_INCLUDE_DIR nag_library.mod +find_path( NAG_INCLUDE_DIR nag_precisions.mod PATHS ${NAG_PATH} ENV NAG_PATH ${NAG_DIR} ENV NAG_DIR PATH_SUFFIXES include NO_DEFAULT_PATH ) -find_library( NAG_LIBRARY NAMES nag nag_nag +find_library( NAG_LIBRARY NAMES nag PATHS ${NAG_PATH} ENV NAG_PATH ${NAG_DIR} ENV NAG_DIR PATH_SUFFIXES lib lib64 diff -Nru ecflow-4.9.0/cmake/VERSION.cmake ecflow-4.11.1/cmake/VERSION.cmake --- ecflow-4.9.0/cmake/VERSION.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake/VERSION.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -1,7 +1,7 @@ set( ECBUILD_MAJOR_VERSION "2" ) -set( ECBUILD_MINOR_VERSION "8" ) +set( ECBUILD_MINOR_VERSION "9" ) set( ECBUILD_PATCH_VERSION "1" ) -set( ECBUILD_VERSION_STR "2.8.1" ) +set( ECBUILD_VERSION_STR "2.9.1" ) set( ECBUILD_MACRO_VERSION "${ECBUILD_VERSION_STR}" ) diff -Nru ecflow-4.9.0/CMakeLists.txt ecflow-4.11.1/CMakeLists.txt --- ecflow-4.9.0/CMakeLists.txt 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -13,6 +13,7 @@ # -DBUILD_SHARED_LIBS=OFF ############################################################################## +# Need cmake(3.12.0) for boost python3 libs to be found. cmake_minimum_required( VERSION 2.8.11 FATAL_ERROR ) # =========================================================================== @@ -84,16 +85,30 @@ find_package(Qt5Network 5.0.0) if (Qt5Network_FOUND) find_package(Qt5Svg 5.0.0) + if (Qt5Svg_FOUND) + find_package(Qt5Charts 5.0.0) + endif() endif() endif() endif() if (Qt5Widgets_FOUND AND Qt5Gui_FOUND AND Qt5Network_FOUND AND Qt5Svg_FOUND) ecbuild_info("Qt5 version ${Qt5Widgets_VERSION_STRING} was found") - set(ECFLOW_QT_INCLUDE_DIR ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIR} ${Qt5Network_INCLUDE_DIR} ${Qt5Svg_INCLUDE_DIR}) + set(ECFLOW_QT_INCLUDE_DIR ${Qt5Widgets_INCLUDE_DIRS} ${Qt5Gui_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS}) set(ECFLOW_QT_LIBRARIES ${Qt5Widgets_LIBRARIES} ${Qt5Gui_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5Svg_LIBRARIES}) set(ECFLOW_QT5 1) add_definitions(-DECFLOW_QT5) + + if(Qt5Charts_FOUND) + ecbuild_info("Qt5Charts was found - the server log viewer will be built") + list(APPEND ECFLOW_QT_INCLUDE_DIR ${Qt5Charts_INCLUDE_DIRS}) + list(APPEND ECFLOW_QT_LIBRARIES ${Qt5Charts_LIBRARIES}) + set(ECFLOW_LOGVIEW 1) + add_definitions(-DECFLOW_LOGVIEW) + else() + ecbuild_info("Qt5Charts was not found - the server log viewer will not be built") + endif() + else() ecbuild_info("Could not find all required Qt5 packages (Qt5Widgets, Qt5Gui and Qt5Network) - searching for Qt4") ecbuild_info(" ** To use a self-built Qt installation, try setting CMAKE_PREFIX_PATH **") @@ -138,6 +153,11 @@ ecbuild_error("If ENABLE_UI_USAGE_LOG is set, UI_LOG_FILE must also be set") endif() +# sanity check - if ENABLE_UI_USAGE_LOG is ON, we must also have UI_LOG_FILE +if(ENABLE_UI_USAGE_LOG AND (NOT LOGUI_LOG_FILE)) + ecbuild_error("If ENABLE_UI_USAGE_LOG is set, LOGUI_LOG_FILE must also be set") +endif() + # sanity check - if ENABLE_UI_USAGE_LOG is ON, we must also have UI_LOG_SITE_TAG if(ENABLE_UI_USAGE_LOG AND (NOT UI_LOG_SITE_TAG)) ecbuild_error("If ENABLE_UI_USAGE_LOG is set, UI_LOG_SITE_TAG must also be set") @@ -161,10 +181,15 @@ ecbuild_info( "UI_BACKTRACE_EMAIL_ADDRESS_FILE : ${UI_BACKTRACE_EMAIL_ADDRESS_FILE}" ) endif() + if(LOGUI_BACKTRACE_EMAIL_ADDRESS_FILE) + ecbuild_info( "LOGUI_BACKTRACE_EMAIL_ADDRESS_FILE : ${LOGUI_BACKTRACE_EMAIL_ADDRESS_FILE}" ) + endif() + ecbuild_info( "ENABLE_UI_USAGE_LOG : ${ENABLE_UI_USAGE_LOG}" ) if(ENABLE_UI_USAGE_LOG) ecbuild_info( "UI_LOG_FILE : ${UI_LOG_FILE}" ) - ecbuild_info( "UI_LOG_SITE_TAG : ${UI_LOG_SITE_TAG}" ) + ecbuild_info( "LOGUI_LOG_FILE : ${LOGUI_LOG_FILE}" ) + ecbuild_info( "UI_LOG_SITE_TAG : ${UI_LOG_SITE_TAG}" ) endif() endif() ecbuild_info("") @@ -192,6 +217,31 @@ endif() # ========================================================================================= +# Python: Must be done before BOOST +# ========================================================================================= + +if (ENABLE_PYTHON) + ecbuild_find_python( VERSION 2.6 REQUIRED ) + if( NOT PYTHON_FOUND ) + ecbuild_error("ecflow python extension is enabled, but python interpreter or libraries not found") + endif() + if ( NOT PYTHONLIBS_FOUND ) + ecbuild_error("ecflow python extension is enabled, but python libraries not found") + endif() + if ( PYTHON_VERSION_MAJOR EQUAL 3) + cmake_minimum_required( VERSION 3.12.0 FATAL_ERROR ) + endif() + message( STATUS " PYTHON_VERSION_STRING : ${PYTHON_VERSION_STRING}" ) + message( STATUS " PYTHON_VERSION_MAJOR : ${PYTHON_VERSION_MAJOR}" ) + message( STATUS " PYTHON_VERSION_MINOR : ${PYTHON_VERSION_MINOR}" ) + message( STATUS " PYTHON_VERSION_PATCH : ${PYTHON_VERSION_PATCH}" ) + message( STATUS " PYTHON_CONFIG_EXECUTABLE : ${PYTHON_CONFIG_EXECUTABLE}" ) + message( STATUS " PYTHON_EXECUTABLE : ${PYTHON_EXECUTABLE}" ) + message( STATUS " PYTHON_INCLUDE_DIRS : ${PYTHON_INCLUDE_DIRS}" ) + message( STATUS " PYTHON_LIBRARIES : ${PYTHON_LIBRARIES}" ) +endif() + +# ========================================================================================= # Boost # ========================================================================================= @@ -215,27 +265,17 @@ set(Boost_DETAILED_FAILURE_MSG ON) #set(Boost_DEBUG ON) +find_package( Boost 1.53.0 REQUIRED COMPONENTS serialization system thread unit_test_framework test_exec_monitor filesystem program_options date_time regex) if ( ENABLE_PYTHON ) - find_package( Boost 1.53.0 REQUIRED COMPONENTS python serialization system thread unit_test_framework test_exec_monitor filesystem program_options date_time regex) -else() - find_package( Boost 1.53.0 REQUIRED COMPONENTS serialization system thread unit_test_framework test_exec_monitor filesystem program_options date_time regex) -endif() - -# Available boost lib should be referenced as: -# -# ${Boost_SYSTEM_LIBRARY} -# ${Boost_SERIALIZATION_LIBRARY} -# ${Boost_THREAD_LIBRARY} -# ${Boost_FILESYSTEM_LIBRARY} -# ${Boost_PROGRAM_OPTIONS_LIBRARY} -# ${Boost_DATE_TIME_LIBRARY} -# -# ${Boost_UNIT_TEST_FRAMEWORK_LIBRARY} -# ${Boost_TEST_EXEC_MONITOR_LIBRARY} + if ( PYTHON_VERSION_MAJOR EQUAL 3) + find_package( Boost 1.53.0 REQUIRED COMPONENTS python3 ) + else() + find_package( Boost 1.53.0 REQUIRED COMPONENTS python ) + endif() +endif() #ecbuild_info( "Boost_LIBRARIES : ${Boost_LIBRARIES}" ) - if (NOT "${CMAKE_PREFIX_PATH}" EQUAL "${_CMAKE_PREFIX_PATH_BACKUP}") set (CMAKE_PREFIX_PATH ${_CMAKE_PREFIX_PATH_BACKUP}) # restore CMAKE_PREFIX_PATH ecbuild_debug("Resetting CMAKE_PREFIX_PATH to ${CMAKE_PREFIX_PATH}") @@ -300,18 +340,9 @@ endif() if (ENABLE_PYTHON) - ecbuild_find_python( VERSION 2.6 REQUIRED ) - # ecbuild_debug_var(PYTHON_LIBRARIES) - # ecbuild_debug_var(PYTHON_INCLUDE_DIR) - - if( NOT PYTHON_FOUND ) - ecbuild_error("ecflow python extension is enabled, but python interpreter or libraries not found") - endif() - if ( ENABLE_PYTHON_PTR_REGISTER ) add_definitions( -DECF_ENABLE_PYTHON_PTR_REGISTER ) endif() - add_subdirectory( Pyext ) endif() @@ -336,6 +367,7 @@ SCRATCH CUSTOMER build_scripts/nightly build_scripts/test_bench + cereal-1.2.2 Doc ) @@ -347,7 +379,6 @@ build_scripts/.pydevproject ) - # ========================================================================================= # final # ========================================================================================= diff -Nru ecflow-4.9.0/cmake.sh ecflow-4.11.1/cmake.sh --- ecflow-4.9.0/cmake.sh 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/cmake.sh 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,10 @@ echo " test_safe - only run deterministic tests" echo " ctest - all ctest -R -V" echo " clang - build with clang compiler" - echo " san - is short for clang thread sanitiser" + echo " clang_tidy - create compilation database for clang_tdiy and then call run-clang-tidy.py" + echo " tsan - is short for clang thread sanitiser" + echo " asan - is short for address sanitiser" + echo " msan - is short for memory sanitiser" echo " no_gui - Don't build the gui" echo " ssl - build using openssl" echo " secure_user - enable password for client server" @@ -42,8 +45,9 @@ test_arg= test_safe_arg= clang_arg= +clang_tidy_arg= intel_arg= -clang_sanitiser_arg= +tsan_arg= mode_arg=release verbose_arg= ctest_arg= @@ -53,6 +57,8 @@ ssl_arg= secure_user_arg= log_arg= +asan_arg= +msan_arg= while [[ "$#" != 0 ]] ; do if [[ "$1" = debug || "$1" = release ]] ; then mode_arg=$1 @@ -78,9 +84,12 @@ elif [[ "$1" = ecbuild ]] ; then ecbuild_arg=$1 ; elif [[ "$1" = log ]] ; then log_arg=$1 ; elif [[ "$1" = clang ]] ; then clang_arg=$1 ; + elif [[ "$1" = clang_tidy ]] ; then clang_tidy_arg=$1 ; elif [[ "$1" = intel ]] ; then intel_arg=$1 ; elif [[ "$1" = clean ]] ; then clean_arg=$1 ; - elif [[ "$1" = san ]] ; then clang_sanitiser_arg=$1 ; + elif [[ "$1" = tsan ]] ; then tsan_arg=$1 ; + elif [[ "$1" = asan ]] ; then asan_arg=$1 ; + elif [[ "$1" = msan ]] ; then msan_arg=$1 ; elif [[ "$1" = package_source ]] ; then package_source_arg=$1 ; elif [[ "$1" = copy_tarball ]] ; then copy_tarball_arg=$1 ; elif [[ "$1" = test ]] ; then test_arg=$1 ; @@ -108,7 +117,8 @@ echo "test_arg=$test_arg" echo "test_safe_arg=$test_safe_arg" echo "clang_arg=$clang_arg" -echo "clang_sanitiser_arg=$clang_sanitiser_arg" +echo "clang_tidy_arg=$clang_tidy_arg" +echo "tsan_arg=$tsan_arg" echo "mode_arg=$mode_arg" echo "verbose_arg=$verbose_arg" echo "python3_arg=$python3_arg" @@ -124,33 +134,52 @@ # GNU 6.1 -Wno-deprecated-declarations -> auto_ptr deprecated warning, mostly in boost headers # CLANG -ftemplate-depth=512 # -CXX_FLAGS="-Wno-unused-local-typedefs -Wno-unused-variable -Wno-deprecated-declarations" +CXX_FLAGS="-Wno-unused-local-typedefs -Wno-unused-variable -Wno-deprecated-declarations -Wno-address" # ==================== modules ================================================ # To load module automatically requires Korn shell, system start scripts -module load cmake/3.10.2 -module load ecbuild/2.8.1 +module load cmake/3.10.2 # need cmake 3.12.0 to build python3. Allow boost python libs to be found +module load ecbuild/2.9.0 +#module load boost/1.53.0 # uncomment to use local BOOST_ROOT cmake_extra_options="" -if [[ "$clang_arg" = clang ]] ; then - module unload gnu - module load clang/5.0.1 - CXX_FLAGS="" # latest clang with latest boost, should not need any warning suppression - cmake_extra_options="-DBOOST_ROOT=/var/tmp/ma0/boost/clang-5.0.1/boost_1_66_0" - - #CXX_FLAGS="$CXX_FLAGS -ftemplate-depth=512 -Wno-expansion-to-defined -Wno-unused-local-typedefs" - #cmake_extra_options="-DBOOST_ROOT=/var/tmp/ma0/boost/clang/boost_1_53_0" -fi -if [[ "$clang_sanitiser_arg" = san ]] ; then - module unload gnu - module load clang - CXX_FLAGS="$CXX_FLAGS -Wno-expansion-to-defined -fsanitize=thread" - cmake_extra_options="-DBOOST_ROOT=/var/tmp/ma0/boost/clang-5.0.1/boost_1_53_0" - - #CXX_FLAGS="$CXX_FLAGS -ftemplate-depth=512 -Wno-expansion-to-defined -fsanitize=thread" - #cmake_extra_options="-DBOOST_ROOT=/var/tmp/ma0/boost/clang/boost_1_53_0" +if [[ "$clang_arg" = clang || "$clang_tidy_arg" = clang_tidy ]] ; then + # ecflow fails to write boost ser' files with clang 6.0.1, but in debug all tests pass + # Had to apply fix: http://clang-developers.42468.n3.nabble.com/boost-serialization-crash-with-clang-5-0-0-td4058283.html + # - still have other crashes n serilisation see: ECFLOW-1328 + module unload gnu + module unload clang + module load clang/6.0.1 + cmake_extra_options="-DBOOST_ROOT=/var/tmp/ma0/boost/clang-6.0.1/boost_1_53_0" + + CXX_FLAGS="" + CXX_FLAGS="$CXX_FLAGS -Wno-deprecated-declarations -Wno-deprecated-register -Wno-expansion-to-defined" + + if [[ "$clang_tidy_arg" = clang_tidy ]] ; then + cmake_extra_options="$cmake_extra_options -DCMAKE_EXPORT_COMPILE_COMMANDS=ON" + fi fi + +# ============================================================================================== +# sanitisers +if [[ "$tsan_arg" = tsan && "$asan_arg" = asan ]] ; then + echo "Cant use address and thread sanitiser at the same time" +fi +if [[ "$tsan_arg" = tsan ]] ; then + CXX_FLAGS="$CXX_FLAGS -fsanitize=thread -fno-omit-frame-pointer" + cmake_extra_options="$cmake_extra_options -DCMAKE_EXE_LINKER_FLAGS='-fsanitize=thread'" # LINK FLAGS +fi +if [[ "$asan_arg" = asan ]] ; then + CXX_FLAGS="$CXX_FLAGS -fsanitize=address -fno-omit-frame-pointer" + cmake_extra_options="$cmake_extra_options -DCMAKE_EXE_LINKER_FLAGS='-fsanitize=address'" # LINK FLAGS +fi +if [[ "$msan_arg" = msan ]] ; then + CXX_FLAGS="$CXX_FLAGS -fsanitize=memory -fPIE -fno-omit-frame-pointer -fsanitize-memory-track-origins" + cmake_extra_options="$cmake_extra_options -DCMAKE_EXE_LINKER_FLAGS=-fsanitize=memory" # LINK FLAGS + #LINK_FLAGS='-DCMAKE_EXE_LINKER_FLAGS="-fsanitize=memory -fPIE -pie"' +fi + if [[ "$ARCH" = cray ]] ; then # disable new UI, no QT on cray @@ -172,9 +201,12 @@ if [[ "$python3_arg" = python3 ]] ; then module unload python - module load python3/3.5.1-01 - cmake_extra_options="$cmake_extra_options -DPYTHON_EXECUTABLE=/usr/local/apps/python3/3.5.1-01/bin/python3.5" - cmake_extra_options="$cmake_extra_options -DBOOST_ROOT=/var/tmp/ma0/boost/boost_1_53_0.python3" + module load python3/3.6.5-01 + + module unload cmake # need cmake 3.12.0 to build python3. Allow boost python libs to be found + module load cmake/3.12.0 + + cmake_extra_options="$cmake_extra_options -DPYTHON_EXECUTABLE=/usr/local/apps/python3/3.6.5-01/bin/python3.6" fi # ==================================================================================== @@ -225,7 +257,27 @@ fi fi if [[ "$ctest_arg" != "" ]] ; then - $ctest_arg + if [[ "$asan_arg" = asan ]] ; then + if [[ $clang_arg != "clang" ]] ; then + # for python module we need to preload asan as it needs to be the very first library + # ==2971==ASan runtime does not come first in initial library list; + # you should either link runtime to your application or manually preload it with LD_PRELOAD. + export LD_PRELOAD=/usr/local/apps/gcc/6.3.0/lib64/gcc/x86_64-suse-linux/6.3.0/libasan.so.3 + fi + export ASAN_OPTIONS=suppressions=$WK/build_scripts/ecflow_asan.supp + export LSAN_OPTIONS=suppressions=$WK/build_scripts/ecflow_lsan.supp + $ctest_arg + elif [[ "$tsan_arg" = tsan ]] ; then + if [[ $clang_arg != "clang" ]] ; then + # LD_PRELOAD needed otherwise we get: .... cannot allocate memory in static TLS block + export LD_PRELOAD=/usr/local/apps/gcc/6.3.0/lib64/gcc/x86_64-suse-linux/6.3.0/libtsan.so + fi + export ASAN_OPTIONS=suppressions=$WK/build_scripts/ecflow_asan.supp + export LSAN_OPTIONS=suppressions=$WK/build_scripts/ecflow_lsan.supp + $ctest_arg + else + $ctest_arg + fi exit 0 fi @@ -296,8 +348,9 @@ ${ssl_options} \ ${secure_user_options} \ ${log_options} \ - ${test_options} + ${test_options} # -DCMAKE_PREFIX_PATH="/tmp/$USER/opt/qt5/" + #-DCMAKE_EXE_LINKER_FLAGS='-fsanitize=memory -fPIE -pie' #-DENABLE_STATIC_BOOST_LIBS=ON \ #-DCMAKE_PYTHON_INSTALL_TYPE=local \ #-DENABLE_PYTHON=OFF \ @@ -310,7 +363,11 @@ #-DENABLE_SERVER=OFF \ #-DENABLE_PROFILING=ON \ #-DECBUILD_GPROF_FLAGS \ - +# export PATH=/tmp/$USER/opt/qt5/bin:$PATH + +if [[ "$clang_tidy_arg" = clang_tidy ]] ; then + python $WK/build_scripts/run-clang-tidy.py +fi # ============================================================================================= if [[ "$make_arg" != "" ]] ; then $make_arg diff -Nru ecflow-4.9.0/CSim/src/SimulatorVisitor.hpp ecflow-4.11.1/CSim/src/SimulatorVisitor.hpp --- ecflow-4.9.0/CSim/src/SimulatorVisitor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/CSim/src/SimulatorVisitor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -28,7 +28,7 @@ class SimulatorVisitor : public NodeTreeVisitor { public: - SimulatorVisitor(const std::string& defs_filename); + explicit SimulatorVisitor(const std::string& defs_filename); /// If we have crons and no endclock then show error message const std::string& errors_found() const { return error_msg_;} diff -Nru ecflow-4.9.0/debian/changelog ecflow-4.11.1/debian/changelog --- ecflow-4.9.0/debian/changelog 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/changelog 2018-11-06 14:21:45.000000000 +0000 @@ -1,3 +1,67 @@ +ecflow (4.11.1-1ubuntu1) disco; urgency=medium + + * Merge with Debian; remaining changes: + - Fix boost build. + - Build-depend on python3-all-dev. + + -- Matthias Klose Tue, 06 Nov 2018 15:21:45 +0100 + +ecflow (4.11.1-1) unstable; urgency=medium + + * New upstream release + + -- Alastair McKinstry Tue, 23 Oct 2018 12:41:24 +0100 + +ecflow (4.10.0-2ubuntu1) cosmic; urgency=medium + + * Fix boost build. + + -- Dimitri John Ledkov Wed, 19 Sep 2018 12:09:59 +0200 + +ecflow (4.11.0-1) unstable; urgency=medium + + * New upstream release + * Remove obsolete patches: + - set-paths.patch + - logsvr_syntax_fix.patch + - boost_python_tuple.patch + - qt5-11-transition.patch + * New patch + - boost python.patch (for Debian python lib name) + * Standards-Version: 4.2.1 + * Remove hard-coded xz compression for debian tarball + + -- Alastair McKinstry Thu, 18 Oct 2018 11:57:51 +0100 + +ecflow (4.10.0-3) unstable; urgency=medium + + * Add python3.7 B-D (temporarily needed on i386? ) + * Standards-Version: 4.2.0 + * Build -fPIC on i386. + + -- Alastair McKinstry Mon, 03 Sep 2018 14:56:32 +0100 + +ecflow (4.10.0-2) unstable; urgency=medium + + * Fix for FTBFS with Qt5.11 + * Update URLs in debian/watch + + -- Alastair McKinstry Sat, 28 Jul 2018 08:43:18 +0100 + +ecflow (4.10.0-1) unstable; urgency=medium + + * New upstream release + * Standards-Version: 4.1.5 + * Add build-dep for libqt5charts5-dev + * Add copyright for ./Viewer/ecflowUI/images/spinning_wheel.gif + * Update docs to not include pdf, docx files + * python3-ecflow now uses python3:Depends, not python:Depends + * Ensure both py3.6, py3.7 extensions are built. + * Add Apache NOTICE to docs + * Fix for python3 + + -- Alastair McKinstry Thu, 26 Jul 2018 07:17:12 +0100 + ecflow (4.9.0-3) unstable; urgency=medium * Temporarily drop slurm dependency that is blocking ecflow diff -Nru ecflow-4.9.0/debian/control ecflow-4.11.1/debian/control --- ecflow-4.9.0/debian/control 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/control 2018-11-06 14:21:45.000000000 +0000 @@ -1,12 +1,13 @@ Source: ecflow Section: science Priority: optional -Maintainer: Alastair McKinstry +Maintainer: Ubuntu Developers +XSBC-Original-Maintainer: Alastair McKinstry Build-Depends: debhelper (>= 10), dh-buildinfo, dh-python, python-dev, - python3-dev, + python3-all-dev, cmake, chrpath, libmotif-dev, @@ -18,13 +19,14 @@ libqt5core5a, libqt5network5, libqt5svg5-dev, + libqt5charts5-dev, libboost-test-dev, libboost-serialization-dev, libboost-date-time-dev, libboost-thread-dev, libboost-regex-dev, libboost-python-dev, libboost-program-options-dev, libboost-filesystem-dev -Standards-Version: 4.1.4 +Standards-Version: 4.2.1 Homepage: https://software.ecmwf.int/wiki/display/ECFLOW/The+ECFLOW+Pre-processor Vcs-Browser: https://salsa.debian.org:/science-team/ecflow.git Vcs-Git: https://salsa.debian.org:/science-team/ecflow.git @@ -91,7 +93,7 @@ Section: python Architecture: any Depends: libecflow-view0d ( = ${binary:Version}), ${misc:Depends}, ${shlibs:Depends}, - ${python:Depends} + ${python3:Depends} Description: Python3 libraries for ecFlow workflow tools ecFlow is a meteorological work flow package that enables users to run a large number of programs ( with dependencies diff -Nru ecflow-4.9.0/debian/copyright ecflow-4.11.1/debian/copyright --- ecflow-4.9.0/debian/copyright 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/copyright 2018-10-23 11:41:24.000000000 +0000 @@ -13,6 +13,10 @@ The texts of this license can be found in /usr/share/common-licenses/Apache-2.0 +File: ./Viewer/ecflowUI/images/spinning_wheel.gif +Copyright: (C) 2004 Sam Hocevar +License: Ajaxinfo + Files: ACore/src/portable_binary_* Files: build_scripts/aix_fix/* Files: build_scripts/hpux_fix/* @@ -114,3 +118,17 @@ May restrict the use of the name and description of the library to the standard version found on the Boost web site. +License: Ajaxinfo + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + Version 2, December 2004 + . + Copyright (C) 2004 Sam Hocevar + . + Everyone is permitted to copy and distribute verbatim or modified + copies of this license document, and changing it is allowed as long + as the name is changed. + . + DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + . + 0. You just DO WHAT THE FUCK YOU WANT TO. diff -Nru ecflow-4.9.0/debian/ecflow-client.docs ecflow-4.11.1/debian/ecflow-client.docs --- ecflow-4.9.0/debian/ecflow-client.docs 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/ecflow-client.docs 2018-10-23 11:41:24.000000000 +0000 @@ -1,4 +1,2 @@ README -Doc/user-manual/client_options.docx -Doc/user-manual/user_manual.docx -Doc/user-manual/user_manual.pdf +NOTICE diff -Nru ecflow-4.9.0/debian/ecflow-server.docs ecflow-4.11.1/debian/ecflow-server.docs --- ecflow-4.9.0/debian/ecflow-server.docs 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/ecflow-server.docs 2018-10-23 11:41:24.000000000 +0000 @@ -1,4 +1,2 @@ README -Doc/user-manual/client_options.docx -Doc/user-manual/user_manual.docx -Doc/user-manual/user_manual.pdf +NOTICE diff -Nru ecflow-4.9.0/debian/patches/auto_ptr.patch ecflow-4.11.1/debian/patches/auto_ptr.patch --- ecflow-4.9.0/debian/patches/auto_ptr.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/auto_ptr.patch 2018-10-23 11:41:24.000000000 +0000 @@ -3,12 +3,12 @@ Last-Updated: 2017-11-14 Forwarded: no -Index: ecflow-4.8.0/ANode/src/ExprDuplicate.cpp +Index: ecflow-4.10.0/ANode/src/ExprDuplicate.cpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/ExprDuplicate.cpp -+++ ecflow-4.8.0/ANode/src/ExprDuplicate.cpp -@@ -45,13 +45,13 @@ ExprDuplicate::~ExprDuplicate() - duplicate_expr.clear(); +--- ecflow-4.10.0.orig/ANode/src/ExprDuplicate.cpp ++++ ecflow-4.10.0/ANode/src/ExprDuplicate.cpp +@@ -55,13 +55,13 @@ void ExprDuplicate::dump(const std::stri + } } -std::auto_ptr ExprDuplicate::find(const std::string& expr) @@ -24,10 +24,10 @@ } void ExprDuplicate::add(const std::string& expr,AstTop* ast) -Index: ecflow-4.8.0/ANode/src/ExprDuplicate.hpp +Index: ecflow-4.10.0/ANode/src/ExprDuplicate.hpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/ExprDuplicate.hpp -+++ ecflow-4.8.0/ANode/src/ExprDuplicate.hpp +--- ecflow-4.10.0.orig/ANode/src/ExprDuplicate.hpp ++++ ecflow-4.10.0/ANode/src/ExprDuplicate.hpp @@ -24,7 +24,7 @@ // ========================================================================= @@ -37,8 +37,8 @@ #include class AstTop; -@@ -35,7 +35,7 @@ public: - ~ExprDuplicate(); +@@ -38,7 +38,7 @@ public: + static void dump(const std::string& msg ); // Find the expr in the map, if found returns a CLONED ast, else NULL - static std::auto_ptr find(const std::string& expr); @@ -46,10 +46,10 @@ // Add the expr to the map, the ast is cloned. static void add(const std::string& expr,AstTop*); -Index: ecflow-4.8.0/ANode/src/Expression.cpp +Index: ecflow-4.10.0/ANode/src/Expression.cpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/Expression.cpp -+++ ecflow-4.8.0/ANode/src/Expression.cpp +--- ecflow-4.10.0.orig/ANode/src/Expression.cpp ++++ ecflow-4.10.0/ANode/src/Expression.cpp @@ -65,7 +65,7 @@ std::ostream& PartExpression::print(std: return os; } @@ -150,10 +150,10 @@ theCombinedAst_->exprType(exprType); } // cout << "****************************************************************\n"; -Index: ecflow-4.8.0/ANode/src/ExprParser.hpp +Index: ecflow-4.10.0/ANode/src/ExprParser.hpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/ExprParser.hpp -+++ ecflow-4.8.0/ANode/src/ExprParser.hpp +--- ecflow-4.10.0.orig/ANode/src/ExprParser.hpp ++++ ecflow-4.10.0/ANode/src/ExprParser.hpp @@ -17,7 +17,7 @@ //============================================================================ @@ -193,10 +193,10 @@ }; #endif -Index: ecflow-4.8.0/Base/src/cts/AlterCmd.cpp +Index: ecflow-4.10.0/Base/src/cts/AlterCmd.cpp =================================================================== ---- ecflow-4.8.0.orig/Base/src/cts/AlterCmd.cpp -+++ ecflow-4.8.0/Base/src/cts/AlterCmd.cpp +--- ecflow-4.10.0.orig/Base/src/cts/AlterCmd.cpp ++++ ecflow-4.10.0/Base/src/cts/AlterCmd.cpp @@ -1056,7 +1056,7 @@ void AlterCmd::createChange( Cmd_ptr& cm name = options[2]; @@ -215,11 +215,11 @@ if (!ast.get()) { ss << error_msg << "\n" << dump_args(options,paths) << "\n"; throw std::runtime_error( ss.str() ); -Index: ecflow-4.8.0/view/src/host.cc +Index: ecflow-4.10.0/view/src/host.cc =================================================================== ---- ecflow-4.8.0.orig/view/src/host.cc -+++ ecflow-4.8.0/view/src/host.cc -@@ -577,7 +577,7 @@ void host::dir( node& n, const char* pat +--- ecflow-4.10.0.orig/view/src/host.cc ++++ ecflow-4.10.0/view/src/host.cc +@@ -585,7 +585,7 @@ void host::dir( node& n, const char* pat { gui::message("%s: fetching file list", this->name()); std::string content; @@ -228,7 +228,7 @@ std::string job = n.variable("ECF_JOB"); // if (use_ecf_out_cmd(n, path, dir.get(), content)) { l.scan(dir.get()); } else std::cout << n.full_name() << "\n" << path << "\n"; -@@ -586,7 +586,7 @@ void host::dir( node& n, const char* pat +@@ -594,7 +594,7 @@ void host::dir( node& n, const char* pat } else if (loghost_ != ecf_node::none()) { logsvr log_server(loghost_, logport_); if (log_server.ok()) { @@ -237,7 +237,7 @@ if (rdir.get()) { l.scan(rdir.get()); } -@@ -622,7 +622,7 @@ void host::dir( node& n, const char* pat +@@ -630,7 +630,7 @@ void host::dir( node& n, const char* pat c++; } @@ -246,10 +246,10 @@ if (ldir.get()) { l.scan(ldir.get()); } -Index: ecflow-4.8.0/ANode/src/Expression.hpp +Index: ecflow-4.10.0/ANode/src/Expression.hpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/Expression.hpp -+++ ecflow-4.8.0/ANode/src/Expression.hpp +--- ecflow-4.10.0.orig/ANode/src/Expression.hpp ++++ ecflow-4.10.0/ANode/src/Expression.hpp @@ -17,7 +17,7 @@ //============================================================================ @@ -294,10 +294,10 @@ Expression& operator=(Expression const& f); private: -Index: ecflow-4.8.0/ANode/test/TestExprParser.cpp +Index: ecflow-4.10.0/ANode/test/TestExprParser.cpp =================================================================== ---- ecflow-4.8.0.orig/ANode/test/TestExprParser.cpp -+++ ecflow-4.8.0/ANode/test/TestExprParser.cpp +--- ecflow-4.10.0.orig/ANode/test/TestExprParser.cpp ++++ ecflow-4.10.0/ANode/test/TestExprParser.cpp @@ -94,7 +94,7 @@ BOOST_AUTO_TEST_CASE( test_expression_pa PartExpression part(vec[i]); @@ -316,10 +316,10 @@ BOOST_REQUIRE_MESSAGE(ast.get(),"Failed to parse " << vec[i] << " " << parseErrorMsg); std::stringstream s2; -Index: ecflow-4.8.0/ANode/src/ExprParser.cpp +Index: ecflow-4.10.0/ANode/src/ExprParser.cpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/ExprParser.cpp -+++ ecflow-4.8.0/ANode/src/ExprParser.cpp +--- ecflow-4.10.0.orig/ANode/src/ExprParser.cpp ++++ ecflow-4.10.0/ANode/src/ExprParser.cpp @@ -1016,7 +1016,7 @@ AstTop* createTopAst( std::cout << "\nPRINT_AST_TRAVERSAL " << expr << "\n"; #endif @@ -329,11 +329,11 @@ (void)doCreateAst(info.trees.begin(),rule_names,ast.get() ); if (!ast->is_valid_ast(error_msg)) { -Index: ecflow-4.8.0/ANode/src/Node.cpp +Index: ecflow-4.10.0/ANode/src/Node.cpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/Node.cpp -+++ ecflow-4.8.0/ANode/src/Node.cpp -@@ -1355,9 +1355,9 @@ bool Node::check_expressions(Ast* ast,co +--- ecflow-4.10.0.orig/ANode/src/Node.cpp ++++ ecflow-4.10.0/ANode/src/Node.cpp +@@ -1382,9 +1382,9 @@ bool Node::check_expressions(Ast* ast,co return true; } @@ -345,11 +345,11 @@ std::string errorMsg; if (!check_expressions(ast.get(),expr,trigger,errorMsg)) { -Index: ecflow-4.8.0/ANode/src/Node.hpp +Index: ecflow-4.10.0/ANode/src/Node.hpp =================================================================== ---- ecflow-4.8.0.orig/ANode/src/Node.hpp -+++ ecflow-4.8.0/ANode/src/Node.hpp -@@ -586,7 +586,7 @@ public: +--- ecflow-4.10.0.orig/ANode/src/Node.hpp ++++ ecflow-4.10.0/ANode/src/Node.hpp +@@ -601,7 +601,7 @@ public: bool check_expressions(Ast*,const std::string& expr, bool trigger, std::string& errorMsg) const; /// check trigger expression have nodes and events,meter,repeat that resolve, will throw for error @@ -358,10 +358,10 @@ protected: /// Used in conjunction with Node::position() -Index: ecflow-4.8.0/Base/src/cts/TaskCmds.cpp +Index: ecflow-4.10.0/Base/src/cts/TaskCmds.cpp =================================================================== ---- ecflow-4.8.0.orig/Base/src/cts/TaskCmds.cpp -+++ ecflow-4.8.0/Base/src/cts/TaskCmds.cpp +--- ecflow-4.10.0.orig/Base/src/cts/TaskCmds.cpp ++++ ecflow-4.10.0/Base/src/cts/TaskCmds.cpp @@ -455,7 +455,7 @@ STC_Cmd_ptr CtsWaitCmd::doHandleRequest( // If the expression references paths that don't exist throw an error // This can be captured in the ecf script, which should then abort the task diff -Nru ecflow-4.9.0/debian/patches/boost-python3.patch ecflow-4.11.1/debian/patches/boost-python3.patch --- ecflow-4.9.0/debian/patches/boost-python3.patch 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/debian/patches/boost-python3.patch 2018-10-23 11:41:24.000000000 +0000 @@ -0,0 +1,26 @@ +Description: Build Fix for boost python3 library name in Debian + Ignore python3 missing; its named differently +Author: Alastair McKinstry +Last-Updated: 2018-10-18 +Forwarded: not-needed + +Index: ecflow-4.11.0/CMakeLists.txt +=================================================================== +--- ecflow-4.11.0.orig/CMakeLists.txt ++++ ecflow-4.11.0/CMakeLists.txt +@@ -267,11 +267,11 @@ set(Boost_DETAILED_FAILURE_MSG ON) + + find_package( Boost 1.53.0 REQUIRED COMPONENTS serialization system thread unit_test_framework test_exec_monitor filesystem program_options date_time regex) + if ( ENABLE_PYTHON ) +- if ( PYTHON_VERSION_MAJOR EQUAL 3) +- find_package( Boost 1.53.0 REQUIRED COMPONENTS python3 ) +- else() ++# if ( PYTHON_VERSION_MAJOR EQUAL 3) ++# find_package( Boost 1.53.0 REQUIRED COMPONENTS python3 ) ++# else() + find_package( Boost 1.53.0 REQUIRED COMPONENTS python ) +- endif() ++# endif() + endif() + + #ecbuild_info( "Boost_LIBRARIES : ${Boost_LIBRARIES}" ) diff -Nru ecflow-4.9.0/debian/patches/boost_python_tuple.patch ecflow-4.11.1/debian/patches/boost_python_tuple.patch --- ecflow-4.9.0/debian/patches/boost_python_tuple.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/boost_python_tuple.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -Description: Fix for python::boost tuple issue - With latest C++ standards, 'tuple' is ambiguous, so we explicitly - declare boost::python::tuple -Author: Alastair McKinstry -Last-Updated: 2018-02-01 -Forwarded: no - -Index: ecflow-4.8.0/Pyext/src/Edit.cpp -=================================================================== ---- ecflow-4.8.0.orig/Pyext/src/Edit.cpp -+++ ecflow-4.8.0/Pyext/src/Edit.cpp -@@ -24,7 +24,7 @@ namespace bp = boost::python; - Edit::Edit(const boost::python::dict& dict){BoostPythonUtil::dict_to_str_vec(dict,vec_);} - Edit::Edit(const boost::python::dict& dict,const boost::python::dict& dict2){BoostPythonUtil::dict_to_str_vec(dict,vec_);BoostPythonUtil::dict_to_str_vec(dict2,vec_);} - --object Edit::init(tuple args, dict kw) { -+object Edit::init(boost::python::tuple args, dict kw) { - //cout << "Edit::init args: " << len(args) << " kwargs " << len(kw) << "\n"; - // args[0] is Edit(i.e self) - for (int i = 1; i < len(args) ; ++i) { -@@ -34,6 +34,6 @@ object Edit::init(tuple args, dict kw) { - } - else throw std::runtime_error("Edit::Edit: only accepts dictionary and key word arguments"); - } -- tuple rest(args.slice(1,_)); -+ boost::python::tuple rest(args.slice(1,_)); - return args[0].attr("__init__")(kw); // calls -> .def(init() -> Edit(const boost::python::dict& dict) - } -Index: ecflow-4.8.0/Pyext/src/ExportDefs.cpp -=================================================================== ---- ecflow-4.8.0.orig/Pyext/src/ExportDefs.cpp -+++ ecflow-4.8.0/Pyext/src/ExportDefs.cpp -@@ -190,7 +190,7 @@ static object do_add(defs_ptr self, cons - return object(self); - } - --static object add(tuple args, dict kwargs) { -+static object add(boost::python::tuple args, dict kwargs) { - int the_list_size = len(args); - defs_ptr self = extract(args[0]); // self - if (!self) throw std::runtime_error("ExportDefs::add() : first argument is not a Defs"); -@@ -221,7 +221,7 @@ static object defs_getattr(defs_ptr self - return object(); - } - --object defs_raw_constructor(tuple args, dict kw) { -+object defs_raw_constructor(boost::python::tuple args, dict kw) { - // cout << "defs_raw_constructor len(args):" << len(args) << endl; - // args[0] is Defs(i.e self) - bp::list the_list; -Index: ecflow-4.8.0/Pyext/src/ExportNodeAttr.cpp -=================================================================== ---- ecflow-4.8.0.orig/Pyext/src/ExportNodeAttr.cpp -+++ ecflow-4.8.0/Pyext/src/ExportNodeAttr.cpp -@@ -49,7 +49,7 @@ namespace bp = boost::python; - - // See: http://wiki.python.org/moin/boost.python/HowTo#boost.function_objects - /////////////////////////////////////////////////////////////////////////////////////////////////// --object late_raw_constructor(tuple args, dict kw) { -+object late_raw_constructor(boost::python::tuple args, dict kw) { - //cout << "late_raw_constructor len(args):" << len(args) << endl; - // args[0] is Late(i.e self) - if (len(args) > 1) throw std::runtime_error("late_raw_constructor: Late only expects keyword arguments, ie. Late(submitted='00:20',active='15:00',complete='+30:00')"); -@@ -87,7 +87,7 @@ static boost::shared_ptr late_ - - - /////////////////////////////////////////////////////////////////////////////////////////////////// --object cron_raw_constructor(tuple args, dict kw) { -+object cron_raw_constructor(boost::python::tuple args, dict kw) { - //cout << "cron_raw_constructor len(args):" << len(args) << endl; - // args[0] is Cron(i.e self) args[1] is string name - for (int i = 1; i < len(args) ; ++i) { -Index: ecflow-4.8.0/Pyext/src/ExportNode.cpp -=================================================================== ---- ecflow-4.8.0.orig/Pyext/src/ExportNode.cpp -+++ ecflow-4.8.0/Pyext/src/ExportNode.cpp -@@ -175,7 +175,7 @@ static object do_lshift(node_ptr self, c - return object(self); - } - --static object add(tuple args, dict kwargs) -+static object add(boost::python::tuple args, dict kwargs) - { - int the_list_size = len(args); - node_ptr self = extract(args[0]); // self -Index: ecflow-4.8.0/Pyext/src/NodeUtil.cpp -=================================================================== ---- ecflow-4.8.0.orig/Pyext/src/NodeUtil.cpp -+++ ecflow-4.8.0/Pyext/src/NodeUtil.cpp -@@ -47,7 +47,7 @@ using namespace ecf; - namespace bp = boost::python; - - --object NodeUtil::node_raw_constructor(tuple args, dict kw) { -+object NodeUtil::node_raw_constructor(boost::python::tuple args, dict kw) { - // cout << "node_raw_constructor len(args):" << len(args) << endl; - // args[0] is Task(i.e self) args[1] is string name - bp::list the_list; diff -Nru ecflow-4.9.0/debian/patches/logsvr_syntax_fix.patch ecflow-4.11.1/debian/patches/logsvr_syntax_fix.patch --- ecflow-4.9.0/debian/patches/logsvr_syntax_fix.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/logsvr_syntax_fix.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -Description: Fix syntax error -Author: Alastair McKinstry -Last-Updated: 2017-10-14 -Forwarded: no - -Index: ecflow-4.8.0/tools/ecflow_logsvr.sh -=================================================================== ---- ecflow-4.8.0.orig/tools/ecflow_logsvr.sh -+++ ecflow-4.8.0/tools/ecflow_logsvr.sh -@@ -37,7 +37,7 @@ LOGMAP=/emos_esuite:/emos_esuite:/vol/em - log=/var/log/emos/logsvr.log - if [[ -x /usr/sbin/logsvr.pl ]] ; then - LOGSVR=/usr/sbin/logsvr.pl --if [[ -x /usr/local/lib/metaps/perl/logsvr.pl ]] ; then -+elif [[ -x /usr/local/lib/metaps/perl/logsvr.pl ]] ; then - LOGSVR=/usr/local/lib/metaps/perl/logsvr.pl - elif [[ -x /usr/local/apps/sms/bin/logsvr.pl ]] ; then - LOGSVR=/usr/local/apps/sms/bin/logsvr.pl diff -Nru ecflow-4.9.0/debian/patches/minver.patch ecflow-4.11.1/debian/patches/minver.patch --- ecflow-4.9.0/debian/patches/minver.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/minver.patch 2018-10-23 11:41:24.000000000 +0000 @@ -5,16 +5,16 @@ Last-Updated: 2017-07-11 Forwarded: no -Index: ecflow-4.8.0/CMakeLists.txt +Index: ecflow-4.11.0/CMakeLists.txt =================================================================== ---- ecflow-4.8.0.orig/CMakeLists.txt -+++ ecflow-4.8.0/CMakeLists.txt -@@ -300,7 +300,7 @@ if (ENABLE_SERVER) - endif() +--- ecflow-4.11.0.orig/CMakeLists.txt ++++ ecflow-4.11.0/CMakeLists.txt +@@ -221,7 +221,7 @@ endif() + # ========================================================================================= if (ENABLE_PYTHON) - ecbuild_find_python( VERSION 2.6 REQUIRED ) + ecbuild_find_python( VERSION ${PYTHON_MIN_VERSION} REQUIRED ) - # ecbuild_debug_var(PYTHON_LIBRARIES) - # ecbuild_debug_var(PYTHON_INCLUDE_DIR) - + if( NOT PYTHON_FOUND ) + ecbuild_error("ecflow python extension is enabled, but python interpreter or libraries not found") + endif() diff -Nru ecflow-4.9.0/debian/patches/python3.patch ecflow-4.11.1/debian/patches/python3.patch --- ecflow-4.9.0/debian/patches/python3.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/python3.patch 2018-10-23 11:41:24.000000000 +0000 @@ -3,10 +3,10 @@ Forwarded: no Last-Updated: 2018-10-16 -Index: ecflow-4.8.0/Pyext/script.py +Index: ecflow-4.10.0/Pyext/script.py =================================================================== ---- ecflow-4.8.0.orig/Pyext/script.py -+++ ecflow-4.8.0/Pyext/script.py +--- ecflow-4.10.0.orig/Pyext/script.py ++++ ecflow-4.10.0/Pyext/script.py @@ -7,5 +7,7 @@ # @@ -16,10 +16,10 @@ + +print ('Hello World !') number = 42 -Index: ecflow-4.8.0/Pyext/unicode.py +Index: ecflow-4.10.0/Pyext/unicode.py =================================================================== ---- ecflow-4.8.0.orig/Pyext/unicode.py -+++ ecflow-4.8.0/Pyext/unicode.py +--- ecflow-4.10.0.orig/Pyext/unicode.py ++++ ecflow-4.10.0/Pyext/unicode.py @@ -1,7 +1,7 @@ # Determine the python unicode - @@ -32,10 +32,10 @@ - print 'UCS2 build' \ No newline at end of file + print ('UCS2 build') -Index: ecflow-4.8.0/Pyext/samples/api/sms2ecf.py +Index: ecflow-4.10.0/Pyext/samples/api/sms2ecf.py =================================================================== ---- ecflow-4.8.0.orig/Pyext/samples/api/sms2ecf.py -+++ ecflow-4.8.0/Pyext/samples/api/sms2ecf.py +--- ecflow-4.10.0.orig/Pyext/samples/api/sms2ecf.py ++++ ecflow-4.10.0/Pyext/samples/api/sms2ecf.py @@ -1,5 +1,6 @@ #!/usr/bin/env python """ one module to store needed function to translate SMS-ECFLOW suites """ @@ -82,3 +82,15 @@ def load(name, host="localhost", port=31415): import ecflow as ec +Index: ecflow-4.10.0/Pyext/samples/api/ecf.py +=================================================================== +--- ecflow-4.10.0.orig/Pyext/samples/api/ecf.py ++++ ecflow-4.10.0/Pyext/samples/api/ecf.py +@@ -2034,7 +2034,6 @@ class Defs(object): + return suite + + +-global DEFS + # DEFS = ecflow.Defs() + DEFS = Defs() + diff -Nru ecflow-4.9.0/debian/patches/reproducible.patch ecflow-4.11.1/debian/patches/reproducible.patch --- ecflow-4.9.0/debian/patches/reproducible.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/reproducible.patch 2018-10-23 11:41:24.000000000 +0000 @@ -3,10 +3,10 @@ Last-Updated: 2017-07-11 Forwarded: no -Index: ecflow-4.8.0/ACore/src/Version.cpp +Index: ecflow-4.11.0/ACore/src/Version.cpp =================================================================== ---- ecflow-4.8.0.orig/ACore/src/Version.cpp -+++ ecflow-4.8.0/ACore/src/Version.cpp +--- ecflow-4.11.0.orig/ACore/src/Version.cpp ++++ ecflow-4.11.0/ACore/src/Version.cpp @@ -85,7 +85,7 @@ std::string Version::description() ss << " openssl"; #endif diff -Nru ecflow-4.9.0/debian/patches/series ecflow-4.11.1/debian/patches/series --- ecflow-4.9.0/debian/patches/series 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/series 2018-10-23 11:41:24.000000000 +0000 @@ -5,6 +5,4 @@ reproducible.patch minver.patch soname.patch -# set-paths.patch -# logsvr_syntax_fix.patch -# boost_python_tuple.patch +boost-python3.patch diff -Nru ecflow-4.9.0/debian/patches/set-paths.patch ecflow-4.11.1/debian/patches/set-paths.patch --- ecflow-4.9.0/debian/patches/set-paths.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/set-paths.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -Description: Set paths to Debian locations -Author: Alastair McKinstry -Last-Updated: 2017-07-17 -Forwarded: not-needed - -Index: ecflow-4.8.0/tools/ecflow_logsvr.sh -=================================================================== ---- ecflow-4.8.0.orig/tools/ecflow_logsvr.sh -+++ ecflow-4.8.0/tools/ecflow_logsvr.sh -@@ -30,6 +30,13 @@ else - log=/sms/logsvr.log - fi - -+# Debian -+LOGPATH=/var/log/emos -+LOGMAP=/emos_esuite:/emos_esuite:/vol/emos/output:/emos_esuite:/vol/emos/output:/$MACHINE_NAME/emos_dir:/$MACHINE_NAME/emos_dir:/$MACHINE_NAME/emos_dir:/emos_esuite:/e\ -+ mos_esuite:/vol/emos/output:/emos_dir:/emos_dir:/emos_dir -+log=/var/log/emos/logsvr.log -+if [[ -x /usr/sbin/logsvr.pl ]] ; then -+ LOGSVR=/usr/sbin/logsvr.pl - if [[ -x /usr/local/lib/metaps/perl/logsvr.pl ]] ; then - LOGSVR=/usr/local/lib/metaps/perl/logsvr.pl - elif [[ -x /usr/local/apps/sms/bin/logsvr.pl ]] ; then diff -Nru ecflow-4.9.0/debian/patches/soname.patch ecflow-4.11.1/debian/patches/soname.patch --- ecflow-4.9.0/debian/patches/soname.patch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/patches/soname.patch 2018-10-23 11:41:24.000000000 +0000 @@ -1,10 +1,10 @@ Description: Add a version number and name to package -Index: ecflow-4.8.0/view/CMakeLists.txt +Index: ecflow-4.11.0/view/CMakeLists.txt =================================================================== ---- ecflow-4.8.0.orig/view/CMakeLists.txt -+++ ecflow-4.8.0/view/CMakeLists.txt +--- ecflow-4.11.0.orig/view/CMakeLists.txt ++++ ecflow-4.11.0/view/CMakeLists.txt @@ -206,7 +206,9 @@ ecbuild_add_library(TARGET ecflow_vie CONDITION MOTIF_FOUND AND X11_FOUND AND CMAKE_THREAD_LIBS_INIT diff -Nru ecflow-4.9.0/debian/rules ecflow-4.11.1/debian/rules --- ecflow-4.9.0/debian/rules 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/rules 2018-11-06 14:21:45.000000000 +0000 @@ -13,14 +13,15 @@ TARGET_ARCH ?= $(shell dpkg-architecture -qDEB_TARGET_ARCH_CPU) AUTOGENERATED:= ecflow-server.install ecflow-client.install libecflow-view0d.install -PY3:=$(shell py3versions -d) -PY3AB:=$(shell py3versions -d | sed -e 's/python3\./py3/' ) -PY3VER:=$(shell py3versions -d | sed -e 's/python//' ) +PY3VERSIONS:=$(shell py3versions -s) FPIC:= ifeq ($(TARGET_ARCH), hppa) FPIC:= -fPIC endif +ifeq ($(TARGET_ARCH), i386) + FPIC:= -fPIC +endif CFLAGS:=$(shell dpkg-buildflags --get CFLAGS) $(FPIC) $(CPPFLAGS) -DNO_REGEXP LDFLAGS:=$(shell dpkg-buildflags --get LDFLAGS) $(FPIC) @@ -50,19 +51,28 @@ dh_auto_configure --builddirectory=debian/build-py2 -- \ ${CMAKE_COMMON_FLAGS} \ -DPYTHON_MIN_VERSION=2.7 \ - -DBoost_PYTHON_LIBRARY_RELEASE=$(LIBDIR)/libboost_python-py27.so - dh_auto_configure --builddirectory=debian/build-py3 -- \ - ${CMAKE_COMMON_FLAGS} \ - -DPYTHON_MIN_VERSION=${PY3VER} \ - -DBoost_PYTHON_LIBRARY_RELEASE=$(LIBDIR)/libboost_python-${PY3AB}.so + -DPYTHON_EXECUTABLE=python2.7 \ + -DBoost_PYTHON_LIBRARY_RELEASE=$(LIBDIR)/libboost_python27.so + for p in ${PY3VERSIONS} ; do \ + PY3AB=`echo $$p | sed -e 's/python3\./3/' ` ; \ + PY3VER=`echo $$p | sed -e 's/python//' `; \ + dh_auto_configure --builddirectory=debian/build-$$p -- \ + ${CMAKE_COMMON_FLAGS} \ + -DPYTHON_MIN_VERSION=$$PY3VER \ + -DPYTHON_EXECUTABLE=$$p \ + -DBoost_PYTHON_LIBRARY_RELEASE=$(LIBDIR)/libboost_python$${PY3AB}.so ; \ + done override_dh_auto_build: dh_auto_build --builddirectory=debian/build-py2 - dh_auto_build --builddirectory=debian/build-py3 + for p in ${PY3VERSIONS} ; do \ + dh_auto_build --builddirectory=debian/build-$$p ; done override_dh_auto_install: dh_auto_install --builddirectory=debian/build-py2 - dh_auto_install --builddirectory=debian/build-py3 + for p in ${PY3VERSIONS} ; do \ + dh_auto_install --builddirectory=debian/build-$$p ; \ + done # Drop the suffixes on scripts ... mv $(DESTDIR)/usr/bin/ecflow_logserver.sh $(DESTDIR)/usr/bin/ecflow_logserver mv $(DESTDIR)/usr/bin/ecflow_start.sh $(DESTDIR)/usr/bin/ecflow_start diff -Nru ecflow-4.9.0/debian/source/options ecflow-4.11.1/debian/source/options --- ecflow-4.9.0/debian/source/options 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/source/options 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -compression = "xz" diff -Nru ecflow-4.9.0/debian/watch ecflow-4.11.1/debian/watch --- ecflow-4.9.0/debian/watch 2018-05-21 06:31:42.000000000 +0000 +++ ecflow-4.11.1/debian/watch 2018-10-23 11:41:24.000000000 +0000 @@ -1,3 +1,3 @@ version=3 # https://software.ecmwf.int/wiki/display/ECFLOW/Releases ecFlow\-(.+)\-Source\.(?:zip|tgz|tbz|txz|(?:tar\.(?:gz|bz2|xz)))\?api=v(.+) -opts="dversionmangle=s/(\d+)$/$1-Source/" https://software.ecmwf.int/wiki/display/ECFLOW/Releases /wiki/download/attachments/8650755/ecFlow\-(.+)\.tar.gz\?api=v2 +opts="dversionmangle=s/(\d+)$/$1-Source/" https://confluence.ecmwf.int/display/ECFLOW/Releases /download/attachments/8650755/ecFlow\-(.+)\.tar.gz\?api=v2 diff -Nru ecflow-4.9.0/project_summary.cmake ecflow-4.11.1/project_summary.cmake --- ecflow-4.9.0/project_summary.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/project_summary.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -1,26 +1,31 @@ message( STATUS "------------------------------------------------------" ) if(Boost_FOUND) - message( STATUS " Boost include : [${Boost_INCLUDE_DIRS}]" ) - message( STATUS " libs : [${Boost_SYSTEM_LIBRARY}]" ) - message( STATUS " libs : [${Boost_SERIALIZATION_LIBRARY}]" ) - message( STATUS " libs : [${Boost_THREAD_LIBRARY}]" ) - message( STATUS " libs : [${Boost_FILESYSTEM_LIBRARY}]" ) - message( STATUS " libs : [${Boost_PROGRAM_OPTIONS_LIBRARY}]" ) - message( STATUS " libs : [${Boost_DATE_TIME_LIBRARY}]" ) - message( STATUS " libs : [${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}]" ) - message( STATUS " libs : [${Boost_TEST_EXEC_MONITOR_LIBRARY}]" ) - message( STATUS " libs : [${Boost_PYTHON_LIBRARY}]" ) -endif() - -if( PYTHONINTERP_FOUND ) - message( STATUS "Python exec : ${PYTHON_EXECUTABLE}" ) - message( STATUS " include : ${PYTHON_INCLUDE_DIRS}" ) - message( STATUS " libs : ${PYTHON_LIBRARIES}" ) + message( STATUS " Boost_MAJOR_VERSION : [${Boost_MAJOR_VERSION}]" ) + message( STATUS " Boost_MINOR_VERSION : [${Boost_MINOR_VERSION}]" ) + message( STATUS " Boost_SUBMINOR_VERSION : [${Boost_SUBMINOR_VERSION}]" ) + message( STATUS " Boost_INCLUDE_DIRS : [${Boost_INCLUDE_DIRS}]" ) + message( STATUS " Boost_LIBRARY_DIR_RELEASE : [${Boost_LIBRARY_DIR_RELEASE}]" ) + message( STATUS " Boost_SYSTEM_LIBRARY : [${Boost_SYSTEM_LIBRARY}]" ) + message( STATUS " Boost_THREAD_LIBRARY : [${Boost_THREAD_LIBRARY}]" ) + message( STATUS " Boost_FILESYSTEM_LIBRARY : [${Boost_FILESYSTEM_LIBRARY}]" ) + message( STATUS " Boost_PROGRAM_OPTIONS_LIBRARY : [${Boost_PROGRAM_OPTIONS_LIBRARY}]" ) + message( STATUS " Boost_DATE_TIME_LIBRARY : [${Boost_DATE_TIME_LIBRARY}]" ) + message( STATUS " Boost_UNIT_TEST_FRAMEWORK_LIBRARY : [${Boost_UNIT_TEST_FRAMEWORK_LIBRARY}]" ) + message( STATUS " Boost_TEST_EXEC_MONITOR_LIBRARY : [${Boost_TEST_EXEC_MONITOR_LIBRARY}]" ) + message( STATUS " Boost_REGEX_LIBRARY : [${Boost_REGEX_LIBRARY}]" ) + message( STATUS " Boost_PYTHON_LIBRARY : [${Boost_PYTHON_LIBRARY}]" ) + message( STATUS " Boost_PYTHON_LIBRARY_RELEASE : [${Boost_PYTHON_LIBRARY_RELEASE}]" ) + message( STATUS " Boost_PYTHON3_LIBRARY : [${Boost_PYTHON3_LIBRARY}]" ) + message( STATUS " Boost_PYTHON3_LIBRARY_RELEASE : [${Boost_PYTHON3_LIBRARY_RELEASE}]" ) + message( STATUS " Boost_PYTHON27_LIBRARY : [${Boost_PYTHON27_LIBRARY}]" ) + message( STATUS " Boost_PYTHON27_LIBRARY_RELEASE : [${Boost_PYTHON27_LIBRARY_RELEASE}]" ) + message( STATUS " Boost_PYTHON36_LIBRARY : [${Boost_PYTHON36_LIBRARY}]" ) + message( STATUS " Boost_PYTHON36_LIBRARY_RELEASE : [${Boost_PYTHON36_LIBRARY_RELEASE}]" ) endif() if (ENABLE_SSL) ecbuild_info("OpenSSL VERSION ${OPENSSL_VERSION}") ecbuild_info(" LIBS ${OPENSSL_LIBRARIES}") ecbuild_info(" INCLUDES ${OPENSSL_INCLUDE_DIR}") -endif() \ No newline at end of file +endif() diff -Nru ecflow-4.9.0/Pyext/CMakeLists.txt ecflow-4.11.1/Pyext/CMakeLists.txt --- ecflow-4.9.0/Pyext/CMakeLists.txt 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -52,9 +52,10 @@ # This ensures that for debug config, we only link with debug boost libs, for other configs, we link with optimised boost libs -if ( PYTHONLIBS_FOUND ) - target_link_libraries(ecflow debug ${Boost_PYTHON_LIBRARY_DEBUG} ) - target_link_libraries(ecflow optimized ${Boost_PYTHON_LIBRARY_RELEASE} ) +if ( PYTHON_VERSION_MAJOR EQUAL 3) + target_link_libraries(ecflow ${Boost_PYTHON3_LIBRARY_RELEASE} ) +else() + target_link_libraries(ecflow ${Boost_PYTHON_LIBRARY_RELEASE} ) endif() # diff -Nru ecflow-4.9.0/Pyext/ecflow/__init__.py ecflow-4.11.1/Pyext/ecflow/__init__.py --- ecflow-4.9.0/Pyext/ecflow/__init__.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/ecflow/__init__.py 2018-10-23 11:41:34.000000000 +0000 @@ -15,6 +15,6 @@ The ecFlow python module """ -__version__ = '4.9.0' +__version__ = '4.11.1' # http://stackoverflow.com/questions/13040646/how-do-i-create-documentation-with-pydoc diff -Nru ecflow-4.9.0/Pyext/jamfile.jam ecflow-4.11.1/Pyext/jamfile.jam --- ecflow-4.9.0/Pyext/jamfile.jam 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/jamfile.jam 2018-10-23 11:41:34.000000000 +0000 @@ -35,12 +35,12 @@ # 3/ Need to manually edit $BOOST_ROOT/project-config.jam, make sure file '$BOOST_ROOT/project-config.jam' has: # # using python -# : 3.5 -# : /usr/local/apps/python3/3.5.1-01/bin/python3 # ***** If this is left as python3, includes get messed up, have mix of python2 & 3 -# : /usr/local/apps/python3/3.5.1-01/include/python3.5m # include directory +# : 3.6 +# : /usr/local/apps/python3/3.6.1-01/bin/python3 # ***** If this is left as python3, includes get messed up, have mix of python2 & 3 +# : /usr/local/apps/python3/3.6.1-01/include/python3.6m # include directory # ; # ... -# option.set includedir : /usr/local/apps/python3/3.5.1-01/include/python3.5m ; # ***MAKE*** sure this is set +# option.set includedir : /usr/local/apps/python3/3.6.1-01/include/python3.6m ; # ***MAKE*** sure this is set # # ***** cmd/prefix must be path to python3, otherwise compilation include files has a mixture of # python 2.7 and 3.5, YUK, took ages to debug diff -Nru ecflow-4.9.0/Pyext/samples/api/ecf.py ecflow-4.11.1/Pyext/samples/api/ecf.py --- ecflow-4.9.0/Pyext/samples/api/ecf.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/samples/api/ecf.py 2018-10-23 11:41:34.000000000 +0000 @@ -33,8 +33,8 @@ global DEFS DEFS = ecflow.Defs() - -DEBUG = 0 +# DEBUG = True +DEBUG = False DECORATE = "ALL" # DECORATE = "ONLY_TRIGGER" # DECORATE = "NO_TRIGGER" @@ -46,6 +46,7 @@ USE_TRIGGER = True USE_LIMIT = True CPUTIME = True +USE_VERIFY = True class DefError(Exception): @@ -88,7 +89,7 @@ deployed.append(pathname) if extn in pathname: # surround .ecf with head, tail: script = "%include \n" + script + "\n%include " - with open(pathname, "w") as destination: # overwrite! + with open(pathname, 'w') as destination: # overwrite! print(script, file=destination) print("#MSG: created", pathname) # deploy("echo acq %TASK%", files + acq + extn) # create wrapper @@ -145,11 +146,11 @@ ecf_home = HOME + "/ecflow_server/include/" if not os.path.exist(head): - with open(ecf_home + head) as fip: + with open(ecf_home + head, 'w') as fip: write(head_h, file=fip) if not os.path.exist(tail): - with open(ecf_home + tail) as fip: + with open(ecf_home + tail, 'w') as fip: write(tail_h, file=fip) @@ -167,7 +168,7 @@ CWN.__CDP = active if active and CWN.is_empty(): return - print("#DBG: CWN reset") + if DEBUG: print("#DBG: CWN reset") CWN.__CWN = [] @classmethod @@ -179,7 +180,7 @@ if CWN.is_empty(): return # if 1: raise - print("#BDG: pop") + if DEBUG: print("#BDG: pop") CWN.__CWN.pop() @classmethod @@ -189,9 +190,13 @@ return CWN.__CWN[-1] def __init__(self, item=None): + def name(item): + if DEBUG: if type(item) in (Event, Meter, Label): print("#BDG:", type(item), "%s" % item.real) + elif type(item) in (Clock, ): + print("#BDG:", type(item), "%s" % item.real) else: print("#BDG:", type(item), item.real.name()) @@ -215,10 +220,8 @@ CWN.__CWN.append(item) elif item is None: pass - # print("#WAR: no attachment item is None", type(item)) elif item.real == None: pass - # print("#WAR: no attachment", item.name(), type(item)) elif CWN.is_empty(): raise DefError("#ERR: no node!") else: @@ -309,22 +312,24 @@ """ extern may be collected and added as soon as Defs is created """ def __init__(self, path): - if type(path) in (tuple, list, str): - pass - else: - raise DefError + # if type(path) not in (tuple, list, str): raise DefError(type(path)) - if type(path) == tuple or type(path) == list: + if type(path) in (tuple, list): for ppp in path: Extern(ppp) - elif type(path) == str: + elif type(path) in (str, unicode): if DEBUG: print("#MSG: extern", path) + if ".Extern" in path: raise Exception if ":" in path: - Extern(path.split(":")[0]) - elif DEFS: - DEFS.add_extern(path) + Extern(path.split(":")[0]) # define parent so user does not have to + + if path == "": pass + elif DEFS is not None: + if ".Extern" in path: raise DefError(type(path)) + DEFS.add_extern(str(path)) + else: raise DefError elif path is None: pass @@ -452,13 +457,39 @@ class Event(Attribute): """ wrap around event""" - def __init__(self, name): - self.load = ecflow.Event(name) + def __init__(self, name="", num=0): + if type(name) == str: + if " " in name: num, name = name.split(" ") + try: + if int(num) > 100000: num = "" + except: pass + self.load = None + if type(name) is int and num == 0: num = name; name = "" + + if type(name) is int and type(num) == str: + self.load = ecflow.Event(name, num) + elif type(name) is str and type(num) == int: + self.load = ecflow.Event(num, name) + elif type(name) is str and type(num) == str: + if num == "": + self.load = ecflow.Event(name) + elif name == "": + self.load = ecflow.Event(num) + else: self.load = ecflow.Event(int(num), name); # print("#str str") + else: self.load = ecflow.Event(name) CWN(self) def add_to(self, node): """ add_event""" - node.load.add_event(self.load) + for test in node.load.events: + if test.name() == self.load.name(): + if (test.number() == self.load.number() and + test.name() == self.load.name()): + # print("dupl"); + return + #else: raise Exception(test.number(), self.load.number(), + # test.name(), self.load.name()) + node.load.add_event(self.load.number(), self.load.name()) return node @@ -467,7 +498,7 @@ silently ignore if USE_LIMIT is False, (in debug mode) """ - def __init__(self, fullpath): + def __init__(self, fullpath, tokens=1): self.load = None if USE_LIMIT: try: @@ -477,15 +508,16 @@ path = "" if name is None: raise DefError - if '-prod' in name: - name = name.split('-')[0] # vdiss-prod + # if '-prod' in name: name = name.split('-')[0] # vdiss-prod if '-' in name: name = name.replace('-', '_') if "None" in path or "None" in name: raise DefError(path, name, fullpath) - self.load = ecflow.InLimit(name, path) + if " " in name: name, tokens = name.split(" ") + self.load = ecflow.InLimit(name, path, int(tokens)) self.path_ = path self.name_ = name + self.tokens_ = tokens CWN(self) def add_to(self, node): @@ -494,6 +526,13 @@ return if self.load is None: raise DefError + # if type(node.real) in (ecflow.Task, ecflow.Family): + for dup in node.real.inlimits: + # print(dup.name, self.load.name()) + if (dup.name() == self.load.name() and + dup.path_to_node() == self.load.path_to_node()): + # print("lim dup"); + return None node.load.add_inlimit(self.load) return node @@ -570,7 +609,7 @@ continue if ploc.isdigit(): continue - Extern(ploc.replace("==complete", "").replace(")", "")) + # Extern(ploc.replace("==complete", "").replace(")", "")) except AttributeError: pass except ImportError: @@ -678,12 +717,12 @@ if 0 and "/prod/wave" in "%s" % self.expr: raise DefError(self.expr, node.fullname()) # help DEBUG - if "trigger" in self.expr: - raise DefError + if "trigger " in self.expr: + raise DefError(self.expr) if "%s" in self.expr: - raise DefError + raise DefError(self.expr) if "%d" in self.expr: - raise DefError + raise DefError(self.expr) # if "make/nemoconst" in self.expr: raise DefError(self.expr) if node.load.get_trigger() is None: node.load.add_trigger(self.expr) @@ -736,16 +775,56 @@ """ wrapper to add clock """ def __init__(self, arg="24:00", hybrid=0): - if type(arg) == str: + self.load = None + if arg == "real": + self.load = ecflow.Clock(False) + elif arg == "hybrid": + self.load = ecflow.Clock(True) + elif type(arg) == str: hybrid = "hybrid" in arg + if "hybrid " in arg: arg = nat(arg, "hybrid"); hybrid=1 + elif "real " in arg: arg = nat(arg, "real"); hybrid=0 + if " -s" in arg: arg = arg.replace(" -s", ""); sync = True + else: sync = False hhh, mmm, sss = [0, 0, 0] - try: - hhh, mmm, sss = arg.split(':') - self.load = ecflow.Clock(hhh, mmm, sss, hybrid) - except ValueError: - self.load = ecflow.Clock(hybrid) - else: - self.load = ecflow.Clock(arg) + if 1: # try: + if "." in arg and " " in arg: + ymd, hhh = arg.split(" ") + ddd, mmm, yyy = ymd.split('.') + self.load = ecflow.Clock(int(ddd), int(mmm), int(yyy), + hybrid) + rel = "+" in hhh + if hhh != "": + self.load.set_gain_in_seconds(int(hhh), rel) + elif "+" in arg: + rel = "+" in arg + self.load = ecflow.Clock(hybrid) + self.load.set_gain_in_seconds(int(arg), rel) + elif arg is None and hybrid is False: pass + elif arg == "None" and hybrid is False: pass + else: + rel = "+" in arg + from datetime import date + # print("#Clock+", arg, type(arg), hybrid, self.load, ) + if 0: + ymd = "%s" % date.today() + yyy, mmm, ddd = ymd.split("-") + self.load = ecflow.Clock( + int(ddd), int(mmm), int(yyy), hybrid) + else: self.load = ecflow.Clock(hybrid) + if arg != "": + self.load.set_gain_in_seconds(int(arg), rel) + #except ValueError: + # print("#Value Error", arg, hybrid) + # self.load = ecflow.Clock(hybrid) + if self.load and sync: self.load.set_virtual(True) + else: + # print("#else", arg, hybrid) + self.load = ecflow.Clock(arg, hybrid) + + # print("#Clock", arg, hybrid, self.load) + if arg is None and hybrid is False: self.load = None + # if 1: raise Exception CWN(self) def add_to(self, node): @@ -753,7 +832,9 @@ print("#WAR: clock can only be attached to suite node,\n", "#WAR: clock is ignored") return - node.load.add_clock(self.load) + if node.real.get_clock() is None and self.load is not None: + # print("#clock", node.real.get_clock(), self.load) + node.load.add_clock(self.load) return node @@ -790,6 +871,28 @@ return node +class Verify(Attribute): + """ wrapper to add time """ + + def __init__(self, arg): + if "verify " in arg: arg = nat(arg, "verify") + y, z = str(arg).split(":") + kinds = { + "aborted": ecflow.State.aborted, + "complete": ecflow.State.complete, + "active": ecflow.State.active, + "submitted": ecflow.State.submitted, + "unknown": ecflow.State.unknown, + "queued": ecflow.State.queued, + } + self.load = ecflow.Verify(kinds[y], int(z)) + + def add_to(self, node): + if USE_VERIFY and self.load is not None: + node.load.add_verify(self.load) + return node + + class Time(Attribute): """ wrapper to add time """ @@ -818,24 +921,31 @@ import argparse self.load = ecflow.Cron() - if not ("-w" in bes or "-m" in bes or "-d" in bes): + if not ("-w" in bes or "-m" in bes or "-d" in bes or " -s" in bes): self.load.set_time_series(bes) return parser = argparse.ArgumentParser() - parser.add_argument("-w", nargs='?', default=0, help="weekdays") - parser.add_argument("-d", nargs='?', default=0, help="days") - parser.add_argument("-m", nargs='?', default=0, help="months") - parser.add_argument("arg", type=str, help="begin end step") + parser.add_argument("-w", nargs=1, default=None, help="weekdays") + parser.add_argument("-m", nargs=1, default=None, help="months") + parser.add_argument("-d", nargs=1, default=None, help="days") + parser.add_argument("-s", action="store_true", help="sync") + parser.add_argument("begin", nargs='+', help="begin") + if " -s" in bes: + bes = bes.replace(" -s", "") + sync = True + else: sync = False parsed = parser.parse_args(bes.split()) if parsed.w: - self.load.set_week_days([int(x) for x in parsed.w.split(',')]) + self.load.set_week_days([ + int(x) for x in str(parsed.w[0]).split(',')]) if parsed.d: - self.load.set_week_days([int(x) for x in parsed.d.split(',')]) + self.load.set_days_of_month([ + int(x) for x in parsed.d[0].split(',')]) if parsed.m: - self.load.set_months([int(x) for x in parsed.m.split(',')]) - self.load.set_time_series(parsed.arg) + self.load.set_months([int(x) for x in parsed.m[0].split(',')]) + self.load.set_time_series(' '.join(parsed.begin)) def add_to(self, node): if USE_TIME and self.load is not None: @@ -867,7 +977,6 @@ yyy = 0 node.load.add_date(int(ddd), int(mmm), int(yyy)) - # node.add_date(self.load) return node @@ -905,6 +1014,7 @@ def __init__(self, kind): if type(kind) == str: kinds = {"suspended": ecflow.DState.suspended, + # "halted": ecflow.DState.halted, "shutdown": ecflow.DState.shutdown, "aborted": ecflow.DState.aborted, "complete": ecflow.DState.complete, "active": ecflow.DState.active, @@ -915,7 +1025,7 @@ elif type(kind) in (ecflow.DState, ): self.load = kind else: - raise DefError(type(kind)) + raise DefError(type(kind), kind) def add_to(self, node): if type(node) in (Suite, Family, Task): @@ -939,7 +1049,7 @@ def __init__(self, name=None, size=1, inlimit=0): self.name = name - self.size = size + self.size = int(size) self.addi = inlimit def add_to(self, node): @@ -1125,7 +1235,7 @@ return node def add(self, what): - print("add???", "%s" % self.load) + print("#ERR: add???", "%s" % self.load) if type(what) in (Suite, Family, Task): raise DefError(what.fullname()) else: @@ -1145,6 +1255,25 @@ def __init__(self, name="YMD", start=20120101, end=21010101, step=1, kind="date"): + # print("repeat", name, "#", start, "#", end, "#", step, "#", kind) + if len(name) > 4: + if (name[:4] == "enum" or + name[:4] == "date" or + "string " in name or + name[:3] == "int" or + "day " in name): + pars = name.split(" ") + kind = pars[0] + name = pars[1] + if "enum" in kind or "string" in kind: + start = [i.strip("'").strip('"') for i in pars[2:]] + elif "int" in kind or "date" in kind: + start = pars[2] + if len(pars) > 3: + end = pars[3] + if len(pars) > 4: + step = pars[4] + if kind in ("date", ): self.load = ecflow.RepeatDate( name, int(start), int(end), int(step)) @@ -1152,8 +1281,10 @@ self.load = ecflow.RepeatInteger( name, int(start), int(end), int(step)) elif kind == "string": + if len(start) == 0: raise Exception self.load = ecflow.RepeatString(name, start) elif "enum" in kind: + if len(start) == 0: raise Exception self.load = ecflow.RepeatEnumerated(name, start) elif kind == "day": self.load = ecflow.RepeatDay(step) @@ -1166,6 +1297,40 @@ return node +class Zombie(Attribute): + """ + | ZombieAttr(ZombieType,ChildCmdTypes, ZombieUserActionType, lifetime) + | ZombieType : Must be one of ZombieType.ecf, ZombieType.path, ZombieType.user + | ChildCmdType : A list(ChildCmdType) of Child commands. Can be left empty in + | which case the action affect all child commands + | ZombieUserActionType : One of [ fob, fail, block, remove, adopt ] + | int lifetime: Defines the life time in seconds of the zombie in the server. + | On expiration, zombie is removed automatically +""" + + def __init__(self, arg="ecf:remove:3600"): + if "zombie " in arg: arg = nat(arg, "zombie") + typ, act, kid, num = arg.split(":") + types = ecflow.ZombieType.names # ecf user path + kids = ecflow.ChildCmdType.names # complete label init abort event wait + acts = ecflow.ZombieUserActionType.names # adopt remove kill fob fail block + when = [] + if kid == "": + pass + elif "," in kid: + for k in kid.split(","): + when.append(kids[k]) + else: when.append(kids[kid]) + + self.load = ecflow.ZombieAttr(types[typ], when, acts[act], + int(num)) + + def add_to(self, node): + """add_zombie""" + node.load.add_zombie(self.load) + return node + + def If(test=True, then=None, otow=None, *args): """ enable Task("t1").add(If(test=(1==1), then=Edit(ONE=1), @@ -1180,11 +1345,10 @@ """ if len(args) > 1: try: - print(then.name()) - print(otow.name()) + print("#ERR:", then.name()) + print("#ERR:", otow.name()) except Exception as exc: print("#ERR:", exc.args) - # print(args[0].name()) raise DefError("test", test, "\nthen\n", then, "\nelse", @@ -1273,7 +1437,8 @@ """ add defstatus attribute""" status = kind if type(kind) == str: - kinds = {"suspended": ecflow.DState.suspended, + kinds = { # "suspended": ecflow.DState.suspended, + "halted": ecflow.DState.halted, "shutdown": ecflow.DState.shutdown, "aborted": ecflow.DState.aborted, "complete": ecflow.DState.complete, "active": ecflow.DState.active, @@ -1311,6 +1476,9 @@ raise DefError(item) elif type(item) == int: pass # raise DefError(item) + elif type(item) == dict: + if item == {}: pass + else: raise DefError(item) else: item.add_to(self) @@ -1449,11 +1617,11 @@ # steps follow upd = { 'variables': dict(), - 'events': [item.name() for item in node.events], + 'events': [(item.number(), item.name()) for item in node.events], 'meters': dict(), 'labels': dict(), 'limits': dict(), - 'inlimits': [item.name() for item in node.inlimits], + 'inlimits': dict(), # [item.name() for item in node.inlimits], } for item in node.variables: upd['variables'][item.name()] = item.value() @@ -1462,7 +1630,12 @@ for item in node.labels: upd['labels'][item.name()] = item.value() for item in node.limits: - upd['limits'][item.name()] = item.value() + upd['limits'][item.name()] = item.limit() + for item in node.inlimits: + arg = item.name() + if item.path_to_node() != "": arg = item.path_to_node() + ":" + item.name() + if item.tokens() > 1: arg += " %d" % item.tokens() + upd['inlimits'][item.name()] = arg # ('variables', 'events', 'meters', 'labels', 'limits', 'inlimits'): for key in upd.keys(): if len(upd[key]) == 0: @@ -1472,6 +1645,8 @@ upd['autocancel'] = True if '%s' % node.get_defstatus() != 'queued': upd['defstatus'] = '%s' % node.get_defstatus() + if node.get_late() != None: + upd['late'] = nat('%s' % node.get_late(), "late") for item in node.nodes: upd.update(to_pyflow(item, container[node.name()])) @@ -1479,32 +1654,48 @@ return container +def nat(name, key): + res = '%s' % str(name) + return res.replace('%s ' % key, '') + + def to_dict(node, container=None): kids = dict() if type(node) is ecflow.Defs: + res = {':suites': [], } # ':externs': [], } + for item in node.suites: + res[':suites'].append(to_dict(item)) + if 1: + for item in node.externs: + if ':externs' not in res.keys(): res[':externs'] = [] + # print(item) + res[':externs'].append("%s" % item) + return res + + if type(node) is ecflow.Alias: + print("#WAR: aliases are ignored") return dict() for item in node.nodes: kids[item.name()] = to_dict(item) - def nat(name, key): - res = '%s' % name - return res.replace('%s ' % key, '') - - temp = {'edits': dict(), - # 'events': ['%s' % item.name_or_number() for item in node.events], - 'events': ['%s' % item.name() for item in node.events], - 'meters': [{'%s' % item.name(): {'min': item.min(), - 'max': item.max(), - 'thr': item.color_change(), - # aka threshold(), - }} for item in node.meters], - 'labels': dict(), - 'limits': dict(), - 'inlimits': ['%s' % item.name() for item in node.inlimits], - - 'zombies': [nat(item, 'zombie') for item in node.zombies], - + temp = {'edits': {item.name(): '%s' % item.value() + for item in node.variables}, + 'events': ["%d " % item.number() + '%s' % item.name() + for item in node.events], + # 'externs': [], + 'meters': [ + {'%s' % item.name(): { + 'min': item.min(), + 'max': item.max(), # aka threshold(), + 'thr': item.color_change(), }} + for item in node.meters], + 'labels': {item.name(): '%s' % item.value() + for item in node.labels}, + 'limits': {item.name(): item.limit() for item in node.limits}, + 'inlimits': [], + 'verifies': ["%s" % item for item in node.verifies], + 'zombies': ["%s" % item for item in node.zombies], 'dates': [nat(item, 'date') for item in node.dates], 'days': [nat(item, 'day') for item in node.days], 'times': [nat(item, 'time') for item in node.times], @@ -1513,27 +1704,33 @@ 'trigger': '%s' % node.get_trigger(), 'complete': '%s' % node.get_complete(), 'children': kids, + # 'late': "%s" % node.get_late(), + # 'clock': '%s' % node.get_clock(), } - for item in node.variables: - temp['edits'][item.name()] = '%s' % item.value() - for item in node.labels: - temp['labels'][item.name()] = '%s' % item.value() - for item in node.limits: - temp['limits'][item.name()] = item.value() + + for item in node.inlimits: + arg = item.name() + if item.path_to_node() != "": + arg = item.path_to_node() + ":" + item.name() + if item.tokens() > 1: arg += " %d" % item.tokens() + temp['inlimits'].append(arg) out = {':name': '%s' % node.name(), ':kind': '%s' % get_kind(node), ':status': '%s' % node.get_state(), } defstatus = '%s' % node.get_defstatus() if defstatus != 'queued': - out[':defstatus'] = defstatus + out[':defstatus'] = "%s" % defstatus + if get_kind(node) == "suite": + out[':clock'] = nat("%s" % node.get_clock(), "clock") + if node.get_late() is not None: + out[':late'] = nat("%s" % node.get_late(), "late") + rep = node.get_repeat() + if not rep.empty(): + out[':repeat'] = nat("%s" % node.get_repeat(), "repeat") + # for key in sorted(temp.keys()): for key in temp.keys(): - if temp[key] is None: - continue - if temp[key] == 'None': - continue # WARNING ??? - if len(temp[key]) == 0: - continue + if temp[key] == 'None': continue # WARNING ??? out[':' + key] = temp[key] return out @@ -1544,7 +1741,6 @@ if type(item) in (list, tuple): pass elif type(item) != dict: - # print(type(item)) item = to_dict(item, pyflow) return json.dumps(item, # default= lambda o: @@ -1553,86 +1749,127 @@ sort_keys=True, indent=2) +def json_to_defs(treedict, parent=None): + if treedict is None: return + res = [] + if ":externs" in treedict.keys(): + for item in treedict[":externs"]: + res.append("extern %s" % item) + # Extern(nat(item, "extern"))) + if ":suites" in treedict.keys(): + for suite in treedict[":suites"]: + res.append(from_json(suite)) + for key in treedict.keys(): + if key not in (":externs", ":suites"): + raise DefError("please use from_json", key) + return res + def from_json(tree): out = [] res = None - ITEMS = {'suite': Suite, - 'family': Family, - 'task': Task, - ':state': State, - ':repeat': Repeat, + if type(tree) in (tuple, list): + # if tree is None: return # IGN + if tree == []: + return # IGN + # if type(tree) in (tuple, list): + if len(tree) == 2: + # print(type(tree[0]), type(tree[1]), tree[0], tree[1]) + if type(tree[0]) == dict: + return from_json(tree[0]) + return {str(tree[0]): from_json(tree[1])} + # return Family(str(tree[0])).add(from_json(tree[1])) + elif len(tree) == 1: # return [str(tree[0])] + return str(tree[0]) + raise Exception("#wwwwww", tree, type(tree)) - ':extern': Extern, - ':event': Event, - ':meter': Meter, - ':label': Label, - ':edit': Edit, + elif type(tree) in (str, unicode): + print("#IGN", tree) + return - ':inlimit': Inlimit, - ':limit': Limit, + elif type(tree) is not dict: + raise Exception("#wwwwww", tree, type(tree)) - ':trigger': Trigger, - ':complete': Complete, - ':defstatus': Defstatus, - ':kids': Limit, + for k in sorted(tree.keys()): + if type(k) == unicode: sk = str(k) + else: sk = k + if type(tree[k]) == unicode: + tree[k] = str(tree[k]) + # print("kkk", k, sk) # , tree[k], type(tree[k]), ) + # if k in ITEMS.keys(): print("keys", k, ITEMS[k]) + if sk in (':name', ): + res = ITEMS[tree[':kind']](str(tree[':name'])) - ':time': Time, - ':cron': Cron, - ':date': Date, - ':day': Day, - ':today': Today, + elif sk in (':status', ':kind'): + continue - ':late': Late, - ':clock': Clock, - ':autocancel': Autocancel, - } - for k, v in tree.items(): - print(k, v) - if k in (':name', ): - res = ITEMS[tree[':kind']](tree[':name']) - elif k in (':status', ':kind'): - pass - elif k in (':children', ): - out.append(from_json(v)) - elif k in (':defstatus', ':trigger', ':complete'): - out.append(ITEMS[k](v)) - elif k in (':meters', ): - for item in v: + elif sk in (':children', ":kids", ): + for kid in sorted(tree[k].keys()): + out.append(from_json(tree[k][kid])) + + elif sk in (":suites", ): + for item in tree[k]: + out.append(from_json(item)) + + elif sk in (':defstatus', ':trigger', ':complete', ':clock', + ':late', ':externs', ':repeat',): + out.append(ITEMS[k](tree[k])) + + elif sk in (':meters', ): + for item in tree[k]: name = item.keys()[0] - out.append(ITEMS[k](name, + out.append(ITEMS[k](str(name), int(item[name]['min']), int(item[name]['max']), - int(item[name]['thr']) - )) - elif ':' in k: - if type(v) == dict: - for key in v.keys(): - out.append(ITEMS[k](key, v[key])) - else: - for item in v: - if item == '': - continue - print(k, item) - out.append(ITEMS[k](item)) - elif 1: - # print('##########U', k, v) - out.append(from_json(v)) - elif k in (':edits', ':labels', ':limits'): - for item in v: - print(k, item) - out.append(ITEMS[k](item[0], item[1])) - elif k in (':events', ':inlimits', ':limits'): - for item in v: - out.append(ITEMS[k](item)) - elif k[0] == ':' and k[-1] == 's': - print('::::::::', k, v) - else: - out.append(from_json(v)) - # print('#UUUUUUU', k, v) - if res is None: - return {} + int(item[name]['thr']))) + elif sk in (':edits', ':labels', ':limits', ): + for item in sorted(tree[k].keys()): + # print("kkkkk", k, item, v, tree[k]) + out.append(ITEMS[k](str(item), str(tree[k][item]))) + elif sk in (':events', ':repeat', ':inlimits', ':crons', + ':verifies', ':dates', + ':times', ':days', ':zombies', ":todays", ): + for item in tree[k]: + # print("kkkkk", k, "#", item, "#", tree[k]) + out.append(ITEMS[k](str(item))) + # elif ':' in k: + # print(":key",k) + # v = tree[k] + # if type(v) == dict: + # for key in v.keys(): + # out.append(ITEMS[k](key, v[key])) + # else: + # for item in v: + # if item != '': + # out.append(ITEMS[k](item)) + elif sk in ITEMS.keys(): + raise Exception + + else: + if (type(tree[k]) == dict and + len(tree[k]) == 1 and + ['children', ] == tree[k].keys()): + kids = tree[k]['children'] + if type(kids) not in (list, tuple): + raise Exception(type(kids)) + for kid in kids: + if type(kid) in (str, unicode): + out.append((str(k), str(kid))) + elif type(kid) in (dict, ): + for elt in kid.keys(): + out.append((str(k), str(elt))) + anot = from_json(kid) + if anot is not None: out.append(anot) + + return out + elif 1: raise Exception(k, tree[k], type(tree[k]), + len(tree[k]), + tree[k].keys()) + else: out.append(from_json(tree[k])) + + if res is None: + return out return res.add(out) @@ -1642,6 +1879,7 @@ def __enter__(self): CWN(self) return self + # def __exit__(self, exc_type, exc_val, exc_tb): pass # return self def events(self): @@ -1729,6 +1967,10 @@ def __get_attr__(self, attr): return getattr(self.load, attr) + @property + def real(self): + return self.load + def auto_add_externs(self, true): self.load.auto_add_externs(true) @@ -1736,8 +1978,13 @@ self.load.check() def add_extern(self, path): + if type(path) != str: raise DefError(type(path)) + if ".Extern" in path: raise DefError(type(path)) self.load.add_extern(path) # TODO list + def save_as_defs(self, fname): + self.load.save_as_defs(fname) + def suites(self): return [suite for suite in self.load.suites] @@ -1758,8 +2005,12 @@ """ add suite """ if type(item) == Suite: self.load.add_suite(item.load) - # elif type(item) == Extern: # obsolete, Extern uses global DEFS var - # for path in item.folk: self.load.add_extern(path) + elif type(item) == Extern: # back again + # path = nat(item, "extern") + path = "%s" % item + if type(path) != str: raise DefError(type(path), item) + if ".Extern" in path: raise DefError(type(path), path, item) + self.load.add_extern(path) elif type(item) == tuple: for one in item: self.add(one) @@ -1768,8 +2019,12 @@ self.add(one) elif item is None: pass + elif type(item) in(str, unicode): + if "extern " in item: + self.load.add_extern(nat(item, "extern")) + else: raise DefError("ERR:load add, what?", type(item), item) else: - raise DefError("ERR:load addwhat?", type(item)) + raise DefError("ERR:load add, what?", type(item), item) return self def suite(self, name): @@ -1779,6 +2034,11 @@ return suite +global DEFS +# DEFS = ecflow.Defs() +DEFS = Defs() + + class Client(object): """ wrapper around client """ @@ -1829,7 +2089,7 @@ return self def __exit__(self, exc_type, exc_val, exc_tb): - pass # return self + pass def __init__(self, name): self.load = ecflow.Suite(name) @@ -1871,7 +2131,7 @@ """ wrapper around family """ def __init__(self, name): - self.load = ecflow.Family(name) + self.load = ecflow.Family(str(name)) CWN(self) def family(self, name): @@ -1894,7 +2154,7 @@ def add_to(self, node): if DEBUG: - print("add: %s %s" % (node.name(), self.name())) + print("#BDG: add: %s %s" % (node.name(), self.name())) parent = self.load.get_parent() if parent: raise DefError("already attached...", @@ -1948,13 +2208,36 @@ def display(defs, fname=None): """ print defs""" if fname is not None: - fop = open(fname, "w") - print(defs, file=fop) + with open(fname, 'w') as fop: + print(defs, file=fop) class TestEcf(unittest.TestCase): """ a test case aka use-case """ + + def test_edge(self): + import json + s = '{"A": {"children": ["B", {"C": {"children": [{"D": {"children": ["E"]}}, "F"]}}]}}' + data = json.loads(s) + edges = from_json(data); + def rec_form(row, fop): + if len(row) == 0: return + elif len(row) == 1: row = row[0] + if len(row) == 2: + print(' {0} -> {1};'.format(*row), file=fop) + else: + for item in row: rec_form(item, fop) + with open('tree.dot', 'w') as fop: + print('\nstrict digraph tree {', file=fop) + print("#s\n", s, "\n", edges) + rec_form(edges, fop) + print('}', file=fop) + """ + python ecf.py; # generate tree.dot + cat tree.dot | dot -Tpng -otree.png && xdg-open tree.png + """ + def xtest_xxx(self): """ a test """ CWN.cdp(False) @@ -2010,7 +2293,7 @@ Inlimit("a_task:a_limit"), Meter("step", -1, 100), Label("info", "none"), - Event(1), + Event(num=1), Event("a"), Defstatus("complete")) @@ -2048,21 +2331,55 @@ os.system(cmd) def test_defs(self): + import json git = os.getenv("GIT_ECFLOW", "./") + if git[-1] != '/': git += '/' locs = [git + "ANode/parser/test/data/good_defs", git + "CSim/test/data/good_defs" ] + global USE_LATE + USE_LATE = True def process_dir(loc): for root, dirs, files in os.walk(loc): - # print(root, dirs, files) for file in files: if file.endswith(".def"): defs = ecflow.Defs(os.path.join(root, file)) - json = to_json(defs) - print(defs, json) - + for att in ecflow.AttrType.names: + defs.sort_attributes(att, True) + tree = to_json(to_dict(defs)) + print("#name", os.path.join(root, file)) + # print("#defs", defs, tree, type(tree)) + data = json.loads(tree) + # print("#data", data) + edges = json_to_defs(data) + # print("#edges", edges) + + DEFS = Defs().add(edges) + import suite_diff + for suite in defs.suites: + for alter in DEFS.real.suites: + if suite.name() == alter.name(): + suite_diff.REF = suite_diff.Content( + defs, suite.name(), suite) + suite_diff.RES = suite_diff.Content( + DEFS.real, suite.name(), alter) + suite_diff.walk(suite) + suite_diff.RES = suite_diff.Content( + defs, suite.name(), suite) + suite_diff.REF = suite_diff.Content( + DEFS.real, suite.name(), alter) + suite_diff.walk(alter) + break + for att in ecflow.AttrType.names: + DEFS.real.sort_attributes(att, True) + + comp = DEFS.real == defs + print("#back", comp) + if not comp: + print("# defs", defs) + print("# DEFS", DEFS) # , data) + for dir in dirs: - # print(dir); process_dir(os.path.join(root, dir)) for loc in locs: @@ -2074,8 +2391,8 @@ ) as s1: with Family("f1"): with Task("t1"): - Event("1") - Event(2) + Event(num=0, name="") + Event(num=1, name="1") Meter("step", -1, 120) Label("info", "msg") with Task("t2"): @@ -2083,13 +2400,13 @@ with Family("f2"): with Task("t3"): pass - print("#DBG: up*3") + if DEBUG: print("#DBG: up*3") with Family("f22"): Task("t01") - print("#DBG: up") + if DEBUG: print("#DBG: up") with Task("t11"): pass - print("#DBG: up*2") + if DEBUG: print("#DBG: up*2") print(s1) print(DEFS) CWN.cdp(False) @@ -2166,5 +2483,62 @@ return self._dot._repr_svg_() +ITEMS = {'suite': Suite, + 'family': Family, + 'task': Task, + + ':state': State, + ':repeat': Repeat, + + ':event': Event, + ':events': Event, + + ':externs': Extern, + ':extern': Extern, + + ':meter': Meter, + ':meters': Meter, + + ':label': Label, + ':labels': Label, + + ':edit': Edit, + ':edits': Edit, + + ':inlimit': Inlimit, + ':limit': Limit, + + ':inlimits': Inlimit, + ':limits': Limit, + + ':trigger': Trigger, + ':complete': Complete, + ':defstatus': Defstatus, + # ':kids': Limit, + + ':time': Time, + ':times': Time, + ':cron': Cron, + ':crons': Cron, + ':date': Date, + ':dates': Date, + ':day': Day, + ':days': Day, + ':today': Today, + ':todays': Today, + + ':zombies': Zombie, # ecflow.ZombieAttr, + ':zombie': Zombie, # ecflow.ZombieAttr, + + ':late': Late, + ':verifies': Verify, + ':clock': Clock, + ':autocancel': Autocancel, + + ':suites': Suite, # dict(), + ':suite': Suite, # dict(), + } + + if __name__ == '__main__': unittest.main() diff -Nru ecflow-4.9.0/Pyext/samples/overview.py ecflow-4.11.1/Pyext/samples/overview.py --- ecflow-4.9.0/Pyext/samples/overview.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/samples/overview.py 2018-10-23 11:41:34.000000000 +0000 @@ -1,8 +1,10 @@ #!/usr/bin/env python +# -*- coding= UTF-8 -*- """ tkinter example with ecflow """ - +from __future__ import with_statement, print_function import Tkinter as tki +from Tkinter import * import Queue import sys import time @@ -33,6 +35,24 @@ "unknwon": "grey"} running = [True] +ONCE = False + + +def show_check(one): + print(one) + sep = ',' + if sep not in one: return + exp = one.split(sep) + if len(exp) != 4: return + host, qid = exp[2], exp[3] + check = os.getenv("CHECK_TASK", "check_task.py") + global ONCE + if check is not None: + print(check + " -n %s -j %s" % (host, qid)) + elif ONCE: + print("#WAR: export CHECK_TASK=$HOME/bin/check_task.py; overview.py $ECF_HOST@$ECF_PORT,") + ONCE = True + class Label(object): """ a class to encapsulate what was a global variable""" @@ -52,12 +72,19 @@ def __init__(self, parent): tki.Frame.__init__(self, parent) + self.parent = parent self.__helpButton = self.__createHelp() self.__helpButton.grid(row=0, column=3) + self.__quitButton = tki.Button(self, text='Quit', font=BUTTON_FONT, + command=root.destroy) + self.__quitButton.grid(row=0, column=1) self.__updateButton = tki.Button(self, text='Update', font=BUTTON_FONT, command=parent.update) self.__updateButton.grid(row=0, column=2) + def quit(self, event=None): + self.parent.quit() + def __createHelp(self): mb = tki.Menubutton(self, font=BUTTON_FONT, relief=tki.RAISED, text='Help') @@ -73,31 +100,67 @@ return mb def __url(self, url=None): - if url is None: - return - os.system("firefox " + url) + if url is not None: + os.system("${BROWSER:=firefox} %s" % url) class TaskList(tki.Frame): """ a class to host all tasks""" - WIDTH = 40 - LINES = 80 + # WIDTH = w / 30 + # LINES = h / 10 def __init__(self, parent, kind): - tki.Frame.__init__(self, parent) + tki.Frame.__init__(self, parent, + # side="bottom", + # fill="both", + # expand=True, + # sticky="nsew", + ) + self.WIDTH = 80 + self.LINES = 80 self.__kind = kind self.__callback = None - self.__label = tki.Label( - self, font=BUTTON_FONT, background=COLORS[kind], text=kind) - self.__label.grid(row=0, column=0, sticky=tki.W) + #self.__label = tki.Label( + # self, font=BUTTON_FONT, background=COLORS[kind], text=kind) + #self.__label.grid(row=0, column=0, sticky=tki.W) + select = tki.Button(self, text=kind, + font=BUTTON_FONT, + background=COLORS[kind], + command=self.select) + select.grid(row=1, column=0, sticky=tki.W) + + selectall = tki.Button(self, text="select all", + font=BUTTON_FONT, + # background=COLORS[kind], + command=self.select_all) + selectall.grid(row=2, column=0, sticky=tki.W) + self.__scrolledList = ScrolledList( - self, width=self.WIDTH, height=self.LINES, + self, + # sticky="nsew", + width=self.WIDTH, + height=self.LINES, # selectmode=tki.EXTENDED, callback=self.__callback) - self.__scrolledList.grid(row=1, column=0) + self.__scrolledList.grid(row=3, column=0) - def insert(self, path): self.__scrolledList.append(path) + def select_all(self): + res = set() + item = self.__scrolledList.listbox.get(0, tki.END) + for one in item: + print(one) + + def select(self): + res = set() + selected = self.__scrolledList.listbox.curselection() + for i in selected: + item = self.__scrolledList.listbox.get(i) + res.add(item) + for one in res: + show_check(one) + + def insert(self, path): self.__scrolledList.append(path) - def clear(self): self.__scrolledList.clear() + def clear(self): self.__scrolledList.clear() class PaceKeeper(): @@ -124,31 +187,25 @@ class Client(object): """ a class to focus on client-ecFlow-server comm""" - def __init__(self, one="local-localhost@31415"): - try: - nick, hhh = one.split("-") - except: - hhh = one - nick = None - try: - host, port = hhh.split("@") - except: - host = "localhost" - port = 31415 - - if nick is None: - self.nick = "%s@%d" % (host, port) - print "# client creation", nick, host, port - self.nick = nick + def __init__(self, one="localhost@31415"): + # try: nick, hhh = one.split("-") + # except: hhh = one; nick = None + if "@" in one: + host, port = one.split("@") + # except: + else: host = "localhost"; port = 31415 + self.nick = one + # if nick is None: self.nick = "%s@%s" % (host, port) + print("# client creation", self.nick, host, port) self.client = ec.Client(host, port) def process(self, win): - Label.update() + # Label.update() self.client.sync_local() defs = self.client.get_defs() if defs is None: print("# %s-%: empty content" % (self.host, self.port)) - Label.update() + # Label.update() for suite in defs.suites: self.process_nc(suite, win) @@ -159,12 +216,21 @@ else: self.process_nc(item, win) - def process_node(self, node, wins): + def process_node(self, item, wins): for kind, win in wins.items(): - status = "%s" % node.get_state() + status = "%s" % item.get_state() if status != kind: continue - win.insert("%s:%s" % (self.nick, node.get_abs_node_path())) + change = item.get_state_change_time() + line = "#%s:%s,%s" % ( + self.nick, item.get_abs_node_path(), change) + try: + import check_task as ct + qid = str(ct.display_var(item, "qid")) + host = ct.display_var(item, "ECF_JOB_CMD") + line += ",%s,%s" % (host, qid) + except: pass + win.insert(line) try: sys.path.append('/usr/local/apps/sms/lib/') @@ -194,7 +260,7 @@ user = get_username() if DEBUG: - print "#MSG: login:", nick, host, port, user + print("#MSG: login:", nick, host, port, user) handle, rc = cdp.sms_client_login(host, user, passwd, timeout, port, tree) self.handle = handle @@ -219,7 +285,7 @@ path = cdp.sms_node_full_name(node) win.insert("%s:%s" % (self.nick, path)) if DEBUG: - print self.host, self.port, path, state + print(self.host, self.port, path, state) def process(self, win): rc = cdp.sms_client_news(self.handle, self.num) @@ -253,40 +319,59 @@ def __exit__(self): cdp.sms_client_logout(self.handle) - print "# logout from", self.nick + print("# logout from", self.nick) def __delete__(self): cdp.sms_client_logout(self.handle) - print "# logout from", self.nick + print("# logout from", self.nick) + class Application(tki.Frame): """ main """ - def __init__(self, master=None, client=None, queue=None): - tki.Frame.__init__(self, master) + def __init__(self, parent=None, client=None, queue=None): + if parent is None: parent = tki.Tk() + w, h = parent.winfo_screenwidth(), parent.winfo_screenheight() + tki.Frame.__init__(self, parent) if client is None: - self.__clients = [Client("localhost@31415"), ] - elif type(client) == set: + raise Exception("#ERR: please provide a client...\n./overview.py $ECF_HOST@$ECF_PORT") + elif type(client) in (set, tuple): self.__clients = client else: self.__clients = [client] - self.__queue = queue - width = 640 - height = 780 + # width = 640; height = 780 + width = w; height = h self.canvas = tki.Canvas(width=width, height=height, bg='black') self.grid() self.createWidgets() self.canvas.after(50, self.check_queue) + self.bind_all("", self.update) + # self.bind_all("Control_q", self.quit) + # self.bind_all("Control_z", self.resize) + self.bind_all("", self.quit) + self.bind_all("", self.resize) + + def resize(self, event=None): print("New size is: {}x{}".format(event.width, event.height)) + + def quit(self, event=None): + print("quit...") + self.destroy() + # global running; running = [ False, ] + # sys.exit(0) + def createWidgets(self): - rowx = 1 - glob = Label(tki.StringVar(self)) + global root root = self + rowx = 1 + var = tki.StringVar(self) + glob = Label(root) + # glob = Label(var) self.__menuBar = MenuBar(root) self.__menuBar.grid(row=0, column=0, sticky=tki.W) - self.label = tki.Label(root, textvariable=Label.inst) + self.label = tki.Label(root, textvariable=var) # Label.inst) self.label.grid(row=0, column=2, sticky=tki.E) self.__wins = dict() rowx += 1 @@ -308,8 +393,8 @@ self.update() self.canvas.after(50, self.check_queue) - def update(self): - Label.update() + def update(self, event=None): + self.label.update() for kind, win in self.__wins.items(): win.clear() for client in self.__clients: @@ -317,10 +402,7 @@ for clt in client: clt.process(self.__wins) else: - try: - client.process(self.__wins) - except: - pass + client.process(self.__wins) def get_username(): return pwd.getpwuid(os.getuid())[0] @@ -335,23 +417,30 @@ except: port = 31415 - if len(sys.argv) > 0: + if len(sys.argv) > 1: clients = [] - for num in xrange(1, len(sys.argv)): - clients.append(Client(sys.argv[num])) - else: - clients = [Client("localhost%d" % port)] + for num in xrange(1, len(sys.argv)): + sep = ',' + arg = sys.argv[num] + if sep in arg: + for one in arg.split(sep): + client = Client(one) + else: + client = Client(arg) + clients.append(client) + else: + one = "localhost%d" % port + print("# using", one) + clients = [Client(one), ] queue = Queue.Queue() - # app = AppSms(host, port, queue) app = Application(client=clients, queue=queue) app.master.title(PROGRAM_NAME) app.columnconfigure(0, weight=1) app.rowconfigure(0, weight=1) - PaceKeeper(app, queue) app.mainloop() """ -python ./overview.py localhost-localhost@31415 +python ./overview.py localhost@31415, """ diff -Nru ecflow-4.9.0/Pyext/src/ClientDoc.cpp ecflow-4.11.1/Pyext/src/ClientDoc.cpp --- ecflow-4.9.0/Pyext/src/ClientDoc.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/ClientDoc.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -792,7 +792,15 @@ " except RuntimeError, e:\n" " print(str(e))\n" " finally:\n" - " ci.ch_drop( client_handle ) # de-register the handle\n" + " ci.ch_drop( client_handle ) # de-register the handle\n\n" + "To automatically drop the handle(preferred) use with::\n\n" + " try:\n" + " with Client() as ci:\n" + " ci.ch_register(True,['s1','s2','s3']) # register interest in suites s1,s2,s3 and any new suites\n" + " client_handle = ci.ch_handle() # get the handle associated with last call to ch_register\n" + " .... # will automatically drop last handle\n" + " except RuntimeError, e:\n" + " print(str(e))\n" ; } @@ -826,7 +834,18 @@ " ci.sync_local() # will only retrieve data for suites s1 & s2\n" " update(ci.get_defs())\n" " finally:\n" - " ci.ch_drop()\n" + " ci.ch_drop()\n\n" + "To automatically drop the handle(Preferred) use with\n::\n\n" + " try:\n" + " with Client() as ci:\n" + " ci.ch_register(False,['s1','s2'])\n" + " while( 1 ):\n" + " # get incremental changes to suites s1 & s2, uses data stored on ci/defs\n" + " ci.sync_local() # will only retrieve data for suites s1 & s2\n" + " update(ci.get_defs())\n" + " .... # will automatically drop last handle\n" + " except RuntimeError, e:\n" + " print(str(e))\n" ; } @@ -870,9 +889,9 @@ " )\n" "\nUsage::\n\n" " try:\n" - " ci = Client() # use default host(ECF_HOST) & port(ECF_PORT)\n" - " ci.ch_register(True,[]) # register interest in any new suites\n" - " ci.ch_add(['s1','s2']) # add suites s1,s2 to the last added handle\n" + " with Client() as ci: # use default host(ECF_HOST) & port(ECF_PORT)\n" + " ci.ch_register(True,[]) # register interest in any new suites\n" + " ci.ch_add(['s1','s2']) # add suites s1,s2 to the last added handle\n" " except RuntimeError, e:\n" " print(str(e))\n\n" ; @@ -894,9 +913,9 @@ " )\n" "\nUsage::\n\n" " try:\n" - " ci = Client() # use default host(ECF_HOST) & port(ECF_PORT)\n" - " ci.ch_register(True,['s1','s2','s3']) # register interest in suites s1,s2,s3 and any new suites\n" - " ci.ch_remove( ['s1'] ) # remove suites s1 from the last added handle\n" + " with Client() as ci: # use default host(ECF_HOST) & port(ECF_PORT)\n" + " ci.ch_register(True,['s1','s2','s3']) # register interest in suites s1,s2,s3 and any new suites\n" + " ci.ch_remove( ['s1'] ) # remove suites s1 from the last added handle\n" " except RuntimeError, e:\n" " print(str(e))\n\n" ; @@ -918,9 +937,9 @@ " )\n" "\nUsage::\n\n" " try:\n" - " ci = Client() # use default host(ECF_HOST) & port(ECF_PORT)\n" - " ci.ch_register(True,['s1','s2','s3']) # register interest in suites s1,s2,s3 and any new suites\n" - " ci.ch_auto_add( False ) # disable adding newly created suites to my handle\n" + " with Client() as ci: # use default host(ECF_HOST) & port(ECF_PORT)\n" + " ci.ch_register(True,['s1','s2','s3']) # register interest in suites s1,s2,s3 and any new suites\n" + " ci.ch_auto_add( False ) # disable adding newly created suites to my handle\n" " except RuntimeError, e:\n" " print(str(e))\n\n" ; diff -Nru ecflow-4.9.0/Pyext/src/Edit.hpp ecflow-4.11.1/Pyext/src/Edit.hpp --- ecflow-4.9.0/Pyext/src/Edit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/Edit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -20,7 +20,7 @@ class Edit { public: - Edit(const boost::python::dict& dict); + explicit Edit(const boost::python::dict& dict); Edit(const boost::python::dict& dict,const boost::python::dict& dict2); const std::vector& variables() const { return vec_;} static std::string to_string() { return "edit";} diff -Nru ecflow-4.9.0/Pyext/src/ExportClient.cpp ecflow-4.11.1/Pyext/src/ExportClient.cpp --- ecflow-4.9.0/Pyext/src/ExportClient.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/ExportClient.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -53,7 +53,7 @@ /// Set the CLI to enable output to standard out class CliSetter { public: - CliSetter(ClientInvoker* self) : _self(self) { self->set_cli(true); } + explicit CliSetter(ClientInvoker* self) : _self(self) { self->set_cli(true); } ~CliSetter() { _self->set_cli(false);} private: ClientInvoker* _self; @@ -151,13 +151,38 @@ void set_child_pid(ClientInvoker* self,int pid) { self->set_child_pid( boost::lexical_cast(pid)); } +// Context mgr. The expression is evaluated and should result in an object called a ``context manager'' +// with expression [as variable]: +// with-block +// +// . The context manager must have __enter__() and __exit__() methods. +// . The context manager's __enter__() method is called. +// The value returned is assigned to VAR. +// If no 'as VAR' clause is present, the value is simply discarded. +// . The code in BLOCK is executed. +// . If BLOCK raises an exception, the __exit__(type, value, traceback) +// is called with the exception details, the same values returned by sys.exc_info(). +// The method's return value controls whether the exception is re-raised: +// any false value re-raises the exception, and True will result in suppressing it. +// You'll only rarely want to suppress the exception, because if you do the author +// of the code containing the 'with' statement will never realize anything went wrong. +// . If BLOCK didn't raise an exception, the __exit__() method is still called, but type, value, and traceback are all None. +// +boost::shared_ptr client_enter(boost::shared_ptr self) { return self;} +bool client_exit(boost::shared_ptr self,const bp::object& type,const bp::object& value,const bp::object& traceback){ + self->ch1_drop(); + return false; +} void export_Client() { - class_("Client",ClientDoc::class_client()) + // Need boost::shared_ptr, to add support for with( __enter__,__exit__) + class_,boost::noncopyable>("Client",ClientDoc::class_client()) .def( init() /* host:port */) .def( init() /* host, port */) .def( init() /* host, port(int) */) + .def("__enter__", &client_enter) // allow with statement + .def("__exit__", &client_exit) // allow with statement, remove last handle .def("version", &version, "Returns the current client version") .def("server_version", &server_version, "Returns the server version, can throw for old servers, that did not implement this request.") .def("set_host_port", &ClientInvoker::set_host_port, ClientDoc::set_host_port()) diff -Nru ecflow-4.9.0/Pyext/src/ExportNodeAttr.cpp ecflow-4.11.1/Pyext/src/ExportNodeAttr.cpp --- ecflow-4.9.0/Pyext/src/ExportNodeAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/ExportNodeAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -354,12 +354,13 @@ .value("complete",Child::COMPLETE) ; - enum_("AttrType", "Sortable attribute type, currently [event | meter | label | limit | variable ]") + enum_("AttrType", "Sortable attribute type, currently [event | meter | label | limit | variable | all ]") .value("event", Attr::EVENT) .value("meter", Attr::METER) .value("label", Attr::LABEL) .value("limit", Attr::LIMIT) .value("variable",Attr::VARIABLE) + .value("all", Attr::ALL) ; // ZombieAttr(ecf::Child::ZombieType t, const std::vector& c, ecf::User::Action a, int zombie_lifetime); diff -Nru ecflow-4.9.0/Pyext/src/ExportNode.cpp ecflow-4.11.1/Pyext/src/ExportNode.cpp --- ecflow-4.9.0/Pyext/src/ExportNode.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/ExportNode.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -141,7 +141,7 @@ if (previous_child && children[i] == child) { // if existing trigger, add new trigger as AND if (child->get_trigger()) child->add_part_trigger( PartExpression( previous_child->name() + " == complete", PartExpression::AND) ); - else child->add_trigger_expr( previous_child->name() + " == complete"); + else child->add_trigger_expr( Expression(previous_child->name() + " == complete")); } if (children[i]->defStatus() != DState::COMPLETE) previous_child = children[i]; } @@ -168,7 +168,7 @@ if (previous_child && previous_child != child && children[i] == child) { // if existing trigger, add new trigger as AND if (previous_child->get_trigger()) previous_child->add_part_trigger( PartExpression( child->name() + " == complete", PartExpression::AND) ); - else previous_child->add_trigger_expr( child->name() + " == complete"); + else previous_child->add_trigger_expr( Expression(child->name() + " == complete")); } } } diff -Nru ecflow-4.9.0/Pyext/src/ExportSuiteAndFamily.cpp ecflow-4.11.1/Pyext/src/ExportSuiteAndFamily.cpp --- ecflow-4.9.0/Pyext/src/ExportSuiteAndFamily.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/ExportSuiteAndFamily.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -92,6 +92,7 @@ .def("add_family",add_family ) .def("add_task", &NodeContainer::add_task , DefsDoc::add_task_doc()) .def("add_task", add_task ) + .def("find_node", &NodeContainer::find_by_name, "Find immediate child node given a name") .def("find_task", &NodeContainer::findTask , "Find a task given a name") .def("find_family", &NodeContainer::findFamily , "Find a family given a name") .add_property("nodes",bp::range( &NodeContainer::node_begin,&NodeContainer::node_end),"Returns a list of Node's") diff -Nru ecflow-4.9.0/Pyext/src/Trigger.hpp ecflow-4.11.1/Pyext/src/Trigger.hpp --- ecflow-4.9.0/Pyext/src/Trigger.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/src/Trigger.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -25,11 +25,11 @@ class Trigger { public: - Trigger(const std::string& expression) { add(PartExpression(expression)); } + explicit Trigger(const std::string& expression) { add(PartExpression(expression)); } Trigger(const std::string& expression,bool and_type) { add(PartExpression(expression,and_type));} - Trigger(const PartExpression& pe) { add(pe); } + explicit Trigger(const PartExpression& pe) { add(pe); } Trigger(const Trigger& rhs) : vec_(rhs.vec_) {} - Trigger(const boost::python::list& list ); + explicit Trigger(const boost::python::list& list ); bool operator==( const Trigger& rhs) const { return vec_ == rhs.vec_;} bool operator!=( const Trigger& rhs) const { return !operator==(rhs);} @@ -45,11 +45,11 @@ class Complete { public: - Complete(const std::string& expression) { add(PartExpression(expression)); } + explicit Complete(const std::string& expression) { add(PartExpression(expression)); } Complete(const std::string& expression,bool and_type) { add(PartExpression(expression,and_type));} - Complete(const PartExpression& pe) { add(pe); } + explicit Complete(const PartExpression& pe) { add(pe); } Complete(const Complete& rhs) : vec_(rhs.vec_) {} - Complete(const boost::python::list& list ); + explicit Complete(const boost::python::list& list ); bool operator==( const Complete& rhs) const { return vec_ == rhs.vec_;} bool operator!=( const Complete& rhs) const { return !operator==(rhs);} diff -Nru ecflow-4.9.0/Pyext/test/py_s_TestClientApi.py ecflow-4.11.1/Pyext/test/py_s_TestClientApi.py --- ecflow-4.9.0/Pyext/test/py_s_TestClientApi.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/test/py_s_TestClientApi.py 2018-10-23 11:41:34.000000000 +0000 @@ -1756,6 +1756,26 @@ dir_to_remove = Test.ecf_home(the_port) + "/" + "test_ECFLOW_199" shutil.rmtree(dir_to_remove) +def test_client_ch_with_drops_handles(the_port,top_ci): + print("test_client_ch_with_drops_handles") + try: + print(" test using with but without register, handle should be zero, ie do nothing") + with Client("localhost",the_port) as local_ci: + #print("Handle:",local_ci.ch_handle()) + assert local_ci.ch_handle() == 0,"Expected handle to be zero" + local_ci.ch_suites() + print(" Test Client with register, should drop handle") + with Client("localhost",the_port) as local_ci: + local_ci.ch_register(True, [ 's1','s2']) + #print("Handle:",local_ci .ch_handle()) + local_ci.ch_suites() + #raise RuntimeError("xxx") #check exeption is still caught + except RuntimeError as e: + print("Exception",e) + print("after with:") + # really need a way to get hold of the suites, via python api. + top_ci.ch_suites() # should be empty + if __name__ == "__main__": print("####################################################################") @@ -1770,30 +1790,32 @@ the_port = ci.get_port(); test_version(ci) PrintStyle.set_style( Style.STATE ) # show node state + test_client_get_server_defs(ci) test_client_new_log(ci, the_port) test_client_clear_log(ci, the_port) test_client_log_msg(ci, the_port) test_client_log_auto_flush(ci, the_port) - + test_client_restart_server(ci) test_client_halt_server(ci) test_client_shutdown_server(ci) - + test_client_load_in_memory_defs(ci) test_client_load_from_disk(ci) test_client_checkpt(ci, the_port) test_client_restore_from_checkpt(ci, the_port) - + test_client_reload_wl_file(ci, the_port) - + test_client_run(ci) test_client_run_with_multiple_paths(ci) test_client_requeue(ci) test_client_requeue_with_multiple_paths(ci) test_client_free_dep(ci) - + test_client_suites(ci) + test_client_ch_with_drops_handles(the_port,ci) test_client_ch_suites(ci) test_client_ch_register(ci) test_client_ch_drop(ci) @@ -1801,7 +1823,7 @@ test_client_ch_add(ci) test_client_ch_auto_add(ci) test_client_ch_remove(ci) - + test_client_get_file(ci) #test_client_plug(ci) test_client_alter_sort(ci) @@ -1811,12 +1833,12 @@ test_client_alter_change(ci) test_client_alter_flag(ci) test_client_flag_migrated(ci) - + test_client_force(ci) test_client_replace(ci,False) test_client_replace(ci,True) test_node_replace(ci) - + #test_client_kill(ci) #test_client_status(ci) #test_client_order(ci) @@ -1827,14 +1849,14 @@ test_client_resume_multiple_paths(ci) test_client_delete_node(ci) test_client_delete_node_multiple_paths(ci) - + test_client_check(ci) test_client_check_defstatus(ci) - + test_client_stats(ci) test_client_stats_reset(ci) test_client_debug_server_on_off(ci) - + test_ECFLOW_189(ci) test_ECFLOW_199(ci) diff -Nru ecflow-4.9.0/Pyext/test/py_u_TestAddDelete.py ecflow-4.11.1/Pyext/test/py_u_TestAddDelete.py --- ecflow-4.9.0/Pyext/test/py_u_TestAddDelete.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/test/py_u_TestAddDelete.py 2018-10-23 11:41:34.000000000 +0000 @@ -41,12 +41,33 @@ expected = ['AECF_URL','XECF_URL_BASE','YECF_URL_CMD','ZFRED'] actual = [] defs.sort_attributes("variable"); + for v in defs.user_variables: actual.append(v.name()) + assert actual == expected,"Attributes not sorted, expected:" + str(expected) + " but found:" + str(actual) + + expected = ['AECF_URL','XECF_URL_BASE','YECF_URL_CMD','ZFRED','ZZ'] + actual = [] + defs.add_variable("ZZ", "x") defs.sort_attributes(ecflow.AttrType.variable); for v in defs.user_variables: actual.append(v.name()) assert actual == expected,"Attributes not sorted, expected:" + str(expected) + " but found:" + str(actual) + + expected = ['AA', 'AECF_URL','XECF_URL_BASE','YECF_URL_CMD','ZFRED','ZZ'] + actual = [] + defs.add_variable("AA", "x") + defs.sort_attributes("all"); + for v in defs.user_variables: actual.append(v.name()) + assert actual == expected,"Attributes not sorted, expected:" + str(expected) + " but found:" + str(actual) + + expected = ['AA', 'AECF_URL', 'BB','XECF_URL_BASE','YECF_URL_CMD','ZFRED','ZZ'] + actual = [] + defs.add_variable("BB", "x") + defs.sort_attributes(ecflow.AttrType.all ); + for v in defs.user_variables: actual.append(v.name()) + assert actual == expected,"Attributes not sorted, expected:" + str(expected) + " but found:" + str(actual) + - defs.delete_variable("ZFRED");assert len(list(defs.user_variables)) == 3, "Expected 3 variables since we just delete FRED" + defs.delete_variable("ZFRED");assert len(list(defs.user_variables)) == 6, "Expected 6 variables since we just delete ZFRED" defs.delete_variable(""); assert len(list(defs.user_variables)) == 0, "Expected 0 variables since we should have deleted all" a_dict = { "name":"value", "name2":"value2", "name3":"value3", "name4":"value4" } diff -Nru ecflow-4.9.0/Pyext/test/py_u_TestCopy.py ecflow-4.11.1/Pyext/test/py_u_TestCopy.py --- ecflow-4.9.0/Pyext/test/py_u_TestCopy.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/test/py_u_TestCopy.py 2018-10-23 11:41:34.000000000 +0000 @@ -24,6 +24,7 @@ print("Running ecflow version " + ecflow.Client().version() + " debug build(" + str(ecflow.debug_build()) +")") print("PYTHONPATH: " + str(os.environ['PYTHONPATH'].split(os.pathsep))) print("sys.path: " + str(sys.path)) + print("Python version: " + str(sys.version_info[0]) + "." + str(sys.version_info[1])) print("####################################################################") defs = ecflow.Defs() diff -Nru ecflow-4.9.0/Pyext/test/py_u_TestError.py ecflow-4.11.1/Pyext/test/py_u_TestError.py --- ecflow-4.9.0/Pyext/test/py_u_TestError.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/test/py_u_TestError.py 2018-10-23 11:41:34.000000000 +0000 @@ -235,10 +235,12 @@ assert check_repeat_string("",["a"] )==False, "Expected Exception since no name specified" assert check_repeat_string(" ",["a"] )==False, "Expected Exception can not have spaces for a name" assert check_repeat_string("name",[ 1,2 ])==False, "Expected Exception since a list of strings was expected" + assert check_repeat_string("name",[])==False, "Expected Exception since list of strings is empty" assert check_repeat_enumerated("name",[ "a" ]), "Expected valid repeat" assert check_repeat_enumerated("",["a"] )==False, "Expected Exception since no name specified" - assert check_repeat_enumerated(" ",["a"] )==False, "Expected Exception since a list of strings was expected" + assert check_repeat_enumerated(" ",["a"] )==False, "Expected Exception since no name specified" assert check_repeat_enumerated("name",[ 1,2 ])==False, "Expected Exception since a list of strings was expected" + assert check_repeat_enumerated("name",[])==False, "Expected Exception since list is empty" assert check_variable("name","value"), "Expected valid Variable" @@ -420,6 +422,20 @@ assert test_passed,"duplicate Task test failed" # ================================================================================= + print("Task and family of same name should not be allowed") + test_passed = False + try: + suite = Suite("1") + ta = Task("a") + tb = Family("a") + suite.add_task(ta) + suite.add_family(tb) + except RuntimeError as e : + test_passed = True + pass + assert test_passed,"Task and family of same name should not be allowed" + + # ================================================================================= print("check duplicate meter not allowed") test_passed = False try: diff -Nru ecflow-4.9.0/Pyext/test/py_u_TestFind.py ecflow-4.11.1/Pyext/test/py_u_TestFind.py --- ecflow-4.9.0/Pyext/test/py_u_TestFind.py 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Pyext/test/py_u_TestFind.py 2018-10-23 11:41:34.000000000 +0000 @@ -46,21 +46,31 @@ f1 = s1.find_family("f1") assert f1 != None, "expected find find family f1" + f1 = s1.find_node("f1") + assert f1 != None, "expected find find family f1" fx = s1.find_family("fx") assert fx == None, "expected not to find family fx" + fx = s1.find_node("fx") + assert fx == None, "expected not to find family fx" f2 = s1.find_family("f2") assert f2 != None, "expected find find family f2" + f2 = s1.find_node("f2") + assert f2 != None, "expected find find family f2" f2_t1 = f2.find_task("f2_t1") assert f2_t1 != None, "Expected to find task" f2_t2 = f2.find_task("f2_t2") assert f2_t2 != None, "Expected to find task" + f2_t2 = f2.find_node("f2_t2") + assert f2_t2 != None, "Expected to find task" f2_tx = f2.find_task("f2_tx") assert f2_tx == None, "Expected not to find task" + f2_tx = f2.find_node("f2_tx") + assert f2_tx == None, "Expected not to find task" tasks = defs.get_all_tasks() assert len(tasks) == 4, "Expected four tasks, but found " + str(len(tasks)) diff -Nru ecflow-4.9.0/Server/src/Server.cpp ecflow-4.11.1/Server/src/Server.cpp --- ecflow-4.9.0/Server/src/Server.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Server/src/Server.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -40,6 +40,7 @@ #include "Version.hpp" #include "Str.hpp" #include "File.hpp" +#include "ExprDuplicate.hpp" #ifdef ECF_OPENSSL #include "Openssl.hpp" #endif @@ -169,6 +170,11 @@ { if (serverEnv_.debug()) cout << "<--Server::~server exiting server on port " << serverEnv_.port() << endl; + // Defs destructor may get called after we have returned from main. See ECFLOW-1291 + // In this case the static memory used in ExprDuplicate will not be reclaimed. hence do it here: + // Duplicate AST are held in a static map. Delete them, to avoid valgrind and ASAN from complaining + ExprDuplicate reclaim_cloned_ast_memory; + defs_.reset(); #ifdef DEBUG diff -Nru ecflow-4.9.0/Server/src/ServerEnvironment.hpp ecflow-4.11.1/Server/src/ServerEnvironment.hpp --- ecflow-4.9.0/Server/src/ServerEnvironment.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Server/src/ServerEnvironment.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -41,7 +41,7 @@ // Also distinguish between server and environment errors class ServerEnvironmentException : public std::runtime_error { public: - ServerEnvironmentException(const std::string& msg) : std::runtime_error(msg) {} + explicit ServerEnvironmentException(const std::string& msg) : std::runtime_error(msg) {} }; diff -Nru ecflow-4.9.0/Server/src/Server.hpp ecflow-4.11.1/Server/src/Server.hpp --- ecflow-4.9.0/Server/src/Server.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Server/src/Server.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -46,7 +46,7 @@ public: /// Constructor opens the acceptor and starts waiting for the first incoming /// connection. - Server(ServerEnvironment&); + explicit Server(ServerEnvironment&); virtual ~Server(); /// Start the server diff -Nru ecflow-4.9.0/Server/test/TestServer1.cpp ecflow-4.11.1/Server/test/TestServer1.cpp --- ecflow-4.9.0/Server/test/TestServer1.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Server/test/TestServer1.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -42,7 +42,7 @@ // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ class TestServer : public Server { public: - TestServer(ServerEnvironment& s) : Server(s) {} + explicit TestServer(ServerEnvironment& s) : Server(s) {} virtual ~TestServer() {} // abort server if check pt files exist, but can't be loaded diff -Nru ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake --- ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-Cray.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,6 @@ set( EC_SIZEOF_SIZE_T 8 ) set( EC_SIZEOF_SSIZE_T 8 ) set( EC_SIZEOF_OFF_T 8 ) -set( EC_SIZEOF_OFF_T 8 ) set( EC_BIG_ENDIAN 0 ) set( EC_LITTLE_ENDIAN 1 ) set( IEEE_BE 0 ) @@ -137,9 +136,9 @@ #################################################################### if( EXISTS "$ENV{CC_X86_64}/lib/x86-64/libcray-c++-rts.so" ) - set( LIBCRAY_CXX_RTS "$ENV{CC_X86_64}/lib/x86-64/libcray-c++-rts.so" ) + set( LIBCRAY_CXX_RTS "$ENV{CC_X86_64}/lib/x86-64/libcray-c++-rts.so" ) elseif( EXISTS "$ENV{CC_X86_64}/lib/libcray-c++-rts.so" ) - set( LIBCRAY_CXX_RTS "$ENV{CC_X86_64}/lib/libcray-c++-rts.so" ) + set( LIBCRAY_CXX_RTS "$ENV{CC_X86_64}/lib/libcray-c++-rts.so" ) endif() set( ECBUILD_SHARED_LINKER_FLAGS "-Wl,--eh-frame-hdr -Ktrap=fp" ) diff -Nru ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake --- ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-GNU.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,6 @@ set( EC_SIZEOF_SIZE_T 8 ) set( EC_SIZEOF_SSIZE_T 8 ) set( EC_SIZEOF_OFF_T 8 ) -set( EC_SIZEOF_OFF_T 8 ) set( EC_BIG_ENDIAN 0 ) set( EC_LITTLE_ENDIAN 1 ) set( IEEE_BE 0 ) diff -Nru ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake --- ecflow-4.9.0/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecbuild/toolchains/ecmwf-XC30-Intel.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -21,7 +21,6 @@ set( EC_SIZEOF_SIZE_T 8 ) set( EC_SIZEOF_SSIZE_T 8 ) set( EC_SIZEOF_OFF_T 8 ) -set( EC_SIZEOF_OFF_T 8 ) set( EC_BIG_ENDIAN 0 ) set( EC_LITTLE_ENDIAN 1 ) set( IEEE_BE 0 ) diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_gui.json ecflow-4.11.1/share/ecflow/etc/ecflowview_gui.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_gui.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_gui.json 2018-10-23 11:41:34.000000000 +0000 @@ -3,7 +3,8 @@ "hidden" : { "visible" : "false", - "line" : "panel.variable.showShadowed" + "line" : "panel.variable.showShadowed", + "line" : "view.table.variableColumns" }, "appearance": { @@ -119,6 +120,7 @@ "title" : "Options", "prefix" : "view", + "line" : "tree.autoExpandLeafNode", "line" : "tree.displayNodeType", "line" : "tree.display_child_count", "line" : "tree.indentation", @@ -151,7 +153,8 @@ "title" : "Options", "prefix" : "view.table", - "line" : "background" + "line" : "background", + "line" : "popupFilter" } }, "tab" : { @@ -252,12 +255,22 @@ "line" : "server.menu.nodeMenuMode", "note" : { - "default" : "Operator and Administrator modes allow suites to be begun, nodes to be removed and servers to be unlocked. Administrator mode additionally enables the Force and Order menus." - }, + "default" : "Operator and Administrator modes allow suites to be begun, nodes to be removed and servers to be unlocked. Administrator mode additionally enables the Force and Order menus." + }, "note" : { "default" : "These settings can be customised for each server separately" } }, + + "group" : { + "title" : "Node context menu control", + + "line" : "server.menu.defStatusMenuModeControl", + "note" : { + "default" : "These settings can be customised for each server separately" + } + }, + "group" : { "title" : "Request confirmation when:", "prefix" : "menu.confirm", diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_gui_server.json ecflow-4.11.1/share/ecflow/etc/ecflowview_gui_server.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_gui_server.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_gui_server.json 2018-10-23 11:41:34.000000000 +0000 @@ -37,7 +37,8 @@ "title" : "Menus", "prefix" : "server.menu", - "line" : "nodeMenuMode" + "line" : "nodeMenuMode", + "line" : "defStatusMenuModeControl" }, "custom-notification" : { diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_icon_conf.json ecflow-4.11.1/share/ecflow/etc/ecflowview_icon_conf.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_icon_conf.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_icon_conf.json 2018-10-23 11:41:34.000000000 +0000 @@ -38,12 +38,20 @@ }, "time" : { "label" : "Time", - "tooltip" : "Node has a time dependency.", - "shortDesc" : "Has time dependency", + "tooltip" : "Node has a holding time dependency.", + "shortDesc" : "Has holding time dependency", "icon" : { "default" : "icon_clock.svg" } }, + "time_free" : { + "label" : "Time (non-holding)", + "tooltip" : "Node has a non-holding time dependency.", + "shortDesc" : "Has non-holding time dependency", + "icon" : { + "default" : "icon_clock_free.svg" + } + }, "date" : { "label" : "Date", "tooltip" : "Node has date or day dependency.", diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_menus.json ecflow-4.11.1/share/ecflow/etc/ecflowview_menus.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_menus.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_menus.json 2018-10-23 11:41:34.000000000 +0000 @@ -223,7 +223,7 @@ "name" : "Defstatus", "type" : "Submenu", "menu" : "Node", - "visible_for" : "node or alias", + "visible_for" : "(node or alias) and (defStatusMenuModeControl)", "command" : "None" }, @@ -465,26 +465,51 @@ { "menu" : "Special", - "name" : "Check", - "command" : "sh ecflow_client --port %ECF_PORT% --host %ECF_HOST% --check /%SUITE%/%FAMILY:%%TASK:%", + "name" : "-" + }, + + { + "menu" : "Special", + "name" : "URL command", + "command" : "sh %ECF_URL_CMD%", "multi" : "false", "status_tip" : "__cmd__" }, + { + "menu" : "Special", + "name" : "-" + }, { "menu" : "Special", - "name" : "Free password", - "visible_for" : "(task or alias)", - "command" : "ecflow_client --alter add variable ECF_PASS FREE ", + "name" : "Create JSD ticket", + "visible_for" : "(task or alias) and ECFLOWUI_ECMWF_OPERATOR_MODE)", + "command" : "create_jsd_ticket", + "multi" : "false", "status_tip" : "__cmd__" }, { "menu" : "Special", - "name" : "Clear zombie flag", - "visible_for" : "(task or alias)", - "command" : "ecflow_client --alter clear_flag zombie ", + "name" : "-", + "visible_for" : "(task or alias) and ECFLOWUI_ECMWF_OPERATOR_MODE)" + }, + + { + "menu" : "Special", + "name" : "Check", + "command" : "sh ecflow_client --port %ECF_PORT% --host %ECF_HOST% --check /%SUITE%/%FAMILY:%%TASK:%", + "multi" : "false", + "status_tip" : "__cmd__" + }, + + { + "menu" : "Special", + "name" : "Check node state in UI", + "visible_for" : "(server or node) and ECFLOWUI_DEVELOP_MODE)", + "command" : "check_ui_node_state", + "multi" : "false", "status_tip" : "__cmd__" }, @@ -514,6 +539,14 @@ { "menu" : "Special", + "name" : "Clear zombie flag", + "visible_for" : "(task or alias)", + "command" : "ecflow_client --alter clear_flag zombie ", + "status_tip" : "__cmd__" + }, + + { + "menu" : "Special", "name" : "Details", "visible_for" : "task", "command" : "sh grep %ECF_NAME% %ECF_LOG%", @@ -530,6 +563,14 @@ "status_tip" : "__cmd__" }, +{ + "menu" : "Special", + "name" : "Free password", + "visible_for" : "(task or alias)", + "command" : "ecflow_client --alter add variable ECF_PASS FREE ", + "status_tip" : "__cmd__" + }, + { "menu" : "Special", "name" : "Mark for move", @@ -702,7 +743,7 @@ { "menu" : "Node", "name" : "Zombies ...", - "visible_for" : "server", + "visible_for" : "server or node", "command" : "zombie", "multi" : "false", "handler" : "info_panel", diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_panels.json ecflow-4.11.1/share/ecflow/etc/ecflowview_panels.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_panels.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_panels.json 2018-10-23 11:41:34.000000000 +0000 @@ -130,7 +130,7 @@ "dock_icon" : "zombie_dock.svg", "show" : "toolbar", "button_tooltip" : "Open zombies tab in a new Info Panel dialog", - "visible_for" : "server" + "visible_for" : "server or node" }, @@ -138,17 +138,25 @@ "name" : "suite", "label" : "Suite filter", "icon" : "filter.svg", - "visible_for" : "server" + "show" : "toolbar", + "visible_for" : "server or node" }, - + { + "name" : "server_load", + "label" : "Server load", + "icon" : "monitor.svg", + "show" : "toolbar", + "visible_for" : "server or node" + + }, { "name" : "server_settings", "label" : "Settings", "icon" : "cogwheel_blue.svg", - "visible_for" : "server" + "show" : "toolbar", + "visible_for" : "server or node" - } - + } ] } diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_server_conf.json ecflow-4.11.1/share/ecflow/etc/ecflowview_server_conf.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_server_conf.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_server_conf.json 2018-10-23 11:41:34.000000000 +0000 @@ -91,7 +91,16 @@ "values" : "user/oper/admin", "values_label" : "User/Operator/Administrator", "default": "user" - } + }, + + "defStatusMenuModeControl": { + "label": "Enable Defstatus submenu in these modes", + "tooltip" : "Controls when the Defstatus submenu is available", + "values" : "user/oper/admin", + "values_label" : "User/Operator/Administrator", + "default": "user/oper/admin", + "multi" : "true" + } }, "notification": { diff -Nru ecflow-4.9.0/share/ecflow/etc/ecflowview_view_conf.json ecflow-4.11.1/share/ecflow/etc/ecflowview_view_conf.json --- ecflow-4.9.0/share/ecflow/etc/ecflowview_view_conf.json 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/share/ecflow/etc/ecflowview_view_conf.json 2018-10-23 11:41:34.000000000 +0000 @@ -27,6 +27,12 @@ "values" : "standard/compact", "values_label" : "Standard/Compact" }, + + "autoExpandLeafNode" : { + "label" : "Automatically expand leaf nodes", + "default" : "false" + }, + "nodeFont" : { "label" : "Nodes", "default": "font(,)" @@ -105,7 +111,15 @@ "font" : { "label" : "Font", "default": "font(,)" - } + }, + "popupFilter" : { + "label" : "Popup filter dialog when new table view is added", + "default" : "true" + }, + "variableColumns" : { + "label" : "Variable columns", + "default" : "__none__" + } }, "attribute" : { @@ -232,7 +246,9 @@ }, "table_columns": { - + + "__config__" : "view.table.variableColumns", + "path" : { "label" : "Node", "tooltip" : "Node" diff -Nru ecflow-4.9.0/Test/src/TestFixture.hpp ecflow-4.11.1/Test/src/TestFixture.hpp --- ecflow-4.9.0/Test/src/TestFixture.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Test/src/TestFixture.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -39,7 +39,7 @@ // Since this class is static, the constructor/destructor can not call // any of BOOST MACRO, since the unit test will not be there. // When running across platforms will will assume server is already running - TestFixture(const std::string& project_test_dir /* Test or view */); + explicit TestFixture(const std::string& project_test_dir /* Test or view */); TestFixture(); ~TestFixture(); diff -Nru ecflow-4.9.0/Test/src/ZombieUtil.hpp ecflow-4.11.1/Test/src/ZombieUtil.hpp --- ecflow-4.9.0/Test/src/ZombieUtil.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Test/src/ZombieUtil.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -29,7 +29,7 @@ class TestClean : private boost::noncopyable { public: - TestClean(int timeout = 25) : timeout_(timeout) { ZombieUtil::test_clean_up(timeout);} + explicit TestClean(int timeout = 25) : timeout_(timeout) { ZombieUtil::test_clean_up(timeout);} ~TestClean() { ZombieUtil::test_clean_up(timeout_);} private: int timeout_; diff -Nru ecflow-4.9.0/tools/CMakeLists.txt ecflow-4.11.1/tools/CMakeLists.txt --- ecflow-4.9.0/tools/CMakeLists.txt 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/tools/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -1,3 +1,9 @@ + +# This can be used by submission system. i.e trimurti +ecbuild_add_executable( TARGET ecflow_standalone + SOURCES ecflow_standalone.c + ) + # # Configure ecflow_site.sh. This is used in ecflow_start.sh and ecflow_stop.sh # Used in coping with start of ecflow server on a linux cluser diff -Nru ecflow-4.9.0/tools/ecflow_logserver.sh ecflow-4.11.1/tools/ecflow_logserver.sh --- ecflow-4.9.0/tools/ecflow_logserver.sh 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/tools/ecflow_logserver.sh 2018-10-23 11:41:34.000000000 +0000 @@ -1,4 +1,4 @@ -#!/bin/ksh +#!/bin/bash #========================================================================================= # Syntax @@ -6,7 +6,7 @@ # #========================================================================================= -USAGE(){ +USAGE() { echo "Usage: $0 [-d ] [-m ] [-l ] [-h]" echo " -d specify the directory name where files will be served" echo " from - default is \$HOME" @@ -54,17 +54,17 @@ fi -if [[ "${EC_TIMECRIT_UID}" = "yes" ]] ; then +if [[ "${EC_TIMECRIT_UID}" == "yes" ]] ; then # Time-critical user with no $HOME set - if [[ "${server_dir:-}" = "" ]] ; then + if [[ "${server_dir:-}" == "" ]] ; then echo "Set the location of the server directory with -d" echo "" USAGE exit 1 fi - if [[ "${server_logfile:-}" = "" ]] ; then + if [[ "${server_logfile:-}" == "" ]] ; then echo "Set the location of the server log file with -l" echo "" USAGE @@ -92,22 +92,21 @@ # prognum is set based on the unique users numeric uid. -username=`id -u` +username=$(id -u) base=35000 prognum=$((base+username)) -PROG=`which $0` -PROG_PATH=`readlink -f $PROG` +PROG=$(which $0) +PROG_PATH=$(readlink -f $PROG) PATH_NAME=$ecflow_DIR/bin -# `dirname $PROG_PATH` export LOGPORT=$prognum -LOGDIR=`dirname $LOGFILE` +LOGDIR=$(dirname $LOGFILE) [[ ! -d $LOGDIR ]] && mkdir -p $LOGDIR -check=`ps -fu ${USER} | grep ecflow_logsvr.pl | grep -v grep 1>/dev/null 2>&1 \ - && echo 1 || echo 0` +check=$(ps -fu ${USER} | grep ecflow_logsvr.pl | grep -v grep 1>/dev/null 2>&1 \ + && echo 1 || echo 0) if [ $check = 0 ] ; then nohup $PATH_NAME/ecflow_logsvr.pl 1>$LOGFILE 2>&1 & else @@ -116,8 +115,8 @@ sleep 1 -check=`ps -fu ${USER} | grep ecflow_logsvr.pl | grep -v grep 1>/dev/null 2>&1 \ - && echo 1 || echo 0` +check=$(ps -fu ${USER} | grep ecflow_logsvr.pl | grep -v grep 1>/dev/null 2>&1 \ + && echo 1 || echo 0) if [ $check = 0 ] ; then /usr/bin/tail -n 30 $LOGFILE | /bin/mail -s "$(hostname -s): logserver for ${USER} did not start. Please investigate..." -c "root" ${USER} exit 1 @@ -125,7 +124,7 @@ if [[ -f ${LOGSERVERLIST} ]] ; then - logserverfound=`grep $LOGPORT ${LOGSERVERLIST} | grep $USER` + logserverfound=$(grep $LOGPORT ${LOGSERVERLIST} | grep $USER) if [[ -z $logserverfound ]]; then echo $USER $LOGPORT $LOGPATH $LOGMAP>> ${LOGSERVERLIST} fi diff -Nru ecflow-4.9.0/tools/ecflow_standalone.c ecflow-4.11.1/tools/ecflow_standalone.c --- ecflow-4.9.0/tools/ecflow_standalone.c 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/tools/ecflow_standalone.c 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,195 @@ +/* +## Copyright 2009-2019 ECMWF. +## This software is licensed under the terms of the Apache Licence version 2.0 +## which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +## In applying this licence, ECMWF does not waive the privileges and immunities +## granted to it by virtue of its status as an intergovernmental organisation +## nor does it submit to any jurisdiction. +* +* Read stdin and create a session from it +* +* Read standard in, to a input file, forks a child process as a separate session +* and then run the shell using the input file. In the child process, we close +* standard out, and duplicate standard out as standard error. The next fopen() +* is then used as standard out/error. i.e output file. +* +# Build stand alone to test +gcc -g -Dlinux ecflow_standalone.c -o ecflow_standalone + +# Create file exe.sh use in the test below. +cat > $(pwd)/exe.sh <<\!! +xxx="hello worlds from /home/ma/ma0" +#printenv +echo $SHELL +fred="ma0@ecmwf.int" +mail -s "$xxx" $fred <<@@ +$xxx +@@ +!! + +# Both these tests must work. +ssh localhost $(pwd)/ecflow_standalone -s /bin/bash -o $(pwd)/out.txt < $(pwd)/exe.sh # EXPECT non empty out.txt, and mail +ssh localhost $(pwd)/ecflow_standalone -s /bin/bash -o $(pwd)/out.txt -i $(pwd)/exe.sh # EXPECT non empty out.txt, and mail + +# expected out.txt ++ xxx='hello worlds from /home/ma/map' ++ printenv ++ echo /bin/ksh ++ fred=ma0@ecmwf.int ++ mail -s 'hello worlds from /home/ma/map' ma0@ecmwf.int + +std=/usr/local/apps/sms/bin/standalone +ssh eurus.ecmwf.int $std -s /bin/bash -o $(pwd)/out.txt < $(pwd)/exe.sh # OK +ssh localhost $std -s /bin/bash -o $(pwd)/out.txt -i $(pwd)/exe.sh # OK + +* +* Other test using: +* echo "xxx=\"hello worlds from $HOME\"\nfred=$USER" | ./ecflow_standalone -i in.txt -o out.txt +* +* in.txt +* xxx="hello worlds from /home/ma/ma0" +* fred=ma0 +* +* out.txt +* + xxx='hello worlds from /home/ma/ma0' +* + fred=ma0 +************************************o*************************************/ + +#include +#include +#include +#ifdef SYG +#define +#endif + +#include +#include +#include +#include /* for PATH_MAX */ + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + +#define MAXLEN 1024 /* Maximum line lenght */ + +char *nameof(char *name) { + char *s; + int len = strlen(name); + for( s=name+len-1 ; len && *s != '/' ; len-- ) s--; + if(*s == '/') s++; + return s; +} + +int main(argc,argv) int argc; char **argv; +{ + char buff[MAXLEN]; /* Temp buffer to read in lines */ + char fname[PATH_MAX]; + char *infile = NULL; /* Temporary input file */ + char *outfile= "/dev/null"; /* Output file (def /dev/null) */ + char *shell= "/bin/sh"; /* default shell */ + /* int keep_file=FALSE;*/ /* Flag to keep the input file */ + + FILE *fp; /* Temp to write the input file */ + + int option; + extern char *optarg; /* Needed for the getopt */ + extern int optind; + + signal(SIGCHLD,SIG_IGN); + + while( (option=getopt(argc,argv,"i:o:s:")) != -1 ) + switch( option ) { + case 'i': + infile = optarg; + /* keep_file = TRUE; */ + break; + + case 'o': + outfile = optarg; + break; + + case 's': + shell = optarg; + break; + + default: + fprintf(stderr,"usage: %s [-i inputfile] -o [outputfile]\n",*argv); + exit(0); + } + + /* Copy standard input to infile */ + if( !infile ) { + /* printf("!infile\n"); */ + static char template[] = "/tmp/tmp_ecflowXXXXXX"; + strcpy(fname, template); /* Copy template */ + + int fd = mkstemp(fname); + /* printf("input Filename is %s\n", fname); Print it for information */ + infile = fname; + close(fd); + + if (!(fp = fopen(infile, "w"))) { + perror("ecflow_standalone.c, temp file creation error"); + exit(1); + } + while( fgets(buff, MAXLEN-1, stdin)) { + /* fprintf(stderr, "%s", buff); */ + fputs(buff,fp); + } + } + else { + if( !(fp=fopen(infile,"r")) ) { + perror("STANDALONE-INPUT-FILE cannot open"); + exit(1); + } + } + fclose(fp); + + /* fork child process, closing the parent */ + switch(fork()) { + case -1: perror(*argv); exit(1); + case 0: break; /* child */ + default: exit(0); /* The parent exits */ + } + + + /* close standard out,and make standard out a copy of standard error + * This is done so that very next fopen(), will be used as stdout/srderr for execl + * + * int dup2(int oldfd, int newfd); + * makes newfd be the copy of oldfd, closing newfd first if necessary + * */ + close(2); /* close standard error in child */ + dup2(2,1); + FILE* fout = fopen(outfile,"w"); /* Open file for output and error, when running execl(..) */ + close(0); /* close standard in , in child */ + + + /* make sure infile exists and is readable */ + if( !(fp=fopen(infile,"r")) ) { + perror("STANDALONE-INPUT-FILE-FOR-SHELL"); + exit(1); + } + /* fclose(fp); */ + + /* if( !keep_file ) unlink(infile); + for (n=3; n<65535 ;n++) fclose(n); */ + + /* create a new session from the child process */ +#if defined(linux) || defined(__APPLE__) || defined(__MACH__) || defined(hpux) || defined(solaris) || defined(SGI) || defined(SVR4) || defined(AIX) || defined(SYG) || defined(alpha) + if( setsid() == -1 ) +#else + if( setsid(0) == -1 ) +#endif + { + perror("STANDALONE-SETSID"); + exit(1); + } + + execl(shell,nameof(shell),"-x",infile,(char *)0); + /* if( !keep_file ) unlink(infile); + fclose(fout); */ + exit(1); +} diff -Nru ecflow-4.9.0/tools/ecflow_start.sh ecflow-4.11.1/tools/ecflow_start.sh --- ecflow-4.9.0/tools/ecflow_start.sh 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/tools/ecflow_start.sh 2018-10-23 11:41:34.000000000 +0000 @@ -26,15 +26,19 @@ backup_server=false verbose=false rerun=false +check=false #========================================================================== # Syntax # ecflow_start [-b] [-d ecf_home_directory] [-f] [-h] [-p port_number ] #========================================================================== # get command line options if any. -while getopts hfbd:vp:r option +while getopts chfbd:vp:r option do case $option in +c) +check=true +;; f) force=true ;; @@ -113,7 +117,26 @@ if [ -f $fname ]; then host=$(cat $fname); fi mkdir -p $rcdir -ecflow_client --port=$ECF_PORT --host=$host --ping && echo "server is already started" && exit 0 || : +THERE=KO +ecflow_client --port=$ECF_PORT --host=$host --ping && THERE=OK +if [[ $THERE == OK ]]; then + echo "server is already started" + res="$(ps -lf -u $USER | grep ecflow_server | grep -v grep)" + # which netstat && res="$(netstat -lnptu 2>/dev/null | grep ecflow | grep $ECF_PORT)" + echo "$res $(ecflow_client --stats)" + if [ "$res" == "" ] ; then + mail $USER -s "server is already started - server hijack?" </dev/null cp $ECF_CHECKOLD log/ 2>/dev/null -if [[ -f $ECF_LOG ]]; then +if [ -f $ECF_LOG ]; then STAMP=$(date +%Y%m%d.%H%M) SIZE=$(du -Hm $ECF_LOG | awk '{print $1}') || SIZE=0 - if [[ $SIZE -gt 100 ]]; then + if [ $SIZE -gt 100 ]; then echo "Moving, compressing logfile ${SIZE}mb ${ECF_LOG}.${STAMP}.log" mv $ECF_LOG log/${ECF_LOG}.${STAMP}.log 2>/dev/null gzip -f log/${ECF_LOG}.${STAMP}.log 2>/dev/null @@ -230,6 +253,10 @@ echo "OK starting ecFlow server..." echo ""; +if [ $check == true ]; then + ecflow_client --load $ECF_CHECK check_only +fi + nohup ecflow_server > $ECF_OUT 2>&1 < /dev/null & # the sleep allows time for server to start diff -Nru ecflow-4.9.0/VERSION.cmake ecflow-4.11.1/VERSION.cmake --- ecflow-4.9.0/VERSION.cmake 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/VERSION.cmake 2018-10-23 11:41:34.000000000 +0000 @@ -1,7 +1,7 @@ set( ECFLOW_RELEASE "4" ) -set( ECFLOW_MAJOR "9" ) -set( ECFLOW_MINOR "0" ) +set( ECFLOW_MAJOR "11" ) +set( ECFLOW_MINOR "1" ) # use this form to be consistent with other packages,+ allow standard extraction of version from scripts # could replaced "4.0.8" with "${ECFLOW_RELEASE}.${ECFLOW_MAJOR}.${ECFLOW_MINOR}" -set( ${PROJECT_NAME}_VERSION_STR "4.9.0" ) +set( ${PROJECT_NAME}_VERSION_STR "4.11.1" ) diff -Nru ecflow-4.9.0/view/src/ecf_node.cc ecflow-4.11.1/view/src/ecf_node.cc --- ecflow-4.9.0/view/src/ecf_node.cc 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/view/src/ecf_node.cc 2018-10-23 11:41:34.000000000 +0000 @@ -1357,7 +1357,7 @@ template<> const Event& ecf_concrete_node::get_event( const std::string& name ) { - return owner_ ? owner_->findEvent(name) : Event::EMPTY(); + return owner_ ? owner_->findEvent(Event(name)) : Event::EMPTY(); } template<> const Meter& ecf_concrete_node::get_meter( const std::string& name ) @@ -1378,7 +1378,7 @@ template<> const Event& ecf_concrete_node::get_event( const std::string& name ) { - return owner_ ? owner_->findEvent(name) : Event::EMPTY(); + return owner_ ? owner_->findEvent(Event(name)) : Event::EMPTY(); } template<> const Meter& ecf_concrete_node::get_meter( const std::string& name ) diff -Nru ecflow-4.9.0/view/src/host.cc ecflow-4.11.1/view/src/host.cc --- ecflow-4.9.0/view/src/host.cc 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/view/src/host.cc 2018-10-23 11:41:34.000000000 +0000 @@ -190,6 +190,7 @@ , history_len_(100) , updating_(false) , jobfile_length_(this, "jobfile_length", 10000) + , _input (getenv("ECFLOWVIEW_INPUT")) { if (number < 1) return; // dummy server OK; @@ -198,9 +199,14 @@ gui::add_host(name); } - if (timeout_ < 30) timeout_ = 30; - if (maximum_ < 30) maximum_ = 30; - + if (timeout_ < 30) { + timeout_ = 30; + gui::error("%s: timeout reset to 30!", this->name()); + } + if (maximum_ < 30) { + maximum_ = 30; + gui::error("%s: maximum reset to 30!", this->name()); + } frequency(timeout_); } @@ -366,6 +372,7 @@ void host::run() { if (!poll_) return; + if(_input != 0) { scripting::run(_input); } update(); if (drift_) drift(5, maximum_ * 60); } @@ -952,7 +959,13 @@ void host::changed( resource& r ) { - if (&r == &timeout_) frequency(timeout_); + if (&r == &timeout_) { + if (timeout_ < 30) { + timeout_ = 30; + gui::error("%s: timeout reset to 30!", this->name()); + } + frequency(timeout_); + } } void ehost::changed( resource& r ) @@ -1749,6 +1762,8 @@ gui::message("host::news-error: %s", e.what()); XECFDEBUG std::cerr << "# host::news-error: " << e.what() << "\n"; } + + // if(_input != 0) { scripting::run(_input); } return err; } @@ -1917,3 +1932,24 @@ catch ( std::exception& e ) { } } + +/* +ECFLOWVIEW_INPUT=/tmp/ecflowview_listen +mkfifo $ECFLOWVIEW_INPUT +./bin/ecflowview & +echo "\nselect eod /law/main" >> $ECFLOWVIEW_INPUT +echo "\nselect eod /law" >> $ECFLOWVIEW_INPUT +echo "\norder eod4:/era5eda_7 alpha" >> $ECFLOWVIEW_INPUT + +echo "\nselect eod /law/main/12/euroshelf/wamrun" >> $ECFLOWVIEW_INPUT +echo "\nwindow -d -f Script" >> $ECFLOWVIEW_INPUT +echo "\nwindow -d -f Manual" >> $ECFLOWVIEW_INPUT + +echo "\nlogin eod2" >> $ECFLOWVIEW_INPUT +echo "\nlogout eod2" >> $ECFLOWVIEW_INPUT +echo "\nquit" >> $ECFLOWVIEW_INPUT + +WINDOW: +Check Collector Edit History Info Job Jobstatus Manual Messages +Options Output Script Suites Timeline Triggers Variables Why Zombies +*/ diff -Nru ecflow-4.9.0/view/src/host.h ecflow-4.11.1/view/src/host.h --- ecflow-4.9.0/view/src/host.h 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/view/src/host.h 2018-10-23 11:41:34.000000000 +0000 @@ -267,6 +267,8 @@ bool updating_; // SUP-423 option jobfile_length_; + + const char* _input; public: void updating(bool b) { updating_ = b; } virtual void stats(std::ostream& f) { }; diff -Nru ecflow-4.9.0/view/src/scripting.cc ecflow-4.11.1/view/src/scripting.cc --- ecflow-4.9.0/view/src/scripting.cc 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/view/src/scripting.cc 2018-10-23 11:41:34.000000000 +0000 @@ -90,17 +90,18 @@ } int process_command(const char *cmd) { - if (!cmd) return 1; + if (!cmd) return False; if (!strncmp("select", cmd, 6)) { char host[80] = { 0, }; char node[1024] = { 0, }; sscanf(cmd, "select %s %s", host, node); if (host[0] != 0 && node[0] != 0) { + std::cout << "#CMD (scripting): " << cmd << "\n"; select_cmd(host, node); } else { std::cerr << "#CMD (scripting): err: " << cmd << "\n"; - return 1; + return False; } } else if (!strncmp("order", cmd, 5)) { @@ -108,16 +109,16 @@ char node[1024] = { 0, }; sscanf(cmd, "order %s %s", node, kind); if (kind[0] != 0 && node[0] != 0) { + std::cout << "#CMD (scripting): " << cmd << "\n"; order_cmd(node, kind); } else { std::cerr << "#CMD (scripting): err: " << cmd << "\n"; - return 1; + return False; } } else if (!strncmp("menu", cmd, 4)) { menu_cmd(cmd); - } else if (!strncmp("quit", cmd, 4)) { quit_cmd(); @@ -128,7 +129,6 @@ login_cmd(host); } - } else if (!strncmp("output", cmd, 6)) { char node[2048] = { 0, }; char path[2048] = { 0, }; @@ -165,23 +165,24 @@ if (!strncmp("-f", name, 2)) frozen = 1; if (*ptr != ' ') { break; /* skip separator */ } ptr++; - std::cerr << "#CMD (scripting): process: " << name << "\n"; + std::cout << "#CMD (scripting): process: " << name << "\n"; } if (name[0] != 0) { + std::cout << "#CMD (scripting): process: " << name << "\n"; window_cmd(name, detached, frozen); } else { std::cerr << "#CMD (scripting): err: " << cmd << "\n"; - return 1; + return False; } } else if (!strncmp("\n", cmd, 1)) { } else { std::cerr << "#CMD (scripting): ignored: " << cmd << "\n"; - return 1; + return False; } std::cout << "#CMD (scripting): " << cmd << "\n"; - return 0; + return True; } #undef SCRIPT_PYTHON @@ -216,8 +217,8 @@ class ecflowview_input { std::string name_; - XtInputId id_; - int fd_; + XtInputId id_; + int fd_; std::string line_; public: @@ -402,7 +403,7 @@ scripting* s = find(argv[0]); if(s) return s->execute(argc,argv); fprintf(stderr,"cannot find command %s\n",argv[0]); - return 1; + return False; } IMP(scripting) diff -Nru ecflow-4.9.0/Viewer/CMakeLists.txt ecflow-4.11.1/Viewer/CMakeLists.txt --- ecflow-4.9.0/Viewer/CMakeLists.txt 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -13,5 +13,8 @@ cmake_policy(SET CMP0046 OLD) endif() -add_subdirectory( src ) -add_subdirectory( scripts ) +add_subdirectory( libViewer) +add_subdirectory( ecflowUI) +if (ECFLOW_LOGVIEW) + add_subdirectory( logViewer ) +endif() diff -Nru ecflow-4.9.0/Viewer/ecflowUI/CMakeLists.txt ecflow-4.11.1/Viewer/ecflowUI/CMakeLists.txt --- ecflow-4.9.0/Viewer/ecflowUI/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,17 @@ +# avoid warning about dependency targets that don't exit +# +# CMake Warning (dev) at Viewer/src/CMakeLists.txt:261 (ADD_DEPENDENCIES): +# Policy CMP0046 is not set: Error on non-existent dependency in +# add_dependencies. Run "cmake --help-policy CMP0046" for policy details. +# Use the cmake_policy command to set the policy and suppress this warning. +# +# The dependency target +# "/tmp/ma0/workspace/ecflow/Viewer/src/../images/zombie_dock.svg" of target +# "Qt_resource_cpp" does not exist. + +if (POLICY CMP0046) + cmake_policy(SET CMP0046 OLD) +endif() + +add_subdirectory( src ) +add_subdirectory( scripts ) diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add_info.svg ecflow-4.11.1/Viewer/ecflowUI/images/add_info.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add_info.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add_info.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,531 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + i + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add.svg ecflow-4.11.1/Viewer/ecflowUI/images/add.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add_table.svg ecflow-4.11.1/Viewer/ecflowUI/images/add_table.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add_table.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add_table.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,422 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add_tab.svg ecflow-4.11.1/Viewer/ecflowUI/images/add_tab.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add_tab.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add_tab.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,301 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add_tree.svg ecflow-4.11.1/Viewer/ecflowUI/images/add_tree.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add_tree.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add_tree.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,537 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/add_variable_column.svg ecflow-4.11.1/Viewer/ecflowUI/images/add_variable_column.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/add_variable_column.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/add_variable_column.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + V + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/arrow_down.svg ecflow-4.11.1/Viewer/ecflowUI/images/arrow_down.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/arrow_down.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/arrow_down.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/arrow_up.svg ecflow-4.11.1/Viewer/ecflowUI/images/arrow_up.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/arrow_up.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/arrow_up.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/attribute.svg ecflow-4.11.1/Viewer/ecflowUI/images/attribute.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/attribute.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/attribute.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + a + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/bookmark.svg ecflow-4.11.1/Viewer/ecflowUI/images/bookmark.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/bookmark.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/bookmark.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/case_sensitive.svg ecflow-4.11.1/Viewer/ecflowUI/images/case_sensitive.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/case_sensitive.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/case_sensitive.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + Aa + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/chain_closed.svg ecflow-4.11.1/Viewer/ecflowUI/images/chain_closed.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/chain_closed.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/chain_closed.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/chain_open.svg ecflow-4.11.1/Viewer/ecflowUI/images/chain_open.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/chain_open.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/chain_open.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/chain.svg ecflow-4.11.1/Viewer/ecflowUI/images/chain.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/chain.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/chain.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/clear_left.svg ecflow-4.11.1/Viewer/ecflowUI/images/clear_left.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/clear_left.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/clear_left.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/close_grey.svg ecflow-4.11.1/Viewer/ecflowUI/images/close_grey.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/close_grey.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/close_grey.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/close_red.svg ecflow-4.11.1/Viewer/ecflowUI/images/close_red.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/close_red.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/close_red.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,177 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/close.svg ecflow-4.11.1/Viewer/ecflowUI/images/close.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/close.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/close.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,242 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/cogwheel_blue.svg ecflow-4.11.1/Viewer/ecflowUI/images/cogwheel_blue.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/cogwheel_blue.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/cogwheel_blue.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + image/svg+xml + + + + + + + + Layer 1 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/cogwheel.svg ecflow-4.11.1/Viewer/ecflowUI/images/cogwheel.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/cogwheel.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/cogwheel.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + image/svg+xml + + + + + + + + Layer 1 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/colour.svg ecflow-4.11.1/Viewer/ecflowUI/images/colour.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/colour.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/colour.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/configure.svg ecflow-4.11.1/Viewer/ecflowUI/images/configure.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/configure.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/configure.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,170 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/copy_path.svg ecflow-4.11.1/Viewer/ecflowUI/images/copy_path.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/copy_path.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/copy_path.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,191 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dependency_detail.svg ecflow-4.11.1/Viewer/ecflowUI/images/dependency_detail.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dependency_detail.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dependency_detail.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,227 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dependency.svg ecflow-4.11.1/Viewer/ecflowUI/images/dependency.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dependency.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dependency.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/diag.svg ecflow-4.11.1/Viewer/ecflowUI/images/diag.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/diag.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/diag.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + D + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/directory_arrow.svg ecflow-4.11.1/Viewer/ecflowUI/images/directory_arrow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/directory_arrow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/directory_arrow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_add_variable_column.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_add_variable_column.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_add_variable_column.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_add_variable_column.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + V + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_chain_closed.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_chain_closed.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_chain_closed.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_chain_closed.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_chain_open.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_chain_open.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_chain_open.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_chain_open.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_close.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_close.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_close.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_close.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_config.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_config.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_config.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_config.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + image/svg+xml + + + + + + + + Layer 1 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_dependency.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_dependency.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_dependency.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_dependency.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_float.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_float.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_float.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_float.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_max_disabled.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_max_disabled.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_max_disabled.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_max_disabled.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_max.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_max.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_max.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_max.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/dock_menu_indicator.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/dock_menu_indicator.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_menu_indicator.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_menu_indicator.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_menu_indicator.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_menu_indicator.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/dock_restore.svg ecflow-4.11.1/Viewer/ecflowUI/images/dock_restore.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/dock_restore.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/dock_restore.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/down_arrow.svg ecflow-4.11.1/Viewer/ecflowUI/images/down_arrow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/down_arrow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/down_arrow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/drawer_close.svg ecflow-4.11.1/Viewer/ecflowUI/images/drawer_close.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/drawer_close.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/drawer_close.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/drawer_open.svg ecflow-4.11.1/Viewer/ecflowUI/images/drawer_open.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/drawer_open.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/drawer_open.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/editcopy.svg ecflow-4.11.1/Viewer/ecflowUI/images/editcopy.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/editcopy.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/editcopy.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/editpaste.svg ecflow-4.11.1/Viewer/ecflowUI/images/editpaste.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/editpaste.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/editpaste.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,185 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/edit.svg ecflow-4.11.1/Viewer/ecflowUI/images/edit.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/edit.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/edit.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,206 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/error.svg ecflow-4.11.1/Viewer/ecflowUI/images/error.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/error.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/error.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/exit.svg ecflow-4.11.1/Viewer/ecflowUI/images/exit.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/exit.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/exit.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,141 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/expression.svg ecflow-4.11.1/Viewer/ecflowUI/images/expression.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/expression.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/expression.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,92 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + {e} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/favourite_empty.svg ecflow-4.11.1/Viewer/ecflowUI/images/favourite_empty.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/favourite_empty.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/favourite_empty.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/favourite.svg ecflow-4.11.1/Viewer/ecflowUI/images/favourite.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/favourite.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/favourite.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filesaveas.svg ecflow-4.11.1/Viewer/ecflowUI/images/filesaveas.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filesaveas.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filesaveas.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,313 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + ecFlowUI icon + + + ecFlowUI icon + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filesave.svg ecflow-4.11.1/Viewer/ecflowUI/images/filesave.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filesave.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filesave.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor_green.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor_green.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor_green.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor_green.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor_red.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor_red.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor_red.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor_red.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_decor.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_decor.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,157 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_edit.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_edit.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_edit.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_edit.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_match.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_match.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_match.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_match.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + M + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter_no_match.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter_no_match.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter_no_match.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter_no_match.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,169 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + N + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/filter.svg ecflow-4.11.1/Viewer/ecflowUI/images/filter.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/filter.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/filter.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,173 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/fontsize_down.svg ecflow-4.11.1/Viewer/ecflowUI/images/fontsize_down.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/fontsize_down.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/fontsize_down.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + A + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/fontsize_up.svg ecflow-4.11.1/Viewer/ecflowUI/images/fontsize_up.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/fontsize_up.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/fontsize_up.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + A + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/font.svg ecflow-4.11.1/Viewer/ecflowUI/images/font.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/font.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/font.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + T + + + T + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/genvar_shadow.svg ecflow-4.11.1/Viewer/ecflowUI/images/genvar_shadow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/genvar_shadow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/genvar_shadow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + G + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/genvar.svg ecflow-4.11.1/Viewer/ecflowUI/images/genvar.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/genvar.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/genvar.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + G + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/goto_line.svg ecflow-4.11.1/Viewer/ecflowUI/images/goto_line.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/goto_line.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/goto_line.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,321 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + 123 + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/grey_info.svg ecflow-4.11.1/Viewer/ecflowUI/images/grey_info.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/grey_info.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/grey_info.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + i + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Apply.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Apply.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_calendar.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_calendar.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_calendar.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_calendar.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_calendar.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_calendar.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,259 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + 7 + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Chat.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Chat.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Check.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Check.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_children_hidden.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_children_hidden.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_children_hidden.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_children_hidden.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,412 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_clock_free.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_clock_free.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_clock_free.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_clock_free.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_clock.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_clock.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_clock.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_clock.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_clock.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_clock.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,283 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_complete.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_complete.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Complete.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Complete.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_complete.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_complete.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_complete.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_complete.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_defstatus.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_defstatus.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Edit.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Edit.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_folded.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_folded.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Info.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Info.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Jobstatus.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Jobstatus.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Josstatus3.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Josstatus3.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_killed.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_killed.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_killed.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_killed.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,207 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + K + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_late.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_late.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_late.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_late.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_late.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_late.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_limit0.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_limit0.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_limit1.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_limit1.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_limit2.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_limit2.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Load.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Load.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_locked.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_locked.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Manual.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Manual.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_memo.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_memo.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Merge.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Merge.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_message.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_message.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Messages.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Messages.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_message.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_message.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_message.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_message.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,295 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_migrated.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_migrated.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_node_log.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_node_log.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_node_log.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_node_log.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + N + + N + N + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_noway.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_noway.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Output.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Output.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_QuickFind.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_QuickFind.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_rerun.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_rerun.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_rerun.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_rerun.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_rerun.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_rerun.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Script.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Script.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Search.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Search.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_slow.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_slow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_slow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_slow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,236 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Status.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Status.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Submit.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Submit.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Time_line.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Time_line.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Triggers.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Triggers.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Update.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Update.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Use_external_editor.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Use_external_editor.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Use_external_viewer.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Use_external_viewer.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Variables.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Variables.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_waiting.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_waiting.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_waiting.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_waiting.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_waiting.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_waiting.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,291 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Why_.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Why_.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_W.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_W.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Zbw.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Zbw.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/icon_zombie.svg ecflow-4.11.1/Viewer/ecflowUI/images/icon_zombie.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/icon_zombie.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/icon_zombie.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + Z + + + + + Z + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/icon_Z.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/icon_Z.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/info.svg ecflow-4.11.1/Viewer/ecflowUI/images/info.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/info.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/info.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,234 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + i + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/job.svg ecflow-4.11.1/Viewer/ecflowUI/images/job.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/job.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/job.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/large_file_search.svg ecflow-4.11.1/Viewer/ecflowUI/images/large_file_search.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/large_file_search.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/large_file_search.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,163 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + ! + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/log_error.svg ecflow-4.11.1/Viewer/ecflowUI/images/log_error.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/log_error.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/log_error.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,218 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/log_info.svg ecflow-4.11.1/Viewer/ecflowUI/images/log_info.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/log_info.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/log_info.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + i + + + + + i + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/logo.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/logo.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/logo_small.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/logo_small.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/logo.svg ecflow-4.11.1/Viewer/ecflowUI/images/logo.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/logo.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/logo.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,111 @@ + + + ecflow logo + + + + image/svg+xml + + ecflow logo + + + ECMWF + + + en-GB + + + ecflow icon + + + ecflow icon + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/manage_server.svg ecflow-4.11.1/Viewer/ecflowUI/images/manage_server.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/manage_server.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/manage_server.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,763 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Ecflowview icon + + + Ecflowview icon + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/manual_ori.svg ecflow-4.11.1/Viewer/ecflowUI/images/manual_ori.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/manual_ori.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/manual_ori.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,426 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/manual.svg ecflow-4.11.1/Viewer/ecflowUI/images/manual.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/manual.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/manual.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,126 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/menu_arrow_down.svg ecflow-4.11.1/Viewer/ecflowUI/images/menu_arrow_down.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/menu_arrow_down.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/menu_arrow_down.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/menu.svg ecflow-4.11.1/Viewer/ecflowUI/images/menu.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/menu.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/menu.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/monitor.svg ecflow-4.11.1/Viewer/ecflowUI/images/monitor.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/monitor.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/monitor.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,155 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/node_log.svg ecflow-4.11.1/Viewer/ecflowUI/images/node_log.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/node_log.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/node_log.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,281 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + + N + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/notification.svg ecflow-4.11.1/Viewer/ecflowUI/images/notification.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/notification.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/notification.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,202 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/output.svg ecflow-4.11.1/Viewer/ecflowUI/images/output.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/output.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/output.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,226 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/overview.svg ecflow-4.11.1/Viewer/ecflowUI/images/overview.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/overview.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/overview.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,231 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + i + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/padlock.svg ecflow-4.11.1/Viewer/ecflowUI/images/padlock.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/padlock.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/padlock.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/path_arrow.svg ecflow-4.11.1/Viewer/ecflowUI/images/path_arrow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/path_arrow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/path_arrow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/reload_green.svg ecflow-4.11.1/Viewer/ecflowUI/images/reload_green.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/reload_green.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/reload_green.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,214 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/reload_one.svg ecflow-4.11.1/Viewer/ecflowUI/images/reload_one.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/reload_one.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/reload_one.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/reload.svg ecflow-4.11.1/Viewer/ecflowUI/images/reload.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/reload.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/reload.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/remove.svg ecflow-4.11.1/Viewer/ecflowUI/images/remove.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/remove.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/remove.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/rescue.svg ecflow-4.11.1/Viewer/ecflowUI/images/rescue.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/rescue.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/rescue.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/reset.svg ecflow-4.11.1/Viewer/ecflowUI/images/reset.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/reset.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/reset.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,282 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + ecFlow_ui icon + + + ecFlow_ui icon + + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/reset_to_default.svg ecflow-4.11.1/Viewer/ecflowUI/images/reset_to_default.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/reset_to_default.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/reset_to_default.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/script.svg ecflow-4.11.1/Viewer/ecflowUI/images/script.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/script.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/script.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,293 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/search_decor_large_file_mode.svg ecflow-4.11.1/Viewer/ecflowUI/images/search_decor_large_file_mode.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/search_decor_large_file_mode.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/search_decor_large_file_mode.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,352 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/search_decor.svg ecflow-4.11.1/Viewer/ecflowUI/images/search_decor.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/search_decor.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/search_decor.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/search.svg ecflow-4.11.1/Viewer/ecflowUI/images/search.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/search.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/search.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,150 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/select_all.svg ecflow-4.11.1/Viewer/ecflowUI/images/select_all.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/select_all.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/select_all.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,99 @@ + + + + + + + + + + + + image/svg+xml + + + ecFlowUi icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/server_log.svg ecflow-4.11.1/Viewer/ecflowUI/images/server_log.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/server_log.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/server_log.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,196 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + EcflowUI icon + + + EcflowUI icon + + + + + + + + S + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/server.svg ecflow-4.11.1/Viewer/ecflowUI/images/server.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/server.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/server.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,689 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Ecflowview icon + + + Ecflowview icon + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/show_shadowed.svg ecflow-4.11.1/Viewer/ecflowUI/images/show_shadowed.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/show_shadowed.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/show_shadowed.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + V + V + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/spinning_wheel.gif and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/spinning_wheel.gif differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/splash_screen.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/splash_screen.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/splash_screen.svg ecflow-4.11.1/Viewer/ecflowUI/images/splash_screen.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/splash_screen.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/splash_screen.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,943 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + ecFlowUI + + ecFlowUI + + + + + + + + + server + + suite 1 + + node 1 + + node 2 + + + suite 2 + + + suite 3 + Preview version Preview version + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/status.svg ecflow-4.11.1/Viewer/ecflowUI/images/status.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/status.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/status.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,222 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/submit_round.svg ecflow-4.11.1/Viewer/ecflowUI/images/submit_round.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/submit_round.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/submit_round.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,105 @@ + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + ECMWF + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/submit.svg ecflow-4.11.1/Viewer/ecflowUI/images/submit.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/submit.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/submit.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/sync_black.svg ecflow-4.11.1/Viewer/ecflowUI/images/sync_black.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/sync_black.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/sync_black.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,392 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/sync.svg ecflow-4.11.1/Viewer/ecflowUI/images/sync.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/sync.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/sync.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,401 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/system.svg ecflow-4.11.1/Viewer/ecflowUI/images/system.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/system.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/system.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + S + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/terminate.svg ecflow-4.11.1/Viewer/ecflowUI/images/terminate.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/terminate.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/terminate.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/tip.svg ecflow-4.11.1/Viewer/ecflowUI/images/tip.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/tip.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/tip.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + ECMWF + + + + + + + + + + Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/tree_branch_end.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/tree_branch_end.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/tree_branch_more.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/tree_branch_more.png differ Binary files /tmp/tmpJd2gxY/I8XVLZwrMO/ecflow-4.9.0/Viewer/ecflowUI/images/tree_vline.png and /tmp/tmpJd2gxY/a_TgVym_Lk/ecflow-4.11.1/Viewer/ecflowUI/images/tree_vline.png differ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/trigger_left_arrow.svg ecflow-4.11.1/Viewer/ecflowUI/images/trigger_left_arrow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/trigger_left_arrow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/trigger_left_arrow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/trigger_right_arrow.svg ecflow-4.11.1/Viewer/ecflowUI/images/trigger_right_arrow.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/trigger_right_arrow.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/trigger_right_arrow.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,109 @@ + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/trigger.svg ecflow-4.11.1/Viewer/ecflowUI/images/trigger.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/trigger.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/trigger.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/unknown.svg ecflow-4.11.1/Viewer/ecflowUI/images/unknown.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/unknown.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/unknown.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,198 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + ? + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/unselect_all.svg ecflow-4.11.1/Viewer/ecflowUI/images/unselect_all.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/unselect_all.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/unselect_all.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,93 @@ + + + + + + + + + + + + image/svg+xml + + + ecFlowUi icon + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/variable.svg ecflow-4.11.1/Viewer/ecflowUI/images/variable.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/variable.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/variable.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,219 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + V + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/warning.svg ecflow-4.11.1/Viewer/ecflowUI/images/warning.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/warning.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/warning.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,238 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/why.svg ecflow-4.11.1/Viewer/ecflowUI/images/why.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/why.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/why.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + ? + + + + + ? + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/images/zombie.svg ecflow-4.11.1/Viewer/ecflowUI/images/zombie.svg --- ecflow-4.9.0/Viewer/ecflowUI/images/zombie.svg 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/images/zombie.svg 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,220 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + ECMWF + + + en-GB + + + Metview icon + + + Metview icon + + + + + Z + + + + + Z + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/scripts/CMakeLists.txt ecflow-4.11.1/Viewer/ecflowUI/scripts/CMakeLists.txt --- ecflow-4.9.0/Viewer/ecflowUI/scripts/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/scripts/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,36 @@ + +# the list of files we want to install +set (files ecflow_ui ecflow_test_ui.sh ecflow_ui_node_state_diag.sh) + + +# set variables which will be expanded in the startup script +if(ENABLE_UI_BACKTRACE) + set(UI_BACKTRACE yes) +else() + set(UI_BACKTRACE no) +endif() + +if(NOT UI_BACKTRACE_EMAIL_ADDRESS_FILE) + set(UI_BACKTRACE_EMAIL_ADDRESS_FILE "x") +endif() + +if(NOT UI_LOG_FILE) + set(UI_LOG_FILE "x") +endif() + +if(NOT UI_LOG_SITE_TAG) + set(UI_LOG_SITE_TAG "x") +endif() + +# for each file, copy it into the build directory at build time +# and install it into the installation directory at install time (!) +foreach( f ${files} ) + configure_file(${f}.in ${CMAKE_BINARY_DIR}/bin/${f} @ONLY) + + + # ensure file is installed at install time + install( FILES ${CMAKE_BINARY_DIR}/bin/${f} + DESTINATION bin + PERMISSIONS OWNER_WRITE OWNER_READ GROUP_READ WORLD_READ OWNER_EXECUTE GROUP_EXECUTE WORLD_EXECUTE) + +endforeach() diff -Nru ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_test_ui.sh.in ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_test_ui.sh.in --- ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_test_ui.sh.in 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_test_ui.sh.in 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,151 @@ +#!/bin/bash + +#============================================================================ +# Copyright 2009-2017 ECMWF. +# This software is licensed under the terms of the Apache Licence version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. +#============================================================================ + + + +# ---------------------------------------------------------- +# Clean up the tmp directory at the end or on error +#----------------------------------------------------------- +cleanup(){ + status=$? + trap "" EXIT ERR + + echo "Shutting down server" + #$EXE_DIR/ecflow_client --delete=yes /operation_suite + #$EXE_DIR/ecflow_client --shutdown=yes + #$EXE_DIR/ecflow_client --halt=yes + $EXE_DIR/ecflow_client --terminate=yes + + echo "Cleaning up temporary directory" + + # remove temporary directory + if [ $(echo $ECFTEST_TMPDIR | grep -c "/ecflow_ui_test.") -ge 1 ] ; then + rm -Rf $ECFTEST_TMPDIR + fi + + echo "Done" + exit $status +} + + + + +# ============================================================= +# tmp directory cleaned automatically at end or at error. +# on new platforms check that these signals are valid: + +ECFLOWUI_SIGLIST="HUP INT QUIT TERM XCPU XFSZ" +# 1 2 3 15 30/24 31/25 + +trap 'cleanup "EXIT" $LINENO' EXIT +trap 'cleanup "SIGNAL trap" $LINENO' $ECFLOWUI_SIGLIST + + + + + +# Figure out the directory of the exe +EXE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +ETC_DIR="$EXE_DIR/../share/ecflow/etc" +DEF_FILE="$ETC_DIR/ecflow_ui_test.def" + + + +# ======================================================================= +# Create a temporary directory +# ======================================================================= + +# Define tmp root dir +TMPDIR_TEST_ROOT=${TMPDIR:=/tmp} + +# check that TMPDIR_ROOT actually exists - we can't run without it +if [ ! -d $TMPDIR_TEST_ROOT ] +then + echo "" + echo " Temporary directory '$TMPDIR_TEST_ROOT' does not exist" + echo " ecFlowUI needs this in order to run. Check that environment" + echo " variable \$TMPDIR is set to a valid directory and try again." + exit 1 +fi + +# Define and create tmp dir +# We add a subdirectory 'x' to the end because otherwise the temp dir inherits +# the time stamp from the .tar.gz that we uncompress there +export ECFTEST_TMPDIR=${TMPDIR_TEST_ROOT}/ecflow_ui_test.$$.$LOGNAME +mkdir -p $ECFTEST_TMPDIR + +# Check if we could create it +if [ $? -ne 0 ] +then + echo "" + echo " Temporary directory '$ECFTEST_TMPDIR' could not be created." + echo " ecFlowUI needs this in order to run. Check that environment" + echo " variable \$TMPDIR is set to a valid directory and try again." + exit 1 +fi + + + + + +# ======================================================================= +# Start server +# ======================================================================= + +export ECF_PORT=4949 +$EXE_DIR/ecflow_client --version + +rm -rf `hostname`.${ECF_PORT}.* + + +export PATH=${EXE_DIR}:${PATH} + +#export ECF_ALLOW_OLD_CLIENT_NEW_SERVER=9 +SLEEPTIME=4 +echo "Start server, port $ECF_PORT (and wait $SLEEPTIME seconds for it to start)" +$EXE_DIR/ecflow_server& +sleep $SLEEPTIME + + +# ======================================================================= +# Create suite definition +# ======================================================================= + +echo "Create suite definition in $ECFTEST_TMPDIR" +sed -e "s,ECF_HOME_PLACEHOLDER,${ECFTEST_TMPDIR}\/operation_suite,g" $DEF_FILE > $ECFTEST_TMPDIR/1.txt +sed -e "s,ECF_INCLUDE_PLACEHOLDER,${ECFTEST_TMPDIR}\/includes,g" $ECFTEST_TMPDIR/1.txt > $ECFTEST_TMPDIR/2.txt +sed -e "s,ECF_FILES_PLACEHOLDER,${ECFTEST_TMPDIR}\/operation_suite,g" $ECFTEST_TMPDIR/2.txt > $ECFTEST_TMPDIR/test_suite.def + +ECFTEST_SCRIPTS_HOME=$ECFTEST_TMPDIR +cd $ECFTEST_SCRIPTS_HOME +tar xzvf $ETC_DIR/ecflow_ui_test_server_scripts.tar.gz +cd - + +# ======================================================================= +# Load suite definition and start +# ======================================================================= + +echo "Load suite definition" +$EXE_DIR/ecflow_client --server_version +$EXE_DIR/ecflow_client --load=$ECFTEST_TMPDIR/test_suite.def +$EXE_DIR/ecflow_client --restart +$EXE_DIR/ecflow_client --begin=operation_suite + + +# ======================================================================= +# Launch ecFlowUI +# ======================================================================= + +echo "Starting ecFlowUI" +export ECFLOWUI_TEMP_SESSION_PRESERVE_CONFIRM="no" +$EXE_DIR/ecflow_ui -ts localhost $ECF_PORT + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_ui.in ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_ui.in --- ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_ui.in 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_ui.in 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,353 @@ +#!/bin/bash + +#============================================================================ +# Copyright 2009-2017 ECMWF. +# This software is licensed under the terms of the Apache Licence version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. +#============================================================================ + +# Environment variables from the configuration step +ECFLOWUI_BT=@UI_BACKTRACE@ +ECFLOWUI_BT_EMAIL_ADDRESS_FILE=@UI_BACKTRACE_EMAIL_ADDRESS_FILE@ +ECFLOWUI_VERSION=@ECFLOW_VERSION_STR@ +ECFLOWUI_INSTALLDIR=@CMAKE_INSTALL_PREFIX@ +ECFLOWUI_USAGE_LOG=@UI_LOG_FILE@ +ECFLOWUI_SITE_TAG=@UI_LOG_SITE_TAG@ + +# ---------------------------------------------------------- +# Clean up the tmp directory at the end or on error +#----------------------------------------------------------- +cleanup(){ + status=$? + trap "" EXIT ERR + set +e # do not exit on errors during cleanup + + # restore stdout/stderr + if [ x$ECFLOWUI_SLOG != xyes ] + then + exec 1>&5 + exec 2>&6 + fi + + if [ "$status" -eq "0" ] + then + echo "$(basename $0): $1 (OK) (line $2), exit status $status, starting 'cleanup'" + else + status_message="$(basename $0): $1 on ERROR (line $2), exit status $status, starting 'cleanup'" + echo $status_message + + if [ -e $ECFLOWUI_LOGFILE ] ; then + + # if the error message is something that's not really a crash, then we should + # not send a crash report + hhh=$(grep "The X11 connection broke" $ECFLOWUI_LOGFILE) # 0 if x11 broke, 1 if not + x11notbroke=$? + + if [ "$x11notbroke" = "1" ] ; then + + TAILNUM=50 + UI_TAILNUM=50 + + if [ $ECFLOWUI_BT != "no" ] ; then + # find the backtrace tag if there + btline=$(grep -n "Backtrace:" $ECFLOWUI_LOGFILE | cut -f1 --delimiter=':') + gret=$? + if [ "$gret" = "0" -a "x$btline" != "x" ] ; then + nlines=$(wc -l $ECFLOWUI_LOGFILE | cut -f1 --delimiter=' ') + TAILNUM=$(expr $nlines - $btline + 100) + fi + fi + + + # Logic: ECFLOWUI_BT_EMAIL_ADDRESS_FILE is not set, then the log/backtrace + # will go to stdout; otherwise it will be e-mailed to the + # addresses instead (the user will not see the backtrace). + + if [ $ECFLOWUI_BT_EMAIL_ADDRESS_FILE != "x" ] ; then + if [ -e $ECFLOWUI_BT_EMAIL_ADDRESS_FILE ] ; then + EFLOWUI_LOGTAIL="${ECFLOWUI_LOGFILE}.tail" + ecfuimsg="ecFlowUI backtrace from user $USER" + echo "Sending crash report by e-mail..." + echo "user/host : $USER/$HOST" > $EFLOWUI_LOGTAIL + echo "ecflow version : $ECFLOWUI_VERSION" >> $EFLOWUI_LOGTAIL + echo "ecflow install dir : $ECFLOWUI_INSTALLDIR" >> $EFLOWUI_LOGTAIL + echo "start time : $ECFLOWUI_STARTTIME" >> $EFLOWUI_LOGTAIL + echo "stop time : $(date)" >> $EFLOWUI_LOGTAIL + echo "" >> $EFLOWUI_LOGTAIL + echo $status_message >> $EFLOWUI_LOGTAIL + tail -$TAILNUM $ECFLOWUI_LOGFILE >> $EFLOWUI_LOGTAIL + + #Add the last lines from the user interaction log file + if [ -e $ECFLOWUI_LOGFILE ] ; then + echo "" >> $EFLOWUI_LOGTAIL + echo "Last $UI_TAILNUM lines of user interaction log file:" >> $EFLOWUI_LOGTAIL + tail -${UI_TAILNUM} $ECFLOWUI_LOGFILE >> $EFLOWUI_LOGTAIL + fi + + addr=$(head -1 $ECFLOWUI_BT_EMAIL_ADDRESS_FILE) + mail -s "$ecfuimsg" $addr < $EFLOWUI_LOGTAIL + rm -f $EFLOWUI_LOGTAIL + echo "Report sent" + else + echo "No report sent because e-mail address file does not exist: $ECFLOWUI_BT_EMAIL_ADDRESS_FILE" + fi + else + echo "Last $TAILNUM lines of log file:" + tail -$TAILNUM $ECFLOWUI_LOGFILE + + #Add the last lines from the user interaction log file + if [ -e $ECFLOWUI_LOGFILE ] ; then + echo "" + echo "Last $UI_TAILNUM lines of user interaction log file:" + tail -${UI_TAILNUM} $ECFLOWUI_LOGFILE + fi + fi + else + echo "X11 Connection broke - no report sent to developers" + fi # $x11notbroke + fi + fi + + + + # remove temporary directory + if [ $(echo $ECFLOWUI_TMPDIR | grep -c "/ecflow_ui.") -ge 1 ] ; then + rm -Rf $ECFLOWUI_TMPDIR + fi + + exit $status +} + + + +#----------------------------------------------------------- +# checkoption() +# Checks the command-line options +#----------------------------------------------------------- + +checkoption() { + +while [ "$1" != "" ] +do + case "$1" in + + -log) + ECFLOWUI_SLOG=yes + ;; + + -slog) + ECFLOWUI_SLOG=yes + ;; + + -s) + export ECFLOWUI_SESSION_MANAGER=yes + ;; + + -ts) + export ECFLOWUI_TEMP_SESSION_HOST=$2 + export ECFLOWUI_TEMP_SESSION_PORT=$3 + if [ "x$ECFLOWUI_TEMP_SESSION_HOST" == x -o "x$ECFLOWUI_TEMP_SESSION_PORT" == x ] + then + echo "Please supply a host and port:" + echo "ecflow_ui -ts " + exit 1 + fi + echo "Starting a temporary session: host=$ECFLOWUI_TEMP_SESSION_HOST, port=$ECFLOWUI_TEMP_SESSION_PORT" + shift 2 + ;; + + -title) + export ECFLOWUI_TITLE=$2 + shift + ;; + + #Specify the location of the config dir. It is used for ui testing only. + #We do not expose it in the usage() print out!! + -confd) + export ECFLOWUI_CONFIG_DIR=$2 + if [[ $ECFLOWUI_CONFIG_DIR != /* ]] ; then + echo "Please specify an absolute path for flag -confd!" + exit 1 + fi + echo "Starting ecFlowUI with user specified config dir: ${ECFLOWUI_CONFIG_DIR}" + shift + ;; + + -h) + ECFLOWUI_HELP=yes + ;; + + esac + shift +done +} + + + +checkoption "$@" + + + +#----------------------------------------------------------- +# Help message +#----------------------------------------------------------- + +if [ "x$ECFLOWUI_HELP" != x ] +then + + cat < : start a temporary session viewing the given server + -h : print this help message +HEND + + exit 0 +fi + + +#----------------------------------------------------------- +# usage log +#----------------------------------------------------------- + +set +e # failing to write to the log file should not cause a fatal error +if [ $ECFLOWUI_USAGE_LOG != x ] +then + # checks on file existence and size + if [ -f $ECFLOWUI_USAGE_LOG ] # log file exists? + then + if [ ! -s $ECFLOWUI_USAGE_LOG ] # file is empty? - write the header line + then + echo "date,time,version,user,host,site" >> $ECFLOWUI_USAGE_LOG + fi + + echo "`date -u '+%Y.%m.%d,%R'`,$ECFLOWUI_VERSION,$USER,`hostname`,$ECFLOWUI_SITE_TAG" >> $ECFLOWUI_USAGE_LOG + else + echo "Log file $ECFLOWUI_USAGE_LOG does not exist." + fi +fi +set -e + + + +#Define tmp root dir +TMPDIR_ROOT=${TMPDIR:=/tmp} + +# check that TMPDIR_ROOT actually exists - we can't run without it +if [ ! -d $TMPDIR_ROOT ] +then + echo "" + echo " Temporary directory '$TMPDIR_ROOT' does not exist" + echo " ecFlowUI needs this in order to run. Check that environment" + echo " variable \$TMPDIR is set to a valid directory and try again." + exit 1 +fi + +#Define and create tmp dir +export ECFLOWUI_TMPDIR=${TMPDIR_ROOT}/ecflow_ui.$$.$LOGNAME +mkdir -p $ECFLOWUI_TMPDIR + +#Check if we could create it +if [ $? -ne 0 ] +then + echo "" + echo " Temporary directory '$ECFLOWUI_TMPDIR' could not be created." + echo " ecFlowUI needs this in order to run. Check that environment" + echo " variable \$TMPDIR is set to a valid directory and try again." + exit 1 +fi + + +# Where to write the log file to +export git=$ECFLOWUI_TMPDIR/ecflowui_log_$$.$LOGNAME.txt + +# Where to write the user interaction log file to +export ECFLOWUI_LOGFILE=$ECFLOWUI_TMPDIR/ecflowui_uilog_$$.$LOGNAME.txt + +#--------------------------------------------------------------- +# Create socket dir for local domain socket base communication +#--------------------------------------------------------------- + +export ECFLOWUI_SOCKETDIR=$ECFLOWUI_TMPDIR/sockets +mkdir -p $ECFLOWUI_SOCKETDIR + +#--------------------------------------------------------------- +# Remove lock files created more than 3 s ago (see ECFLOW-980) +#--------------------------------------------------------------- + +confDir=${ECFLOWUI_CONFIG_DIR:=${HOME}/.ecflow_ui} +#We are extra careful an delete the files in a loop instead of using exec for find +for fn in $(find $confDir -type f -cmin +0.05 -name *.lock) ; do + if [[ $fn == *.lock ]] ; then + echo "Remove lock file: ${fn}" + rm -f $fn + fi +done + +#----------------------------------------------------------- +# Close tty and redirect stdout to a file? +#----------------------------------------------------------- + +if [ x$ECFLOWUI_SLOG != xyes ] +then + exec < /dev/null + exec 5>&1 # store for later + exec 6>&2 # store for later + exec 1> $ECFLOWUI_LOGFILE # (restored in cleanup()) + exec 2>&1 # redirect stderr to stdout, (restored in cleanup()) + # note that it seems to be better to do + # exec 2>&1 + # rather than + # exec 2> $ECFLOWUI_LOGFILE + # because this way we get the stderr at the end rather than the start + # due to the difference in flushing behaviours +fi + + + +# ============================================================= +# tmp directory cleaned automatically at end or at error. +# on new platforms check that these signals are valid: + +ECFLOWUI_SIGLIST="QUIT TERM XCPU XFSZ" +# 3 15 30/24 31/25 + +trap 'cleanup "EXIT" $LINENO' EXIT +trap 'cleanup "SIGNAL trap" $LINENO' $ECFLOWUI_SIGLIST + +#Figure out the directory of the exe +EXE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + + +# note when we started it up +ECFLOWUI_STARTTIME=$(date) + + +#----------------------------------------------------------- +# The log viewer exe +#----------------------------------------------------------- + +ECFLOWUI_LOG_VIEWER=${EXE_DIR}/ecflow_ui_log.x + +#----------------------------------------------------------- +# Run the executable +#----------------------------------------------------------- + +exe=${EXE_DIR}/ecflow_ui.x + +#----------------------------------------------------------- +# Backtrace report? +#----------------------------------------------------------- + +if [ $ECFLOWUI_BT != "no" ] +then + catchsegv $exe +else + ${exe} +fi + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_ui_node_state_diag.sh.in ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_ui_node_state_diag.sh.in --- ecflow-4.9.0/Viewer/ecflowUI/scripts/ecflow_ui_node_state_diag.sh.in 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/scripts/ecflow_ui_node_state_diag.sh.in 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,30 @@ +#!/bin/bash + +#============================================================================ +# Copyright 2009-2018 ECMWF. +# This software is licensed under the terms of the Apache Licence version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. +#============================================================================ + +if [[ $# -ne 5 ]] ; then + echo "Error: wrong number of arguments = $# (must be 5)" + exit 1 +fi + +uiDefs=$1 +host=$2 +port=$3 +nodePath=$4 +serverDefs=$5 + +echo "Get defs via ecflow_client:" +echo "ecflow_client --host=${host} --port=${port} --migrate ${nodePath} > ${serverDefs}" +ecflow_client --host=${host} --port=${port} --migrate ${nodePath} > ${serverDefs} + +echo "Compare defs with meld:" +echo "meld ${uiDefs} ${serverDefs} &" +meld ${uiDefs} ${serverDefs} & + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/scripts/generate_ui_test_scripts.sh ecflow-4.11.1/Viewer/ecflowUI/scripts/generate_ui_test_scripts.sh --- ecflow-4.9.0/Viewer/ecflowUI/scripts/generate_ui_test_scripts.sh 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/scripts/generate_ui_test_scripts.sh 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,34 @@ +#!/bin/bash + +#============================================================================ +# Copyright 2009-2017 ECMWF. +# This software is licensed under the terms of the Apache Licence version 2.0 +# which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +# In applying this licence, ECMWF does not waive the privileges and immunities +# granted to it by virtue of its status as an intergovernmental organisation +# nor does it submit to any jurisdiction. +#============================================================================ + + +echo "Create suite definition" + +rm -rf ui_test_gen +mkdir -p ui_test_gen +cd ui_test_gen + + +# Figure out the directory of the exe +EXE_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +echo "$EXE_DIR" +ETC_DIR="$EXE_DIR/../../share/ecflow/etc" +DEF_FILE="$ETC_DIR/ecflow_ui_test.def" + +ECFTEST_SCRIPTS_HOME="." +TBPY=$EXE_DIR/../../Doc/online/cookbook/src/test_bench.py +unset WK # otherwise it will be used by the Python script +export ECF_PORT=4949 +python $TBPY --port $ECF_PORT --ecf_home $ECFTEST_SCRIPTS_HOME $DEF_FILE + +tar -pzcf ../ecflow_ui_test_server_scripts.tar.gz operation_suite includes + +echo " ** Now copy ecflow_ui_test_server_scripts.tar.gz into $ETC_DIR **" diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,115 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AboutDialog.hpp" + +#include "DirectoryHandler.hpp" +#include "Version.hpp" +#include "WidgetNameProvider.hpp" + +#include +#include +#include +#include + +AboutDialog::AboutDialog(QWidget* parent) : QDialog(parent) +{ + setupUi(this); + + QString title="EcflowUI"; + QString ecfVersionTxt=QString::fromStdString(ecf::Version::raw()); + QString desc=QString::fromStdString(ecf::Version::description()); + QString descTxt="ecflow version: " + ecfVersionTxt; + + int pos=0; + QRegExp rx("boost\\((\\S+)\\)"); + if((pos = rx.indexIn(desc, pos)) != -1) + { + descTxt+="
boost version: " + rx.cap(1); + } + + rx=QRegExp("compiler\\(([^\\)]+)\\)"); + if((pos = rx.indexIn(desc, pos)) != -1) + { + descTxt+="
compiler: " + rx.cap(1); + } + + rx=QRegExp("protocol\\((\\S+)\\)"); + if((pos = rx.indexIn(desc, pos)) != -1) + { + descTxt+="
protocol: " + rx.cap(1); + } + + descTxt+="
compiled on: " +desc.section("Compiled on",1,1); + + const char *qtv=qVersion(); + if(qtv) + { + descTxt+="
Qt version: " + QString(qtv); + } + + QString logoTxt="    
"; + logoTxt+="

" + title + "

"; + if(!ecfVersionTxt.isEmpty()) + { + logoTxt+="

ecflow version: " + ecfVersionTxt + "
"; + logoTxt+="Copyright 2009-" + QString::number(QDate::currentDate().year()) + " ECMWF

"; + } + + logoTxt+="

"; + logoLabel_->setText(logoTxt); + + versionLabel_->setText(descTxt); + + QString licenseText="Copyright 2009-" + QString::number(QDate::currentDate().year()) + " ECMWF."; + licenseText+=" This software is licensed under the terms of the Apache Licence version 2.0"; + licenseText+=" which can be obtained at http://www.apache.org/licenses/LICENSE-2.0."; + licenseText+=" In applying this licence, ECMWF does not waive the privileges and immunities"; + licenseText+=" granted to it by virtue of its status as an intergovernmental organisation"; + licenseText+=" nor does it submit to any jurisdiction."; + + licenseLabel_->setText(licenseText); + + WidgetNameProvider::nameChildren(this); + + //Paths + std::string pathText="Log file: "; + + if(DirectoryHandler::uiLogFileName().empty()) + pathText+= "not defined (log is written to stdout)
"; + else + pathText+=DirectoryHandler::uiLogFileName() + "
"; + + pathText+="UI event log file: " + DirectoryHandler::uiEventLogFileName() +"
"; + pathText+="Socket directory: " + DirectoryHandler::socketDir() +"
"; + + pathLabel_->setText(QString::fromStdString(pathText)); + + //Env vars + envTree_->setRootIsDecorated(false); + envTree_->setColumnCount(2); + QStringList envCols; + envCols << "Variable" << "Value"; + envTree_->setHeaderLabels(envCols); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + Q_FOREACH(QString envKey,env.keys()) + { + if(envKey.startsWith("ECFLOWUI_")) + { + QString envVal=env.value(envKey); + QTreeWidgetItem* item=new QTreeWidgetItem(envTree_); + item->setText(0,envKey); + item->setText(1,envVal); + } + } + + envTree_->resizeColumnToContents(0); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,25 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ABOUTDIALOG_INC_ +#define ABOUTDIALOG_INC_ + +#include + +#include "ui_AboutDialog.h" + +class AboutDialog : public QDialog, protected Ui::AboutDialog +{ +public: + explicit AboutDialog(QWidget *parent=0); +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/AboutDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AboutDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,239 @@ + + + AboutDialog + + + + 0 + 0 + 538 + 312 + + + + + 0 + 0 + + + + About EcflowUI + + + true + + + + + + + 0 + 0 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 255 + 255 + + + + + + + + true + + + QFrame::StyledPanel + + + logoLabel_ + + + + + + + 2 + + + + Version + + + + + + version + + + 10 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + License + + + + + + license + + + true + + + 10 + + + Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + + Paths + + + + + + pathLabel_ + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + + Environment + + + + + + + 1 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AboutDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox + rejected() + AboutDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,175 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "AbstractNodeModel.hpp" + +#include + + +#include "ServerFilter.hpp" +#include "ServerHandler.hpp" +#include "ServerItem.hpp" +#include "VFilter.hpp" +#include "VModelData.hpp" +#include "VNState.hpp" + + +AbstractNodeModel::AbstractNodeModel(QObject *parent) : + QAbstractItemModel(parent), + active_(false) +{ + //At this point the model is not active and it cannot see its data! +} + +AbstractNodeModel::~AbstractNodeModel() +{ + clean(); +} + +void AbstractNodeModel::active(bool active) +{ + if(active_ != active) + { + active_=active; + + beginResetModel(); + + data()->setActive(active_); + +#if 0 + //When the model becomes active we reload everything + if(active_) + { + data()->runFilter(false); + //init(); + + //Initialises the filter + //resetStateFilter(false); + } + + //When the model becomes inactive we clean it and + //release all the resources + else + { + data()->clear(); //clean(); + } +#endif + endResetModel(); + + //After finishing reset the view will automatically be notified about + //the changes. Also the filter model will be notified! + } +} + +//Called when the list of servers to be displayed has changed. +//void AbstractNodeModel::notifyConfigChanged(ServerFilter*) +//{ +// if(active_) +// reload(); +//} + +void AbstractNodeModel::init() +{ + /*ServerFilter *filter=config_->serverFilter(); + for(unsigned int i=0; i < filter->items().size(); i++) + { + if(ServerHandler *server=filter->items().at(i)->serverHandler()) + { + //The model has to observe the nodes o the server. + server->addNodeObserver(this); + + //The model stores the servers it has to deal with in a local object. + servers_->add(server,makeFilter()); + } + }*/ +} + +void AbstractNodeModel::clean() +{ + /*for(int i=0; i < servers_->count(); i++) + { + if(ServerHandler *s=servers_->server(i)) + { + s->removeNodeObserver(this); + } + } + + servers_->clear();*/ +} + +//TODO!!!!! + +//Should be reviewed what it is actually doing!!!!!!!!!!!!!!!!! +void AbstractNodeModel::reload() +{ + if(active_) + { + beginResetModel(); + //data_->reload(); + //clean(); + //init(); + //resetStateFilter(false); //do not emit change signal + endResetModel(); + } +} + + +bool AbstractNodeModel::hasData() const +{ + return (active_ && data()->count() > 0); +} + +void AbstractNodeModel::dataIsAboutToChange() +{ + beginResetModel(); +} + +void AbstractNodeModel::slotFilterDeleteBegin() +{ + beginResetModel(); +} + + +void AbstractNodeModel::slotFilterDeleteEnd() +{ + endResetModel(); +} + + +//---------------------------------------------- +// +// Server to index mapping and lookup +// +//---------------------------------------------- + +QModelIndex AbstractNodeModel::infoToIndex(VInfo_ptr info,int column) const +{ + if(info) + { + if(info->isServer()) + { + if(ServerHandler *s=info->server()) + { + return serverToIndex(s); + } + } + else if(info->isNode()) + { + VNode* n=info->node(); + return nodeToIndex(n); + } + else if(info->isAttribute()) + { + VAttribute* a=info->attribute(); + return attributeToIndex(a); + } + } + + return QModelIndex(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,88 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef ABSTRACTNODEMODEL_H +#define ABSTRACTNODEMODEL_H + +#include + +#include "Aspect.hpp" +#include "NodeObserver.hpp" +#include "VInfo.hpp" + +class Node; + +class IconFilter; +class VModelData; +class VModelServer; +class VParamSet; + +class AbstractNodeModel; + +class AbstractNodeModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit AbstractNodeModel(QObject *parent=0); + virtual ~AbstractNodeModel(); + + enum CustomItemRole {FilterRole = Qt::UserRole+1, IconRole = Qt::UserRole+2, + ServerRole = Qt::UserRole+3, NodeNumRole = Qt::UserRole+4, + InfoRole = Qt::UserRole+5, LoadRole = Qt::UserRole+6, + ConnectionRole = Qt::UserRole+7, ServerDataRole = Qt::UserRole+8, + NodeDataRole = Qt::UserRole+9, AttributeRole = Qt::UserRole+10, + AttributeLineRole = Qt::UserRole+11, AbortedReasonRole = Qt::UserRole + 12, + NodeTypeRole = Qt::UserRole + 13, NodeTypeForegroundRole = Qt::UserRole + 14, + ServerPointerRole = Qt::UserRole + 15, SortRole = Qt::UserRole + 16, + NodePointerRole = Qt::UserRole + 17, VariableRole = Qt::UserRole + 18}; + + void dataIsAboutToChange(); + virtual VInfo_ptr nodeInfo(const QModelIndex& index)=0; + void reload(); + void active(bool); + bool active() const {return active_;} + + virtual VModelData* data() const = 0; + virtual QModelIndex infoToIndex(VInfo_ptr,int column=0) const; + virtual QModelIndex nodeToIndex(const VNode*,int column=0) const=0; + virtual QModelIndex attributeToIndex(const VAttribute* a, int column=0) const=0; + +Q_SIGNALS: + void changed(); + void filterChanged(); + void rerender(); + +public Q_SLOTS: + void slotFilterDeleteBegin(); + void slotFilterDeleteEnd(); + + virtual void slotServerAddBegin(int row)=0; + virtual void slotServerAddEnd()=0; + virtual void slotServerRemoveBegin(VModelServer*,int)=0; + virtual void slotServerRemoveEnd(int)=0; + + virtual void slotDataChanged(VModelServer*)=0; + virtual void slotBeginServerScan(VModelServer* server,int)=0; + virtual void slotEndServerScan(VModelServer* server,int)=0; + virtual void slotBeginServerClear(VModelServer* server,int)=0; + virtual void slotEndServerClear(VModelServer* server,int)=0; + +protected: + void init(); + void clean(); + bool hasData() const; + + virtual void resetStateFilter(bool broadcast) {} + virtual QModelIndex serverToIndex(ServerHandler*) const=0; + + bool active_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1315 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AbstractNodeView.hpp" + +#include "Animation.hpp" +#include "ExpandState.hpp" +#include "TreeNodeModel.hpp" +#include "TreeNodeViewDelegate.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define _UI_QABSTRACTNODEVIEW_DEBUG + +AbstractNodeView::AbstractNodeView(TreeNodeModel* model,QWidget* parent) : + QAbstractScrollArea(parent), + model_(model), + verticalScrollMode_(ScrollPerItem), + rowCount_(0), + maxRowWidth_(0), + topMargin_(4), + leftMargin_(4), + itemGap_(12), + connectorGap_(1), + expandConnectorLenght_(20), + connectorColour_(Qt::black), + drawConnector_(true), + autoExpandLeafNode_(true), + indentation_(0), + lastViewedItem_(0), + noSelectionOnMousePress_(false), + autoScroll_(true) +{ + expandConnectorLenght_=itemGap_-2*connectorGap_; + + setContextMenuPolicy(Qt::CustomContextMenu); + + viewport()->setBackgroundRole(QPalette::Window); + + //We attach the model. + attachModel(); + + //We should call reset here but it has a pure virtual method, + //so cannot be called from the constructor. We need to call it from + //the constructor of the derived classes + + delegate_=new TreeNodeViewDelegate(model_,this); +} + +AbstractNodeView::~AbstractNodeView() +{ + +} + +//Connect the models signal to the view. Must only be called once!! +void AbstractNodeView::attachModel() +{ + //Standard signals from the model + connect(model_,SIGNAL(modelReset()), + this,SLOT(reset())); + + connect(model_,SIGNAL(rowsInserted(QModelIndex,int,int)), + this, SLOT(rowsInserted(QModelIndex,int,int))); + + connect(model_,SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), + this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); + + connect(model_,SIGNAL(rowsRemoved(QModelIndex,int,int)), + this, SLOT(rowsRemoved(QModelIndex,int,int))); + + connect(model_,SIGNAL(dataChanged(const QModelIndex&,const QModelIndex&)), + this,SLOT(dataChanged(const QModelIndex&,const QModelIndex&))); + + //The selection model + selectionModel_ = new QItemSelectionModel(model_, this); + connect(model_, SIGNAL(destroyed()), selectionModel_, SLOT(deleteLater())); + + connect(selectionModel_, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SLOT(selectionChanged(QItemSelection,QItemSelection))); + + //broadcast the selection change + connect(selectionModel_, SIGNAL(selectionChanged(QItemSelection,QItemSelection)), + this, SIGNAL(selectionChangedInView(QItemSelection,QItemSelection))); + + connect(selectionModel_, SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(currentChanged(QModelIndex,QModelIndex))); + +} + +void AbstractNodeView::mousePressEvent(QMouseEvent* event) +{ + //When the expand indicator is pressed + if(event->button() == Qt::LeftButton) + { + int viewItemIndex=itemAtCoordinate(event->pos()); + if(viewItemIndex != -1 && viewItems_[viewItemIndex].hasChildren) + { + if(isPointInExpandIndicator(viewItemIndex,event->pos())) + { + if(viewItems_[viewItemIndex].expanded) + { + collapse(viewItemIndex); + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } + else + { + expand(viewItemIndex); + } + return; + } + } + } + + //Middle button - expand only, no selection + else if(event->button() == Qt::MidButton) + { + int viewItemIndex=itemAtCoordinate(event->pos()); + if(viewItemIndex != -1 && viewItems_[viewItemIndex].hasChildren) + { +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << " midbutton index=" << viewItemIndex << " name=" << + viewItems_[viewItemIndex].index.data().toString(); +#endif + if(viewItems_[viewItemIndex].expanded) + { + collapse(viewItemIndex); + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } + else + { + expand(viewItemIndex); + } + } + return; + } + + //No selection for context menu. Works on linux but can be platform dependent!!! + else if(event->button() == Qt::RightButton) + { + return; + } + + QPoint pos = event->pos(); + QPersistentModelIndex index = indexAt(pos); + + pressedIndex_ = index; + + //Get the selection flags + QItemSelectionModel::SelectionFlags command = selectionCommand(index, event); + + noSelectionOnMousePress_ = command == QItemSelectionModel::NoUpdate || !index.isValid(); + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "TreeNodeViewBase::mousePressEvent --> current=" << currentIndex().data().toString() << + " pressed=" << pressedIndex_.data().toString() << + " pos=" << pos << " pressedRef=" << pressedRefIndex_.data().toString(); +#endif + + if((command & QItemSelectionModel::Current) == 0) + pressedRefIndex_ = index; + else if(!pressedRefIndex_.isValid()) + pressedRefIndex_ = currentIndex(); + + QPoint pressedRefPosition=visualRect(pressedRefIndex_).center(); + + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << " pressedRefPosition=" << pressedRefPosition << " visrect=" << visualRect(currentIndex()) << + " center=" << visualRect(currentIndex()).center() << " pressedRef=" << indexAt(pressedRefPosition).data().toString() << + " pressedRef=" << pressedRefIndex_.data().toString(); +#endif + + if(index.isValid()) + { + bool autoScroll=autoScroll_; + autoScroll_=false; + selectionModel_->setCurrentIndex(index, QItemSelectionModel::NoUpdate); + autoScroll_=autoScroll; + QPoint p1=pressedRefPosition; + QRect rect(p1,QSize(pos.x()-p1.x(),pos.y()-p1.y())); +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << " rect=" << rect << " p1=" << p1 << " p2=" << pos ; +#endif +#if 0 + if (command.testFlag(QItemSelectionModel::Toggle)) + { + command &= ~QItemSelectionModel::Toggle; + command |= selectionModel_->isSelected(index) ? QItemSelectionModel::Deselect : QItemSelectionModel::Select; + } +#endif + setSelection(rect, command); + } + else + { + //Forces a finalize even if mouse is pressed, but not on a item + selectionModel_->select(QModelIndex(), QItemSelectionModel::Select); + } +} + +void AbstractNodeView::mouseReleaseEvent(QMouseEvent *event) +{ + QPoint pos = event->pos(); + QPersistentModelIndex index = indexAt(pos); + + if(selectionModel_ && noSelectionOnMousePress_) + { + noSelectionOnMousePress_ = false; + selectionModel_->select(index, selectionCommand(index, event)); + } +} + +void AbstractNodeView::mouseDoubleClickEvent(QMouseEvent *event) +{ + if(event->button() == Qt::LeftButton) + { + int viewItemIndex=itemAtCoordinate(event->pos()); + if(viewItemIndex != -1) + { + if(viewItems_[viewItemIndex].hasChildren) + { +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "CompactNodeView::mousePressEvent " << viewItemIndex << " name=" << + viewItems_[viewItemIndex].index.data().toString(); +#endif + if(viewItems_[viewItemIndex].expanded) + { + collapse(viewItemIndex); + } + else + { + expand(viewItemIndex); + } + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } + else + { + Q_EMIT doubleClicked(viewItems_[viewItemIndex].index); + } + } + } +} + +void AbstractNodeView::keyPressEvent(QKeyEvent *event) +{ + QModelIndex current = currentIndex(); + + if (current.isValid()) + { + switch(event->key()) + { + case Qt::Key_Plus: + expand(current); + break; + case Qt::Key_Minus: + collapse(current); + break; + } + } + + QAbstractScrollArea::keyPressEvent(event); +} + +bool AbstractNodeView::viewportEvent(QEvent *event) +{ + if(event->type() == QEvent::ToolTip) + { + QHelpEvent *he = static_cast(event); + const QModelIndex index = indexAt(he->pos()); + + //see qbatractitemdelegate::helpEvent() + QVariant tooltip = index.data(Qt::ToolTipRole); + if(tooltip.canConvert()) + { + QToolTip::showText(he->globalPos(),tooltip.toString(),this); + return true; + } + return false; + } + return QAbstractScrollArea::viewportEvent(event); +} + +void AbstractNodeView::timerEvent(QTimerEvent *event) +{ + if(event->timerId() == delayedWidth_.timerId()) + { + updateScrollBars(); + viewport()->update(); + delayedWidth_.stop(); + } +} + +void AbstractNodeView::reset() +{ + viewItems_.clear(); + rowCount_=0; + maxRowWidth_=0; + expandedIndexes.clear(); + pressedRefIndex_=QPersistentModelIndex(QModelIndex()); + //currentIndexSet_ = false; + if(selectionModel_) + selectionModel_->reset(); + + layout(-1,false,false,false); + updateRowCount(); + updateScrollBars(); +} + +/* + Informs the view that the rows from the start row to the end row + inclusive have been inserted into the parent model item. +*/ +void AbstractNodeView::rowsInserted(const QModelIndex& parent,int start,int end) +{ + const int parentItem = viewIndex(parent); + + //If the item is expanded we need to relayout the whole tree + if(((parentItem != -1) && viewItems_[parentItem].expanded) || (parent == root_)) + { + doItemsLayout(); + } + + //the parent just went from 0 children to more. update to re-paint the decoration + else if(parentItem != -1 && (model_->rowCount(parent) == end - start + 1)) + { + viewItems_[parentItem].hasChildren = true; + viewport()->update(); + } +} + +/* + Informs the view that the rows from the start row to the end row + inclusive are about to removed from the given parent model item. +*/ +void AbstractNodeView::rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end) +{ + //TODO: the selection has to be adjusted!!! + //QAbstractItemView::rowsAboutToBeRemoved(parent, start, end); + + //A safety measure + viewItems_.clear(); +} + +/* + Informs the view that the rows from the start row to the end row + inclusive have been removed from the given parent model item. +*/ +void AbstractNodeView::rowsRemoved(const QModelIndex &parent, int start, int end) +{ + doItemsLayout(true); +} + +void AbstractNodeView::doItemsLayout(bool hasRemovedItems) +{ + if(hasRemovedItems) + { + //clean the QSet that may contains old (and thus invalid) indexes + QSet::iterator it = expandedIndexes.begin(); + while (it != expandedIndexes.constEnd()) + { + if (!it->isValid()) + it = expandedIndexes.erase(it); + else + ++it; + } + //TODO: do we need to clear the selectionmodel? + } + + viewItems_.clear(); // prepare for new layout + rowCount_=0; + maxRowWidth_=0; + pressedRefIndex_=QPersistentModelIndex(QModelIndex()); + + QModelIndex parent = root_; + if(model_->hasChildren(parent)) + { + layout(-1,false,false,false); + } + updateRowCount(); + updateScrollBars(); + viewport()->update(); +} + + +void AbstractNodeView::paintEvent(QPaintEvent *event) +{ + QPainter painter(viewport()); + paint(&painter,event->region()); +} + +void AbstractNodeView::slotRepaint(Animation* an) +{ + if(!an) + return; + + Q_FOREACH(VNode* n,an->targets()) + { + update(model_->nodeToIndex(n)); + } +} + +//Updates the area occupied by the given index. +void AbstractNodeView::update(const QModelIndex &index) +{ + if (index.isValid()) + { + const QRect rect = visualRect(index); + //this test is important for peformance reasons + //For example in dataChanged if we simply update all the cells without checking + //it can be a major bottleneck to update rects that aren't even part of the viewport + if(viewport()->rect().intersects(rect)) + { +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "update -->" << index.data().toString() << " rect=" << rect; +#endif + updateViewport(rect); + } + } +} + +/* + This slot is called when items are changed in the model. The + changed items are those from topLeft to bottomRight + inclusive. If just one item is changed topLeft == + bottomRight. +*/ +void AbstractNodeView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight) +{ + // Single item changed + if (topLeft == bottomRight && topLeft.isValid()) + { + update(topLeft); + return; + } + + viewport()->update(); +} + +void AbstractNodeView::resizeEvent(QResizeEvent *event) +{ + QAbstractScrollArea::resizeEvent(event); + updateScrollBars(); + viewport()->update(); +} + +void AbstractNodeView::doDelayedWidthAdjustment() +{ + if(!delayedWidth_.isActive()) + { + delayedWidth_.start(0,this); + } +} + +int AbstractNodeView::translation() const +{ + return horizontalScrollBar()->value(); +} + +void AbstractNodeView::scrollTo(const QModelIndex &index) +{ + if(!index.isValid()) + return; + + //d->executePostedLayout(); + updateScrollBars(); + + // Expand all parents if the parent(s) of the node are not expanded. + QList parentLst; + QModelIndex parent = index.parent(); + while(parent.isValid()) + { + parentLst.prepend(parent); + parent = model_->parent(parent); + } + + Q_FOREACH(QModelIndex pt,parentLst) + { + if(!isExpanded(pt)) + expand(pt); + } + + int item = viewIndex(index); + if (item < 0) + return; + + if (verticalScrollMode_ == ScrollPerItem) + { + int row=itemRow(item); + + int top = verticalScrollBar()->value(); + int bottom = top + verticalScrollBar()->pageStep(); + + if (row >= top && row < bottom) + { + // nothing to do + } + else if(row < top) + { + verticalScrollBar()->setValue(row); + } + else + { + verticalScrollBar()->setValue(row); +#if 0 + const int currentItemHeight = viewItem_[item].height; + int y = area.height(); + if (y > currentItemHeight) + { + while (item >= 0) + { + y -= viewItem_[item].height; + if (y < 0) + { //there is no more space left + item++; + break; + } + item--; + } + } + verticalScrollBar()->setValue(item); +#endif + } + } + else // ScrollPerPixel + { + + } + + // horizontal + int viewportWidth = viewport()->width(); + int xp=viewItems_[item].x; + int horizontalPosition=xp-horizontalScrollBar()->value(); + + if( horizontalPosition< 0) + { + xp-=10; + if(xp < 0) xp=0; + horizontalScrollBar()->setValue(xp); + } + else if(horizontalPosition > viewportWidth) + { + xp-=10; + horizontalScrollBar()->setValue(xp); + } +} + + +//point is in viewport coordinates +QModelIndex AbstractNodeView::indexAt(const QPoint &point) const +{ + int item=itemAtCoordinate(point); + return (item>=0)?viewItems_[item].index:QModelIndex(); +} + +QModelIndex AbstractNodeView::modelIndex(int i) const +{ + if(i < 0 || i >= static_cast(viewItems_.size())) + return QModelIndex(); + + return viewItems_[i].index; +} + +//Returns the index of the view item representing the given index +int AbstractNodeView::viewIndex(const QModelIndex& index) const +{ + if(!index.isValid() || viewItems_.empty()) + return -1; + + const int totalCount = static_cast(viewItems_.size()); + const QModelIndex topIndex = index.sibling(index.row(), 0); + const int row = topIndex.row(); + const quintptr internalId = topIndex.internalId(); + + // We start nearest to the lastViewedItem + int localCount = qMin(lastViewedItem_ - 1, totalCount - lastViewedItem_); + for(int i = 0; i < localCount; ++i) + { + const QModelIndex &idx1 = viewItems_[lastViewedItem_ + i].index; + if(idx1.row() == row && idx1.internalId() == internalId) + { + lastViewedItem_ = lastViewedItem_ + i; + return lastViewedItem_; + } + const QModelIndex &idx2 = viewItems_[lastViewedItem_ - i - 1].index; + if(idx2.row() == row && idx2.internalId() == internalId) + { + lastViewedItem_ = lastViewedItem_ - i - 1; + return lastViewedItem_; + } + } + + for(int j = qMax(0, lastViewedItem_ + localCount); j < totalCount; ++j) + { + const QModelIndex &idx = viewItems_[j].index; + if (idx.row() == row && idx.internalId() == internalId) + { + lastViewedItem_ = j; + return j; + } + } + for(int j = qMin(totalCount, lastViewedItem_ - localCount) - 1; j >= 0; --j) + { + const QModelIndex &idx = viewItems_[j].index; + if (idx.row() == row && idx.internalId() == internalId) + { + lastViewedItem_ = j; + return j; + } + } + + // nothing found + return -1; +} + +void AbstractNodeView::insertViewItems(int pos, int count, const TreeNodeViewItem &viewItem) +{ + ViewItemIterator it=viewItems_.begin(); + viewItems_.insert(it+pos,count,viewItem); + + //We need to update the parentItem in the items after the insertion + const int itemsCount=static_cast(viewItems_.size()); + for(int i = pos + count; i < itemsCount; i++) + if (viewItems_[i].parentItem >= pos) + viewItems_[i].parentItem += count; +} + + +void AbstractNodeView::removeViewItems(int pos, int count) +{ + ViewItemIterator it=viewItems_.begin(); + viewItems_.erase(it+pos,it+pos+count); + + //We need to update the parentItem in the items after the deletion + const int itemsCount=static_cast(viewItems_.size()); + for(int i=0; i < itemsCount; i++) + if(viewItems_[i].parentItem >= pos) + viewItems_[i].parentItem -= count; +} + + +//--------------------------------------- +// Expand / collapse +//--------------------------------------- + +void AbstractNodeView::totalNumOfChildren(const QModelIndex& idx,int& num) const +{ + int count=model_->rowCount(idx); + num+=count; + for(int i=0; i < count; i++) + { + QModelIndex chIdx=model_->index(i,0,idx); + totalNumOfChildren(chIdx,num); + } +} + +void AbstractNodeView::totalNumOfExpandedChildren(const QModelIndex& idx,int& num) const +{ + int count=model_->rowCount(idx); + num+=count; + for(int i=0; i < count; i++) + { + QModelIndex chIdx=model_->index(i,0,idx); + if(isIndexExpanded(chIdx)) + { + totalNumOfExpandedChildren(chIdx,num); + } + } +} + +void AbstractNodeView::expand(int item) +{ + if(item != -1 && !viewItems_[item].expanded) + { + QModelIndex idx=viewItems_[item].index; + + //mark the item as expanded + storeExpanded(idx); + viewItems_[item].expanded = true; + + if(autoExpandLeafNode_) + { + int count=model_->rowCount(idx); + for(int i=0; i < count; i++) + { + QModelIndex chIdx=model_->index(i,0,idx); + if(model_->isFlatNode(chIdx)) + { + if(!isIndexExpanded(chIdx)) + storeExpanded(chIdx); + } + } + } + + //The total number items to be inserted + int total=0; + totalNumOfExpandedChildren(idx,total); + + //Insert the required number items + ViewItemIterator it=viewItems_.begin(); + viewItems_.insert(it+item+1,total,TreeNodeViewItem()); + + //recursively relayout the item + layout(item,false,false,true); + + UI_ASSERT(static_cast(viewItems_[item].total)==total,"viewItems_[" << item << "].total=" << viewItems_[item].total << + " total=" << total); + + //We need to update the parentItem in the items after the insertion. + //When layout() is called with the given arguments it is delayed to + //this point to gain performance! + const int itemsCount=static_cast(viewItems_.size()); + int count=viewItems_[item].total; + for(int i = item + count+1; i < itemsCount; i++) + if (viewItems_[i].parentItem >= item) + viewItems_[i].parentItem += count; + + //update the scrollbars and rerender the viewport + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } +} + +void AbstractNodeView::expand(const QModelIndex &idx) +{ + int item=viewIndex(idx); + expand(item); +} + +void AbstractNodeView::expandAll(const QModelIndex& idx) +{ + int item = viewIndex(idx); + if (item != -1) // is visible + { + //first we need to collapse all the children to start + //with a managable state. + collapseAllCore(idx); + + //mark the item as expanded + storeExpanded(idx); + viewItems_[item].expanded = true; + + //The total number items to be inserted + int total=0; + totalNumOfChildren(idx,total); + + //Insert the required number items + ViewItemIterator it=viewItems_.begin(); + viewItems_.insert(it+item+1,total,TreeNodeViewItem()); + + //recursively relayout the item + layout(item,true,false,true); + + UI_ASSERT(static_cast(viewItems_[item].total)==total,"viewItems_[" << item << "].total=" << viewItems_[item].total << + " total=" << total); + + //We need to update the parentItem in the items after the insertion. + //When layout() is called with the given arguments it is delayed to + //this point to gain performance! + const int itemsCount=static_cast(viewItems_.size()); + int count=viewItems_[item].total; + for(int i = item + count+1; i < itemsCount; i++) + if (viewItems_[i].parentItem >= item) + viewItems_[i].parentItem += count; + + + //update the scrollbars and rerender the viewport + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } +} + +void AbstractNodeView::restoreExpand(const QModelIndex& idx) +{ + //expandedIndexed now contains all the indexes to expand + expand(idx); +} + +void AbstractNodeView::collapse(int item) +{ + if (item == -1 || expandedIndexes.isEmpty()) + return; + + const QModelIndex &modelIndex = viewItems_.at(item).index; + //if(!isPersistent(modelIndex)) + // return; // if the index is not persistent, no chances it is expanded + + QSet::iterator it = expandedIndexes.find(modelIndex); + if (it == expandedIndexes.end() || viewItems_.at(item).expanded == false) + return; // nothing to do + + expandedIndexes.erase(it); + viewItems_[item].expanded = false; + int total=viewItems_[item].total; + int index = item; + while (index > -1) + { + viewItems_[index].total-=total; + index = viewItems_[index].parentItem; + } + removeViewItems(item + 1, total); // collapse +} + +void AbstractNodeView::collapse(const QModelIndex &index) +{ + int i = viewIndex(index); + if (i != -1) // is visible + { + collapse(i); + updateRowCount(); + updateScrollBars(); + viewport()->update(); + } +} + +bool AbstractNodeView::collapseAllCore(const QModelIndex &index) +{ + //identify item + int item = viewIndex(index); + + //check if there is nothing to do + if (item == -1 || expandedIndexes.isEmpty()) + return false; + + //check if the item is expanded + QSet::iterator it = expandedIndexes.find(index); + if (it == expandedIndexes.end() || viewItems_.at(item).expanded == false) + return false; + + //remove all the children of the item + viewItems_[item].expanded = false; + int total=viewItems_[item].total; + int parentItem = item; + while (parentItem > -1) + { + viewItems_[parentItem].total-=total; + parentItem = viewItems_[parentItem].parentItem; + } + removeViewItems(item + 1, total); + + //recursivel remove the indexes related to the deleted items from the expanded set + removeAllFromExpanded(index); + + updateRowCount(); + return true; +} + +void AbstractNodeView::collapseAll(const QModelIndex &index) +{ + if(collapseAllCore(index)) + { + updateScrollBars(); + viewport()->update(); + } +} + +void AbstractNodeView::removeAllFromExpanded(const QModelIndex &index) +{ + if(expandedIndexes.isEmpty()) + return; + + QSet::iterator it = expandedIndexes.find(index); + if(it == expandedIndexes.end()) + return; + + expandedIndexes.erase(it); + + for(int i=0; i < model_->rowCount(index); i++) + { + QModelIndex chIdx=model_->index(i, 0, index); + removeAllFromExpanded(chIdx); + } +} + + +bool AbstractNodeView::isExpanded(const QModelIndex &index) const +{ + return isIndexExpanded(index); +} + +void AbstractNodeView::setExpanded(const QModelIndex &index, bool expanded) +{ + if (expanded) + expand(index); + else + collapse(index); +} + +//Expands all expandable items. +void AbstractNodeView::expandAll() +{ + viewItems_.clear(); + expandedIndexes.clear(); + //d->interruptDelayedItemsLayout(); + layout(-1, true,false,false); + updateRowCount(); + updateScrollBars(); + viewport()->update(); +} + +//Collapses all expanded items. +void AbstractNodeView::collapseAll() +{ + expandedIndexes.clear(); + doItemsLayout(); +} + +//======================================================== +// +// Selection +// +//======================================================== + +void AbstractNodeView::setCurrentIndex(const QModelIndex &index) +{ + if(selectionModel_ && index.isValid()) + { + QItemSelectionModel::SelectionFlags command = selectionCommand(index, 0); + selectionModel_->setCurrentIndex(index, command); + //currentIndexSet_ = true; + QPoint offset; + if((command & QItemSelectionModel::Current) == 0) + //pressedPosition_ = visualRect(currentIndex()).center() + offset; + pressedRefIndex_=currentIndex(); + } +} + + +QModelIndex AbstractNodeView::currentIndex() const +{ + return selectionModel_ ? selectionModel_->currentIndex() : QModelIndex(); +} + +QModelIndexList AbstractNodeView::selectedIndexes() const +{ + if(selectionModel_) + return selectionModel_->selectedIndexes(); + + return QModelIndexList(); +} + + +/* + Applies the selection command to the items in or touched by the + rectangle rect. +*/ +void AbstractNodeView::setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command) +{ + if (!selectionModel_ || rect.isNull()) + return; + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "TreeNodeViewBase::setSelection --> rect=" << rect; +#endif + + QPoint tl=QPoint(rect.x(),rect.y()); + QPoint br=QPoint(rect.x()+rect.width(),rect.y()+rect.height()); + + if(tl.y() > br.y()) + qSwap(tl,br); + + QModelIndex topLeft = indexAt(tl); + QModelIndex bottomRight = indexAt(br); + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << " tl=" << tl << " " << topLeft.data().toString() << + " br=" << br << " " << bottomRight.data().toString(); +#endif + + if (!topLeft.isValid() && !bottomRight.isValid()) + { + if(command & QItemSelectionModel::Clear) + selectionModel_->clear(); + return; + } + + if (!topLeft.isValid() && !viewItems_.empty()) + topLeft = viewItems_.front().index; + + if (!bottomRight.isValid() && !viewItems_.empty()) + { + const QModelIndex index = viewItems_.back().index; + bottomRight = index.sibling(index.row(),0); + } + + select(topLeft, bottomRight, command); +} + +void AbstractNodeView::select(const QModelIndex &topIndex, const QModelIndex &bottomIndex, + QItemSelectionModel::SelectionFlags command) +{ + QItemSelection selection; + const int top = viewIndex(topIndex), + bottom = viewIndex(bottomIndex); + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "TreeNodeViewBase::select --> command=" << command; + UiLog().dbg() << "top=" << top << " " << topIndex.data().toString() << + " bottom=" << bottom << " " << bottomIndex.data().toString(); +#endif + + QModelIndex previous; + QItemSelectionRange currentRange; + QStack rangeStack; + for(int i = top; i <= bottom; ++i) + { + QModelIndex index = modelIndex(i); + QModelIndex parent = index.parent(); + QModelIndex previousParent = previous.parent(); + + //same parent as previous + if (previous.isValid() && parent == previousParent) + { + //same parent + if (qAbs(previous.row() - index.row()) > 1) + { + //a hole (hidden index inside a range) has been detected + if (currentRange.isValid()) + { + selection.append(currentRange); + } + //let's start a new range + currentRange = QItemSelectionRange(index, index); + } + + else + { + QModelIndex tl = model_->index(currentRange.top(),0, + currentRange.parent()); + currentRange = QItemSelectionRange(tl, index); + } + } + + //The current parent is the previous item + else if(previous.isValid() && + parent == model_->index(previous.row(), 0, previousParent)) + { + rangeStack.push(currentRange); + currentRange = QItemSelectionRange(index, index); + } + + else + { + if(currentRange.isValid()) + selection.append(currentRange); + if(rangeStack.isEmpty()) + { + currentRange = QItemSelectionRange(index, index); + } + else + { + currentRange = rangeStack.pop(); + index = currentRange.bottomRight(); //let's resume the range + --i; //we process again the current item + } + } + + previous = index; + } + + if (currentRange.isValid()) + selection.append(currentRange); + + for (int i = 0; i < rangeStack.count(); ++i) + selection.append(rangeStack.at(i)); + +#if 0 + UiLog().dbg() << "before"; + Q_FOREACH(QModelIndex idx,selectionModel_->selectedIndexes()) + { + UiLog().dbg() << " " << idx.data().toString(); + } + + UiLog().dbg() << "selection"; + Q_FOREACH(QModelIndex idx,selection.indexes()) + { + UiLog().dbg() << " " << idx.data().toString(); + } +#endif + + selectionModel_->select(selection, command); + +#if 0 + UiLog().dbg() << "after"; + Q_FOREACH(QModelIndex idx,selectionModel_->selectedIndexes()) + { + UiLog().dbg() << " " << idx.data().toString(); + } +#endif +} + + +/* + Returns the SelectionFlags to be used when updating a selection with + to include the index specified. The event is a user input event, + such as a mouse or keyboard event. +*/ + +QItemSelectionModel::SelectionFlags AbstractNodeView::selectionCommand( + const QModelIndex &index, const QEvent *event) const +{ + Qt::KeyboardModifiers modifiers = QApplication::keyboardModifiers(); + if (event) { + + switch (event->type()) { + case QEvent::MouseMove: { + // Toggle on MouseMove + modifiers = static_cast(event)->modifiers(); + if (modifiers & Qt::ControlModifier) + return QItemSelectionModel::ToggleCurrent|selectionBehaviorFlags(); + break; + } + case QEvent::MouseButtonPress: { + modifiers = static_cast(event)->modifiers(); + const Qt::MouseButton button = static_cast(event)->button(); + const bool rightButtonPressed = button & Qt::RightButton; + const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; + const bool controlKeyPressed = modifiers & Qt::ControlModifier; + const bool indexIsSelected = selectionModel_->isSelected(index); + if ((shiftKeyPressed || controlKeyPressed) && rightButtonPressed) + return QItemSelectionModel::NoUpdate; + if (!shiftKeyPressed && !controlKeyPressed && indexIsSelected) + return QItemSelectionModel::NoUpdate; + if (!index.isValid() && !rightButtonPressed && !shiftKeyPressed && !controlKeyPressed) + return QItemSelectionModel::Clear; + if (!index.isValid()) + return QItemSelectionModel::NoUpdate; + break; + } + case QEvent::MouseButtonRelease: { + // ClearAndSelect on MouseButtonRelease if MouseButtonPress on selected item or empty area + modifiers = static_cast(event)->modifiers(); + const Qt::MouseButton button = static_cast(event)->button(); + const bool rightButtonPressed = button & Qt::RightButton; + const bool shiftKeyPressed = modifiers & Qt::ShiftModifier; + const bool controlKeyPressed = modifiers & Qt::ControlModifier; + if (((index == pressedIndex_ && selectionModel_->isSelected(index)) + || !index.isValid()) //&& state != QAbstractItemView::DragSelectingState + && !shiftKeyPressed && !controlKeyPressed && (!rightButtonPressed || !index.isValid())) + return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); + return QItemSelectionModel::NoUpdate; + } + case QEvent::KeyPress: { + // NoUpdate on Key movement and Ctrl + modifiers = static_cast(event)->modifiers(); + switch (static_cast(event)->key()) { + case Qt::Key_Backtab: + modifiers = modifiers & ~Qt::ShiftModifier; // special case for backtab + case Qt::Key_Down: + case Qt::Key_Up: + case Qt::Key_Left: + case Qt::Key_Right: + case Qt::Key_Home: + case Qt::Key_End: + case Qt::Key_PageUp: + case Qt::Key_PageDown: + case Qt::Key_Tab: + if (modifiers & Qt::ControlModifier +#ifdef QT_KEYPAD_NAVIGATION + // Preserve historical tab order navigation behavior + || QApplication::navigationMode() == Qt::NavigationModeKeypadTabOrder +#endif + ) + return QItemSelectionModel::NoUpdate; + break; + case Qt::Key_Select: + return QItemSelectionModel::Toggle|selectionBehaviorFlags(); + case Qt::Key_Space:// Toggle on Ctrl-Qt::Key_Space, Select on Space + if (modifiers & Qt::ControlModifier) + return QItemSelectionModel::Toggle|selectionBehaviorFlags(); + return QItemSelectionModel::Select|selectionBehaviorFlags(); + default: + break; + } + } + default: + break; + } + } + + if (modifiers & Qt::ShiftModifier) + return QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); + if (modifiers & Qt::ControlModifier) + return QItemSelectionModel::Toggle|selectionBehaviorFlags(); + //if (state == QAbstractItemView::DragSelectingState) { + // //when drag-selecting we need to clear any previous selection and select the current one + // return QItemSelectionModel::Clear|QItemSelectionModel::SelectCurrent|selectionBehaviorFlags(); + //} + + return QItemSelectionModel::ClearAndSelect|selectionBehaviorFlags(); +} + +/* + Returns the rectangle from the viewport of the items in the given + selection. + + The returned region only contains rectangles intersecting + (or included in) the viewport. +*/ +QRegion AbstractNodeView::visualRegionForSelection(const QItemSelection &selection) const +{ + if(selection.isEmpty()) + return QRegion(); + + QRegion selectionRegion; + const QRect &viewportRect = viewport()->rect(); + for(int i = 0; i < selection.count(); ++i) + { + QItemSelectionRange range = selection.at(i); + if (!range.isValid()) + continue; + + QModelIndex leftIndex = range.topLeft(); + if (!leftIndex.isValid()) + continue; + + QModelIndex rightIndex = range.bottomRight(); + if (!rightIndex.isValid()) + continue; + + int left=100000000,right=0,top=1000000000, bottom=0; + Q_FOREACH(QModelIndex idx,range.indexes()) + { + const QRect r = visualRect(idx); + //UiLog().dbg() << r << " " << idx << " " << idx.data().toString(); + if(r.x() < left) left=r.x(); + if(r.right()+1 > right) right=r.right()+1; + if(r.y() < top) top=r.y(); + if(r.bottom()+1 > bottom ) bottom=r.bottom()+1; + } + + top-=1; + bottom+=1; + + QRect combined(left,top,right-left+1,bottom-top+1); + if (viewportRect.intersects(combined)) + selectionRegion += combined; + } + return selectionRegion; +} + +/* + This slot is called when the selection is changed. The previous + selection (which may be empty), is specified by deselected, and the + new selection by selected. +*/ +void AbstractNodeView::selectionChanged(const QItemSelection &selected, + const QItemSelection &deselected) +{ + if(isVisible()) // && updatesEnabled()) { + { + QRegion des=visualRegionForSelection(deselected); + QRegion sel=visualRegionForSelection(selected); + +#ifdef _UI_QABSTRACTNODEVIEW_DEBUG + UiLog().dbg() << "TreeNodeViewBase::selectionChanged -->"; + UiLog().dbg() << " deselect=" << des.boundingRect() << " select=" << sel.boundingRect(); + QRegion un=des | sel; + UiLog().dbg() << " union=" << un.boundingRect(); +#endif + viewport()->update(des | sel); + } +} + +/* + This slot is called when a new item becomes the current item. + The previous current item is specified by the previous index, and the new + item by the current index. +*/ +void AbstractNodeView::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if(!isVisible()) + return; + + if(previous.isValid()) + { + update(previous); + } + + if(current.isValid()) + { + if(autoScroll_) + { + scrollTo(current); + } + update(current); + } +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractNodeView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractNodeView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,222 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ABSTRACTNODEVIEW_HPP +#define ABSTRACTNODEVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +class Animation; +class TreeNodeModel; +class TreeNodeViewDelegate; + +//Struct representing visible items in the view. When an item is collapsed +//all its children will be removed from viewItems. +struct TreeNodeViewItem +{ + TreeNodeViewItem() : parentItem(-1), total(0), widestInSiblings(0), expanded(0), hasChildren(0), + hasMoreSiblings(0), level(0), width(0), height(0), x(0) {} + + QModelIndex index; //the model index represented by the item. + //We remove items whenever the indexes are invalidated + int parentItem; // parent item index in viewItems + unsigned int total; // total number of visible children in the view + unsigned int widestInSiblings; + unsigned int expanded : 1; //the item expanded + unsigned int hasChildren : 1; // if the item has children in the model (it is + // independent of the expanded/collapsed state) + unsigned int hasMoreSiblings : 1; + unsigned int level : 12; // indentation + unsigned int width: 12; + unsigned int height : 16; + unsigned int x: 16; + + int right() const {return x+width;} + int alignedRight() const {return x+widestInSiblings;} + bool isFirstChild() const {return index.row() ==0;} + bool isLeaf() const {return total == 0;} +}; + + +class AbstractNodeView : public QAbstractScrollArea +{ +Q_OBJECT + + friend class TreeNodeView; + +public: + explicit AbstractNodeView(TreeNodeModel* model,QWidget *parent=0); + virtual ~AbstractNodeView(); + + QModelIndex currentIndex() const; + QModelIndexList selectedIndexes() const; + QModelIndex indexAt(const QPoint &point) const; + virtual QRect visualRect(const QModelIndex &index) const=0; + bool isExpanded(const QModelIndex &index) const; + void setExpanded(const QModelIndex &index, bool expanded); + void expandAll(const QModelIndex &index); + void collapseAll(const QModelIndex &index); + TreeNodeViewDelegate* delegate() const {return delegate_;} + +public Q_SLOTS: + void reset(); + void setCurrentIndex(const QModelIndex &index); + void update(const QModelIndex &index); + void expand(const QModelIndex &index); + void collapse(const QModelIndex &index); + void expandAll(); + void collapseAll(); + void slotRepaint(Animation*); + +protected Q_SLOTS: + void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight); + void rowsInserted(const QModelIndex&,int,int); + void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end); + void rowsRemoved(const QModelIndex &parent, int start, int end); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + +Q_SIGNALS: + void doubleClicked(const QModelIndex&); + void selectionChangedInView(const QItemSelection &selected, const QItemSelection &deselected); + +protected: + void mousePressEvent(QMouseEvent* event); + void mouseReleaseEvent(QMouseEvent *event); + void mouseDoubleClickEvent(QMouseEvent *event); + void keyPressEvent(QKeyEvent *event); + void paintEvent(QPaintEvent *event); + void resizeEvent(QResizeEvent *event); + bool viewportEvent(QEvent *event); + void timerEvent(QTimerEvent *event); + + void attachModel(); + void insertItems(const QModelIndex& parent, int); + + virtual void paint(QPainter *painter,const QRegion& region)=0; + + //layout + void doDelayedWidthAdjustment(); + void doItemsLayout(bool hasRemovedItems=false); + virtual void layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated)=0; + + //Item lookup + void scrollTo(const QModelIndex &index); + virtual int itemRow(int item) const=0; + virtual int itemAtCoordinate(const QPoint& coordinate) const=0; + QModelIndex modelIndex(int i) const; + int viewIndex(const QModelIndex& index) const; + virtual bool isPointInExpandIndicator(int,QPoint) const=0; + + //Expand collapse + void restoreExpand(const QModelIndex& idx); + void removeAllFromExpanded(const QModelIndex &index); + void totalNumOfChildren(const QModelIndex& idx,int& num) const; + void totalNumOfExpandedChildren(const QModelIndex& idx,int& num) const; + + inline bool isIndexExpanded(const QModelIndex &idx) const + { + //We first check if the idx is a QPersistentModelIndex, because creating QPersistentModelIndex is slow + return expandedIndexes.contains(idx); + } + + //selection + void setSelection(const QRect &rect, QItemSelectionModel::SelectionFlags command); + void select(const QModelIndex &topIndex, const QModelIndex &bottomIndex, + QItemSelectionModel::SelectionFlags command); + + QItemSelectionModel::SelectionFlags selectionCommand(const QModelIndex &index, + const QEvent *event) const; + + QRegion visualRegionForSelection(const QItemSelection &selection) const; + + virtual void updateViewport(const QRect rect)=0; + + virtual int firstVisibleItem(int &offset) const=0; + virtual void updateRowCount()=0; + virtual void updateScrollBars()=0; + void adjustWidthInParent(int start); + + void setIndentation(int i) {indentation_=i;} + void setExpectedBg(QColor c) {expectedBg_=c;} + void setConnectorColour(QColor c) {connectorColour_=c;} + void setDrawConnector(bool b) {drawConnector_=b;} + void setAutoExpandLeafNode(bool b) {autoExpandLeafNode_=b;} + + void insertViewItems(int pos, int count, const TreeNodeViewItem &viewItem); + void removeViewItems(int pos, int count); + + int translation() const; + + enum ScrollMode { + ScrollPerItem, + ScrollPerPixel + }; + + + TreeNodeModel* model_; //The model + TreeNodeViewDelegate* delegate_; + QSet expandedIndexes; // used when expanding and collapsing items + + typedef std::vector::iterator ViewItemIterator; + ScrollMode verticalScrollMode_; + mutable std::vector viewItems_; + int rowCount_; + int maxRowWidth_; + QModelIndex root_; + int topMargin_; + int leftMargin_; + int itemGap_; + int connectorGap_; + int expandConnectorLenght_; + QPointer selectionModel_; + QColor expectedBg_; + QColor connectorColour_; + bool drawConnector_; + int indentation_; + bool autoExpandLeafNode_; + +private: + void expand(int item); + void collapse(int item); + bool collapseAllCore(const QModelIndex &index); + + mutable int lastViewedItem_; + QPoint pressedPosition_; + QPersistentModelIndex pressedIndex_; + QPersistentModelIndex pressedRefIndex_; + bool noSelectionOnMousePress_; + bool autoScroll_; + QBasicTimer delayedWidth_; + + inline bool storeExpanded(const QPersistentModelIndex &idx) + { + if(expandedIndexes.contains(idx)) + return false; + expandedIndexes.insert(idx); + return true; + } + + inline QItemSelectionModel::SelectionFlags selectionBehaviorFlags() const + { + return QItemSelectionModel::NoUpdate; + } +}; + +#endif // ABSTRACTNODEVIEW_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractSearchLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractSearchLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractSearchLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,220 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include +#include +#include +#include "AbstractSearchLine.hpp" +#include "IconProvider.hpp" +#include "ViewerUtil.hpp" + +AbstractSearchLine::AbstractSearchLine(QWidget* parent) : + QWidget(parent), + confirmSearch_(false) +{ + setupUi(this); + + confirmSearchLabel_->hide(); + confirmSearchLabel_->setShowTypeTitle(false); + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + searchLine_->setPlaceholderText(tr("Find")); + label_->hide(); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + searchLine_->setClearButtonEnabled(true); +#endif + + connect(searchLine_, SIGNAL(textChanged(QString)), + this, SLOT(slotFind(QString))); + + connect(searchLine_, SIGNAL( returnPressed()), + this, SLOT(slotFindNext())); + + connect(actionNext_,SIGNAL(triggered()), + this, SLOT(slotFindNext())); + + connect(actionPrev_,SIGNAL(triggered()), + this, SLOT(slotFindPrev())); + + connect(closeTb_,SIGNAL(clicked()), + this, SLOT(slotClose())); + + nextTb_->setDefaultAction(actionNext_); + prevTb_->setDefaultAction(actionPrev_); + + oriBrush_=QBrush(searchLine_->palette().brush(QPalette::Base)); + redBrush_=ViewerUtil::lineEditRedBg(); + greenBrush_=ViewerUtil::lineEditGreenBg(); + +#if 0 + oriColour_=QColor(searchLine_->palette().color(QPalette::Base)); + redColour_=QColor(247,230,230); + greenColour_=QColor(186,249,206); +#endif + + QIcon icon; + icon.addPixmap(QPixmap(":/viewer/close_grey.svg"),QIcon::Normal); + icon.addPixmap(QPixmap(":/viewer/close_red.svg"),QIcon::Active); + closeTb_->setIcon(icon); + + // for the 'find next' functionality, although Qt (at the time of writing) uses + // both F3 and CTRL-G for most platforms, this is not true for Linux. Therefore, + // we have to add CTRL-G ourselves. + + QKeySequence ctrlg(tr("Ctrl+G")); + QShortcut *shortcut = new QShortcut(ctrlg, parent); // should be destroyed by parent + connect(shortcut, SIGNAL(activated()), this, SLOT(slotFindNext())); + + status_=true; + + setFocusProxy(searchLine_); + + // set the menu on the Options toolbutton + caseSensitive_ = false; + wholeWords_ = false; + highlightAll_ = false; + QMenu *menu=new QMenu(this); + menu->addAction(actionCaseSensitive_); + menu->addAction(actionWholeWords_); + menu->addAction(actionHighlightAll_); + optionsTb_->setMenu(menu); + + matchModeCb_->setMatchMode(StringMatchMode::ContainsMatch); // set the default match mode + //matchModeChanged(1); // dummy call to initialise the 'whole words' option state + + setConfirmSearch(false); +} + +AbstractSearchLine::~AbstractSearchLine() +{ +} + +void AbstractSearchLine::clear() +{ + //clearRequested(); + searchLine_->clear(); +} + +bool AbstractSearchLine::isEmpty() +{ + return searchLine_->text().isEmpty(); +} + +void AbstractSearchLine::selectAll() +{ + searchLine_->selectAll(); +} + +void AbstractSearchLine::toDefaultState() +{ + QPalette p=searchLine_->palette(); + p.setBrush(QPalette::Base,oriBrush_); + searchLine_->setPalette(p); +} + +void AbstractSearchLine::updateButtons(bool found) +{ + status_=found; + + if(searchLine_->text().isEmpty()) + { + QPalette p=searchLine_->palette(); + p.setBrush(QPalette::Base,oriBrush_); + searchLine_->setPalette(p); + } + else + { + if(!found) + { + QPalette p=searchLine_->palette(); + p.setBrush(QPalette::Base,redBrush_); + searchLine_->setPalette(p); + } + else + { + QPalette p=searchLine_->palette(); + p.setBrush(QPalette::Base,greenBrush_); + searchLine_->setPalette(p); + } + } +} + +void AbstractSearchLine::slotClose() +{ + hide(); +} + +void AbstractSearchLine::on_actionCaseSensitive__toggled(bool b) +{ + caseSensitive_ = b; +} + +void AbstractSearchLine::on_actionWholeWords__toggled(bool b) +{ + wholeWords_ = b; +} + +void AbstractSearchLine::on_actionHighlightAll__toggled(bool b) +{ + highlightAll_ = b; +} + +void AbstractSearchLine::setConfirmSearch(bool confirmSearch) +{ + confirmSearch_=confirmSearch; + //confirmSearchLabel_->setVisible(confirmSearch_); + + if(confirmSearch_) + { + //confirmSearchLabel_->showWarning("Large file mode"); + if(searchLine_->text().isEmpty()) + { + status_=false; + toDefaultState(); + } + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + searchLine_->setPlaceholderText(tr("Find (you need to hit enter to start search)")); +#endif + } + else + { +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + searchLine_->setPlaceholderText(tr("Find")); +#endif + } + + searchLine_->setToolTip(confirmSearchText()); +} + +QString AbstractSearchLine::confirmSearchText() const +{ + return (confirmSearch_)? + "Search works in large file mode. After typing in the search term press enter or use the arrows to start search!": + "Search works in continuous mode. As you type in a new character the search starts immediately."; + +} + +void AbstractSearchLine::hideEvent(QHideEvent* event) +{ + QWidget::hideEvent(event); + Q_EMIT visibilityChanged(); +} + +void AbstractSearchLine::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); + if(!event->spontaneous()) + slotFind(searchLine_->text()); + //refreshSearch(); + Q_EMIT visibilityChanged(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractSearchLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractSearchLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractSearchLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef ABSTRACTSEARCHLINE_HPP_ +#define ABSTRACTSEARCHLINE_HPP_ + +#include "ui_SearchLineWidget.h" + +#include + +class AbstractSearchLine : public QWidget, protected Ui::SearchLineWidget +{ + Q_OBJECT + +public: + explicit AbstractSearchLine(QWidget *parent=0); + virtual ~AbstractSearchLine(); + virtual void clear(); + virtual bool isEmpty(); + void selectAll(); + void setConfirmSearch(bool); + bool confirmSearch() const {return confirmSearch_;} + QString confirmSearchText() const; + + bool caseSensitive() {return caseSensitive_;} + bool wholeWords() {return wholeWords_;} + bool highlightAll() {return highlightAll_;} + +public Q_SLOTS: + virtual void slotFind(QString)=0; + virtual void slotFindNext()=0; + virtual void slotFindPrev()=0; + virtual void slotClose(); + virtual void on_actionCaseSensitive__toggled(bool); + virtual void on_actionWholeWords__toggled(bool); + virtual void on_actionHighlightAll__toggled(bool); + +Q_SIGNALS: + void visibilityChanged(); + +protected: + void updateButtons(bool); + void toDefaultState(); + void hideEvent(QHideEvent* event); + void showEvent(QShowEvent* event); + + bool status_; + bool caseSensitive_; + bool wholeWords_; + bool highlightAll_; + StringMatchMode matchMode_; + + QBrush oriBrush_; + QBrush redBrush_; + QBrush greenBrush_; + + bool confirmSearch_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,38 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AbstractTextEditSearchInterface.hpp" + +#include "VConfig.hpp" +#include "VProperty.hpp" + +QColor AbstractTextEditSearchInterface::highlightColour_=QColor(200, 255, 200); + +AbstractTextEditSearchInterface::AbstractTextEditSearchInterface() +{ + if(VProperty *p=VConfig::instance()->find("panel.search.highlightColour")) + { + highlightColour_=p->value().value(); + } + + vpPerformAutomaticSearch_ = VConfig::instance()->find("panel.output.automaticSearch.performSearch"); + vpAutomaticSearchMode_ = VConfig::instance()->find("panel.output.automaticSearch.searchMode"); + vpAutomaticSearchText_ = VConfig::instance()->find("panel.output.automaticSearch.searchText"); + vpAutomaticSearchFrom_ = VConfig::instance()->find("panel.output.automaticSearch.searchFrom"); + vpAutomaticSearchCase_ = VConfig::instance()->find("panel.output.automaticSearch.caseSensitive"); + + // these should always exist - if they don't then there is a spelling error in the above lines + assert(vpPerformAutomaticSearch_); + assert(vpAutomaticSearchMode_); + assert(vpAutomaticSearchText_); + assert(vpAutomaticSearchFrom_); + assert(vpAutomaticSearchCase_); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AbstractTextEditSearchInterface.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,47 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_ABSTRACTTEXTEDITSEARCHINTERFACE_HPP_ +#define VIEWER_SRC_ABSTRACTTEXTEDITSEARCHINTERFACE_HPP_ + +#include "StringMatchMode.hpp" +#include "VProperty.hpp" + +#include +#include +#include + +class AbstractTextEditSearchInterface +{ +public: + AbstractTextEditSearchInterface(); + virtual ~AbstractTextEditSearchInterface() {} + + virtual bool findString (QString str, bool highlightAll, QTextDocument::FindFlags findFlags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode)=0; + virtual void automaticSearchForKeywords(bool)=0; + virtual void refreshSearch()=0; + virtual void clearHighlights()=0; + virtual void disableHighlights()=0; + virtual void enableHighlights()=0; + virtual bool highlightsNeedSearch()=0; + virtual void gotoLastLine()=0; + +protected: + static QColor highlightColour_; + VProperty *vpPerformAutomaticSearch_; + VProperty *vpAutomaticSearchMode_; + VProperty *vpAutomaticSearchText_; + VProperty *vpAutomaticSearchFrom_; + VProperty *vpAutomaticSearchCase_; +}; + + +#endif /* VIEWER_SRC_ABSTRACTTEXTEDITSEARCHINTERFACE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ActionHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ActionHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ActionHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ActionHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,342 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include + +#include "ActionHandler.hpp" + +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#else +#include +#include +#endif + +#include "CommandHandler.hpp" +#include "VNode.hpp" +#include "Str.hpp" +#include "ServerHandler.hpp" +#include "MenuHandler.hpp" +#include "CustomCommandDialog.hpp" +#include "TextFormat.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "UserMessage.hpp" +#include "VConfig.hpp" +#include "VNodeMover.hpp" +#include "VNodeStateDiag.hpp" +#include "VReportMaker.hpp" + +#define _UI_ACTIONHANDLER_DEBUG + +ActionHandler::ActionHandler(QObject *actionSender,QWidget* menuParent) : + QObject(actionSender), + actionSender_(actionSender), + menuParent_(menuParent) +{ + connect(this,SIGNAL(viewCommand(VInfo_ptr,QString)), + actionSender_,SLOT(slotViewCommand(VInfo_ptr,QString))); + + connect(this,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + actionSender_,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); + + connect(this,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + actionSender_,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + //makeShortcut(); +} + +void ActionHandler::contextMenu(std::vector nodesLst,QPoint pos) +{ + // deal with tricky cases - if the user selects a combination of 'normal' nodes + // and attribute nodes, we want to ignore the attribute nodes, so we will remove + // them from the list here and pretend they were not selected + + // count how many attributes and non-attributes are selected + long numAttrs=0, numNonAttrNodes=0; + for (std::vector::iterator itNodes = nodesLst.begin(); itNodes != nodesLst.end(); ++itNodes) + { + if ((*itNodes)->isAttribute()) + numAttrs++; + else + numNonAttrNodes++; + } + + std::vector filteredNodes; + if (numAttrs > 0 && numNonAttrNodes > 0) // just keep the non-attribute nodes + { + for (std::vector::iterator itNodes = nodesLst.begin(); itNodes != nodesLst.end(); ++itNodes) + { + if (!((*itNodes)->isAttribute())) + filteredNodes.push_back(*itNodes); + } + } + else // keep all the nodes + { + filteredNodes = nodesLst; + } + + + + std::string view=menuParent_->property("view").toString().toStdString(); + MenuItem* item=MenuHandler::invokeMenu("Node", filteredNodes, pos, menuParent_,view); + + if(item) + { + +#ifdef _UI_ACTIONHANDLER_DEBUG + UiLog().dbg() << "ActionHandler::contextMenu --> item=" + item->name(); +#endif + if(item->handler() == "info_panel") + { + Q_EMIT infoPanelCommand(filteredNodes.at(0),QString::fromStdString(item->command())); + return; + } + else if(item->handler() == "dashboard") + { + Q_EMIT dashboardCommand(filteredNodes.at(0),QString::fromStdString(item->command())); + return; + } + else if(item->handler() == "tree" || item->handler() == "table" || item->handler() == "trigger") + { + Q_EMIT viewCommand(filteredNodes.at(0),QString::fromStdString(item->command())); + return; + } + + /*if(action->iconText() == "Set as root") + { + //Q_EMIT viewCommand(filteredNodes,"set_as_root"); + }*/ + else if(item->command() == "copy") + { + QString txt; + for(std::vector::const_iterator it=filteredNodes.begin(); it != filteredNodes.end(); ++it) + { + if(*it) + { + if(!txt.isEmpty()) + txt+=","; + txt+=QString::fromStdString((*it)->path()); + } + } +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif + } + + else if(item->command() == "create_jsd_ticket") + { + if(filteredNodes.size() == 1) + { + if(filteredNodes[0] && filteredNodes[0]->node()) + { + VReportMaker::sendReport(filteredNodes[0]); + } + } + } + + else if(item->command() == "check_ui_node_state") + { + if(filteredNodes.size() == 1) + { + if(filteredNodes[0] && filteredNodes[0]->node()) + { + VNodeStateDiag diag(filteredNodes[0]); + } + } + } + + else if(item->command() == "mark_for_move") + { + if(filteredNodes.size() > 1) + { + UserMessage::message(UserMessage::ERROR, true, "Only one node can be marked for move at a time"); + return; + } + + VNodeMover::markNodeForMove(filteredNodes[0]); + } + + else if(item->command() == "move_marked") + { + if (filteredNodes.size() > 1) + { + UserMessage::message(UserMessage::ERROR, true, "Only one destination node should be selected"); + return; + } + + VNodeMover::moveMarkedNode(filteredNodes[0]); + } + + else + { + CustomCommandDialog *customCommandDialog = NULL; + + if(item->command() == "custom") // would expect this to be 'Custom...' but it's just 'Custom' + { + // invoke the custom command dialogue + customCommandDialog = new CustomCommandDialog(0); + customCommandDialog->setNodes(filteredNodes); + if (customCommandDialog->exec() == QDialog::Accepted) + { + // the user could have changed the node selection within the custom editor + customCommandDialog->selectedNodes(); + + // the dialogue contains a 'fake' menu item created from the custom command + item = &(customCommandDialog->menuItem()); + } + else + { + // user cancelled the custom command dialogue + delete customCommandDialog; + return; + } + } + + bool ok=true; + if (item->isCustom()) + MenuHandler::interceptCommandsThatNeedConfirmation(item); + + bool needQuestion=item && !item->question().empty() && item->shouldAskQuestion(filteredNodes); + + //We can control if a confrmation is needed for a command from the config dialogue + if(needQuestion && !item->questionControl().empty()) + if(VProperty* prop=VConfig::instance()->find(item->questionControl())) + needQuestion=prop->value().toBool(); + + if(needQuestion) + { + std::string fullNames("
    "); + std::string nodeNames("
      "); + if (filteredNodes.size() == 1) + { + fullNames = filteredNodes[0]->path(); + nodeNames = "" + filteredNodes[0]->name() + ""; + } + else + { + int numNodes = filteredNodes.size(); + int numItemsToList = std::min(numNodes, 5); + + for(int i=0; i < numItemsToList; i++) + { + fullNames += "
    • "; + fullNames += filteredNodes[i]->path(); + fullNames += "
    • "; + + nodeNames += "
    • "; + nodeNames += filteredNodes[i]->name(); + nodeNames += "
    • "; + } + if(numItemsToList < static_cast(filteredNodes.size())) + { + std::string numExtra = QString::number(numNodes-numItemsToList).toStdString(); + + fullNames += "...and " + numExtra + " more "; + nodeNames += "...and " + numExtra + " more "; + } + fullNames += "
    "; + nodeNames += "
"; + } + + std::string question(item->question()); + + std::string placeholder(""); + ecf::Str::replace_all(question, placeholder, fullNames); + placeholder = ""; + ecf::Str::replace_all(question, placeholder, nodeNames); + + QString msg=QString::fromStdString(question); + + QString warning=QString::fromStdString(item->warning()); + if(!warning.isEmpty()) + { + if(!msg.contains("
    ")) + msg+="

    "; + + msg+="warning: " + Viewer::formatText(warning,QColor(196,103,36)) + "
    "; + } + + if(!item->command().empty()) + { + QString cmdStr=QString::fromStdString(item->command()); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + cmdStr=cmdStr.toHtmlEscaped(); +#else + cmdStr=Qt::escape(cmdStr); +#endif + if(!warning.isEmpty()) + msg+="
    "; + else if(!msg.contains("
      ")) + msg+="

      "; + + msg+="command: " + Viewer::formatText(cmdStr,QColor(41,78,126)) + ""; + msg+="
      "; + } + + QMessageBox msgBox; + msgBox.setText(msg); + msgBox.setTextFormat(Qt::RichText); + msgBox.setIcon(QMessageBox::Question); + msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); + if (msgBox.exec() == QMessageBox::Cancel) + { + ok=false; + } + } + + if(ok) + CommandHandler::run(filteredNodes,item->command()); + + if (customCommandDialog) + delete customCommandDialog; + } + } + +/* + QMenu *menu=new QMenu(parent_); + + QList acLst; + + QAction *ac=new QAction("Requeue",parent_); + acLst << ac; + + ac=new QAction("Submit",parent_); + acLst << ac; + + ac=new QAction("Set as root",parent_); + acLst << ac; + + if(QAction* res=QMenu::exec(acLst,pos,0,parent_)) + { + + if(res->iconText() == "Set as root") + { + emit viewCommand(filteredNodes,"set_as_root"); + } + else + ServerHandler::command(filteredNodes,res->iconText().toStdString()); + } + + delete menu; +*/ +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ActionHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ActionHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ActionHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ActionHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ACTIONHANDLER_HPP_ +#define ACTIONHANDLER_HPP_ + +#include +#include + +#include + +#include "VInfo.hpp" + +class QWidget; +class Node; +class ServerHandler; + +class ActionHandler : public QObject +{ +Q_OBJECT +public: + explicit ActionHandler(QObject*,QWidget* menuParent); + + void contextMenu(std::vector,QPoint); + bool actionHandler(); + +Q_SIGNALS: + void viewCommand(VInfo_ptr,QString); + void infoPanelCommand(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + +protected: + QObject *actionSender_; + QWidget *menuParent_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,105 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "AddModelColumnDialog.hpp" +#include "ModelColumn.hpp" + +#include +#include + +#include "ui_AddModelColumnDialog.h" + +AddModelColumnDialog::AddModelColumnDialog(QWidget *parent) : + QDialog(parent), + ui(new Ui::AddModelColumnDialog) +{ + ui->setupUi(this); +} + +AddModelColumnDialog::~AddModelColumnDialog() +{ + delete ui; +} + +void AddModelColumnDialog::init(ModelColumn* mc,const std::set& vars,QString defaultText) +{ + modelColumn_=mc; + QStringList varLst; + for(std::set::const_iterator it=vars.begin(); + it != vars.end(); ++it) + { + int idx=-1; + bool ok=true; + QString n=QString::fromStdString(*it); + if((idx=modelColumn_->indexOf(n)) != -1) + ok=!modelColumn_->isExtra(idx); + + if(ok) + varLst << n; + } + + QCompleter *c=new QCompleter(varLst,this); + c->setCaseSensitivity(Qt::CaseInsensitive); + ui->variableLe->setCompleter(c); + ui->variableLe->setText(defaultText); +} + +void AddModelColumnDialog::accept() +{ + QString name=ui->variableLe->text(); + if(modelColumn_->indexOf(name) == -1) + { + modelColumn_->addExtraItem(name,name); + } + else + { + QMessageBox::warning(this,"Column already defined",tr("Column \'") + name + + tr("\' is already added to table view. Please choose another variable!"), + QMessageBox::Ok,QMessageBox::NoButton); + return; + } + + QDialog::accept(); +} + +ChangeModelColumnDialog::ChangeModelColumnDialog(QWidget *parent) : + AddModelColumnDialog(parent) +{ + setWindowTitle(tr("Change column in table view")); +} + + +void ChangeModelColumnDialog::setColumn(QString cname) +{ + columnName_=cname; + ui->variableLe->setText(columnName_); + setWindowTitle(tr("Change column ") + columnName_ + tr(" in table view")); +} + +void ChangeModelColumnDialog::accept() +{ + QString name=ui->variableLe->text(); + if(modelColumn_->indexOf(name) == -1) + { + int colIdx=modelColumn_->indexOf(columnName_); + if(colIdx != -1) + { + modelColumn_->changeExtraItem(colIdx,name,name); + } + } + else + { + QMessageBox::warning(this,"Column already defined",tr("Column ") + name + + tr(" is already added to table view. Please choose another variable!"), + QMessageBox::Ok,QMessageBox::NoButton); + return; + } + + QDialog::accept(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,59 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef ADDMODELCOLUMNDIALOG_HPP +#define ADDMODELCOLUMNDIALOG_HPP + +#include +#include +#include +#include + +class ModelColumn; + +namespace Ui { +class AddModelColumnDialog; +} + +class AddModelColumnDialog : public QDialog +{ + Q_OBJECT + +public: + explicit AddModelColumnDialog(QWidget *parent = 0); + virtual ~AddModelColumnDialog(); + + void init(ModelColumn* mc,const std::set&,QString defaultText = QString("")); + +public Q_SLOTS: + void accept(); + +protected: + Ui::AddModelColumnDialog *ui; + ModelColumn* modelColumn_; +}; + +class ChangeModelColumnDialog : public AddModelColumnDialog +{ + Q_OBJECT + +public: + explicit ChangeModelColumnDialog(QWidget *parent = 0); + + void setColumn(QString); + +public Q_SLOTS: + void accept(); + +protected: + QString columnName_; +}; + + +#endif // ADDMODELCOLUMNDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/AddModelColumnDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AddModelColumnDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ + + + AddModelColumnDialog + + + + 0 + 0 + 402 + 89 + + + + Add new column to table view + + + + + + + + Variable name: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + AddModelColumnDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + AddModelColumnDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Animation.cpp ecflow-4.11.1/Viewer/ecflowUI/src/Animation.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Animation.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Animation.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "Animation.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" + +#include + +//===================================================== +// +// Animation +// +//===================================================== + +Animation::Animation(QWidget *view,Type type) : + view_(view), + type_(type) +{ + if(type_ == ServerLoadType) + setFileName(":/viewer/spinning_wheel.gif"); + + setScaledSize(QSize(12,12)); + + connect(this,SIGNAL(frameChanged(int)), + this,SLOT(renderFrame(int))); + + connect(this,SIGNAL(repaintRequest(Animation*)), + view_,SLOT(slotRepaint(Animation*))); +} + +void Animation::addTarget(VNode *n) +{ + Q_ASSERT(n); + if(!targets_.contains(n)) + targets_ << n; + + ServerHandler* s=n->server(); + Q_ASSERT(s); + s->addServerObserver(this); + + start(); +} + +void Animation::removeTarget(VNode* n) +{ + targets_.removeAll(n); + + if(targets_.isEmpty()) + QMovie::stop(); +} + +void Animation::renderFrame(int frame) +{ + if(targets_.isEmpty()) + QMovie::stop(); + + Q_EMIT(repaintRequest(this)); +} + +void Animation::notifyServerDelete(ServerHandler* server) +{ + removeTarget(server->vRoot()); + server->removeServerObserver(this); + //TODO: make it work for nodes as well +} + +void Animation::notifyBeginServerClear(ServerHandler* /*server*/) +{ + //TODO: make it work for nodes +} + +//===================================================== +// +// AnimationHandler +// +//===================================================== + +AnimationHandler::AnimationHandler(QWidget* view) : view_(view) +{ + +} + +AnimationHandler::~AnimationHandler() +{ + Q_FOREACH(Animation* an,items_) + { + delete an; + } +} + +Animation* AnimationHandler::find(Animation::Type type, bool makeIt) +{ + QMap::iterator it=items_.find(type); + if(it != items_.end()) + { + return it.value(); + } + else if(makeIt) + { + Animation* an=new Animation(view_,type); + items_[type]=an; + return an; + } + + return 0; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Animation.hpp ecflow-4.11.1/Viewer/ecflowUI/src/Animation.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Animation.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Animation.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef ANIMATION_HPP_ +#define ANIMATION_HPP_ + +#include +#include +#include + +#include "ServerObserver.hpp" +#include "VNode.hpp" + +class VNode; + +class Animation : public QMovie, public ServerObserver +{ +Q_OBJECT + +public: + enum Type {ServerLoadType}; + Animation(QWidget*,Type); + + void addTarget(VNode*); + void removeTarget(VNode*); + QList targets() const {return targets_;} + + void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + +Q_SIGNALS: + void repaintRequest(Animation*); + +protected Q_SLOTS: + void renderFrame(int); + +protected: + QWidget* view_; + QList targets_; + Type type_; +}; + +class AnimationHandler +{ +public: + explicit AnimationHandler(QWidget* view); + ~AnimationHandler(); + Animation* find(Animation::Type,bool makeIt); + +protected: + QWidget* view_; + QMap items_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AstCollateVNodesVisitor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AstCollateVNodesVisitor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AstCollateVNodesVisitor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AstCollateVNodesVisitor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,110 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AstCollateVNodesVisitor.hpp" + +#include + +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +static std::vector attrTypes; + +AstCollateVNodesVisitor::AstCollateVNodesVisitor(std::vector& s) : items_(s) +{ + if(attrTypes.empty()) + { + QStringList types; + types << "event" << "meter" << "var" << "genvar"; + Q_FOREACH(QString name,types) + { + VAttributeType *t=VAttributeType::find(name.toStdString()); + Q_ASSERT(t); + attrTypes.push_back(t); + + } + } +} + +AstCollateVNodesVisitor::~AstCollateVNodesVisitor() {} + +void AstCollateVNodesVisitor::visitEventState(AstEventState* astNode) +{ +} + +void AstCollateVNodesVisitor::visitNode(AstNode* astNode) +{ + if(Node* referencedNode = astNode->referencedNode()) + { + if(VNode* n=static_cast(referencedNode->graphic_ptr())) + { + items_.push_back(n); + } + } +} + +void AstCollateVNodesVisitor::visitVariable(AstVariable* astVar) +{ + if(Node* referencedNode = astVar->referencedNode()) + { + if(VNode* n=static_cast(referencedNode->graphic_ptr())) + { + std::size_t nType=attrTypes.size(); + std::size_t nItem=items_.size(); + for(std::size_t i=0; i < nType; i++) + { + if(VAttribute *a=n->findAttribute(attrTypes[i],astVar->name())) + { + for(std::size_t k=0; k < nItem; k++) + { + if(a == items_[k]) + return; + } + + items_.push_back(a); + return; + } + } + } + } +} + +void AstCollateVNodesVisitor::visitParentVariable(AstParentVariable* astVar) +{ + if(Node* referencedNode = astVar->referencedNode()) + { + if(VNode* n=static_cast(referencedNode->graphic_ptr())) + { + std::size_t nType=attrTypes.size(); + std::size_t nItem=items_.size(); + for(std::size_t i=0; i < nType; i++) + { + if(VAttribute *a=n->findAttribute(attrTypes[i],astVar->name())) + { + for(std::size_t k=0; k < nItem; k++) + { + if(a == items_[k]) + return; + } + + items_.push_back(a); + return; + } + } + } + } +} + +void AstCollateVNodesVisitor::visitFlag(AstFlag* astVar) +{ + // ??? +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AstCollateVNodesVisitor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AstCollateVNodesVisitor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AstCollateVNodesVisitor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AstCollateVNodesVisitor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,57 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ASTCOLLATEVNODESVISITOR_HPP +#define ASTCOLLATEVNODESVISITOR_HPP + +#include "ExprAstVisitor.hpp" + +#include + +class VItem; + +class AstCollateVNodesVisitor : public ecf::ExprAstVisitor +{ +public: + AstCollateVNodesVisitor(std::vector& ); + virtual ~AstCollateVNodesVisitor(); + + virtual void visitTop(AstTop*){} + virtual void visitRoot(AstRoot*){} + virtual void visitAnd(AstAnd*){} + virtual void visitNot(AstNot*){} + virtual void visitPlus(AstPlus*){} + virtual void visitMinus(AstMinus*){} + virtual void visitDivide(AstDivide*){} + virtual void visitMultiply(AstMultiply*){} + virtual void visitModulo(AstModulo*){} + virtual void visitOr(AstOr*){} + virtual void visitEqual(AstEqual*){} + virtual void visitNotEqual(AstNotEqual*){} + virtual void visitLessEqual(AstLessEqual*){} + virtual void visitGreaterEqual(AstGreaterEqual*){} + virtual void visitGreaterThan(AstGreaterThan*){} + virtual void visitLessThan(AstLessThan*){} + virtual void visitLeaf(AstLeaf*){} + virtual void visitInteger(AstInteger*){} + virtual void visitFunction(AstFunction*){} + virtual void visitNodeState(AstNodeState*){} + virtual void visitEventState(AstEventState*); + virtual void visitNode(AstNode*); + virtual void visitVariable(AstVariable*); + virtual void visitParentVariable(AstParentVariable*); + virtual void visitFlag(AstFlag*); + +private: + std::vector& items_; +}; + +#endif // ASTCOLLATEVNODESVISITOR_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,380 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AttributeEditor.hpp" + +#include "AttributeEditorFactory.hpp" +#include "ConnectState.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "ServerHandler.hpp" +#include "UiLogS.hpp" +#include "VConfig.hpp" +#include "VRepeatAttr.hpp" + +#include + +#define _USE_MODELESS_ATTRIBUTEDITOR +#define _UI_ATTRIBUTEDITOR_DEBUG + +#ifdef _USE_MODELESS_ATTRIBUTEDITOR +static QList editors; +#endif + +AttributeEditor::AttributeEditor(VInfo_ptr info,QString type,QWidget* parent) : QDialog(parent), info_(info), form_(0), type_(type) +{ + setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + +#ifdef _USE_MODELESS_ATTRIBUTEDITOR + setModal(false); +#endif + + Q_ASSERT(info_ && info_->isAttribute() && info_->attribute()); + messageLabel_->hide(); + + attrData_=info_->attribute()->data(); + + QString wt="Edit " + type; + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + attachInfo(); + + connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), + this,SLOT(slotButton(QAbstractButton*))); +} + +AttributeEditor::~AttributeEditor() +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG +#endif + detachInfo(); +#ifdef _USE_MODELESS_ATTRIBUTEDITOR + editors.removeOne(this); +#endif +} + +void AttributeEditor::edit(VInfo_ptr info,QWidget *parent) +{ + Q_ASSERT(info && info->isAttribute() && info->attribute()); + VAttribute* a=info->attribute(); + Q_ASSERT(a->type()); + +#ifdef _USE_MODELESS_ATTRIBUTEDITOR + Q_FOREACH(AttributeEditor* e,editors) + { + if((e->info_ && info) && + *(e->info_.get()) == *(info.get())) + { + e->raise(); + return; + } + } +#endif + + //For repeats we create an editor for each type + std::string typeStr=a->type()->strName(); + if(a->type() == VAttributeType::find("repeat")) + { + typeStr+="_" + a->subType(); + } + + //The edtior will be automatically deleted on close (Qt::WA_DeleteOnClose is set) + if(AttributeEditor* e=AttributeEditorFactory::create(typeStr,info,0)) + { +#ifdef _USE_MODELESS_ATTRIBUTEDITOR + editors << e; + e->show(); +#else + e->exec(); +#endif + } +} + +void AttributeEditor::addForm(QWidget* w) +{ + form_=w; + mainLayout_->insertWidget(3,w,1); +} + +void AttributeEditor::accept() +{ + apply(); + QDialog::accept(); +} + +void AttributeEditor::attachInfo() +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG +#endif + + if(info_) + { + if(info_->server()) + { + info_->server()->addNodeObserver(this); + info_->server()->addServerObserver(this); + } + + info_->addObserver(this); + } +} + +void AttributeEditor::detachInfo() +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG +#endif + if(info_) + { + if(info_->server()) + { +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " remove NodeObserver " << this; +#endif + info_->server()->removeNodeObserver(this); +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " remove ServerObserver " << this; +#endif + info_->server()->removeServerObserver(this); + } +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " remove InfoObserver"; +#endif + info_->removeObserver(this); + } + + messageLabel_->stopLoadLabel(); +} + +void AttributeEditor::slotButton(QAbstractButton* b) +{ + if(b && buttonBox_->buttonRole(b) == QDialogButtonBox::ResetRole) + resetValue(); +} + +void AttributeEditor::checkButtonStatus() +{ + setResetStatus(isValueChanged()); + setSaveStatus(isValueChanged()); +} + +void AttributeEditor::setResetStatus(bool st) +{ + QPushButton *resetpb=buttonBox_->button(QDialogButtonBox::Reset); + Q_ASSERT(resetpb); + resetpb->setEnabled(st); +} + +void AttributeEditor::setSaveStatus(bool st) +{ + QPushButton *savepb=buttonBox_->button(QDialogButtonBox::Save); + Q_ASSERT(savepb); + savepb->setEnabled(st); +} + +void AttributeEditor::setSuspended(bool st) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG + UiLog().dbg() << " status=" << st; +#endif + + Q_ASSERT(form_); + form_->setEnabled(!st); + + QPushButton *savePb=buttonBox_->button(QDialogButtonBox::Save); + Q_ASSERT(savePb); + savePb->setEnabled(!st); + + QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset); + Q_ASSERT(resetPb); + resetPb->setEnabled(!st); +} + +void AttributeEditor::notifyDataLost(VInfo* info) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG +#endif + if(info_ && info_.get() == info) + { + detachInfo(); + messageLabel_->showWarning("The parent node or the edited " + type_ + " is not available anymore! Please close the dialog!"); + setSuspended(true); + } +} + +void AttributeEditor::notifyBeginNodeChange(const VNode* vn, const std::vector& aspect,const VNodeChange&) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG +#endif + if(info_ && info_->node() && info_->node() == vn) + { + bool attrNumCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_ATTR) != aspect.end()); + if(attrNumCh) + { +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " ADD_REMOVE_ATTR"; +#endif + //try to regain the data + info_->regainData(); + + //If the attribute is not available dataLost() was already called. + if(!info_ || !info_->hasData()) + { +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " attribute does not exist"; +#endif + return; + } + + Q_ASSERT(info_->server() && info_->node()); + setSuspended(false); + +#if 0 + if(1) + { +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UiLog().dbg() << " attribute does not exist"; +#endif + detachInfo(); + messageLabel_->showWarning("The edited " + type_ + + " is not available any more! Please close the dialog!"); + setSuspended(true); + } +#endif + } + else + { + nodeChanged(aspect); + } + } +} + +void AttributeEditor::notifyDefsChanged(ServerHandler* server, const std::vector& a) +{ + if(info_ && info_->server() && info_->server() == server) + { + //TODO: implement + + } +} + +void AttributeEditor::notifyServerDelete(ServerHandler* server) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG_S(server) +#endif + if(info_ && info_->server() == server) + { + detachInfo(); + messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + + " was removed from ecFlowUI! The edited " + type_ + + " is not available anymore! Please close the dialog!"); + setSuspended(true); + } +} + +//This must be called at the beginning of a reset +void AttributeEditor::notifyBeginServerClear(ServerHandler* server) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG_S(server) +#endif + + if(info_) + { + if(info_->server() && info_->server() == server) + { + messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + " is being reloaded. \ + Until it is finished this " + type_ + " cannot be modified!"); + + messageLabel_->startLoadLabel(); + + setSuspended(true); + checkButtonStatus(); + } + } +} + +//This must be called at the end of a reset +void AttributeEditor::notifyEndServerScan(ServerHandler* server) +{ +#ifdef _UI_ATTRIBUTEDITOR_DEBUG + UI_FUNCTION_LOG_S(server) +#endif + + if(info_) + { + if(info_->server() && info_->server() == server) + { + messageLabel_->hide(); + messageLabel_->clear(); + + //We try to ressurect the info. We have to do it explicitly because it is not guaranteed + //that notifyEndServerScan() will be first called on other objects than AttributeEditor. So it + //is possible that the node exists but is still set to NULL in VInfo. + info_->regainData(); + + //If the node is not available dataLost() was already called. + if(!info_ || !info_->hasData()) + return; + + Q_ASSERT(info_->server() && info_->node()); + + setSuspended(false); + } + } +} + +void AttributeEditor::notifyServerConnectState(ServerHandler* server) +{ + Q_ASSERT(server); + if(info_->server() && info_->server() == server) + { + switch(server->connectState()->state()) + { + case ConnectState::Lost: + messageLabel_->showWarning("Connection lost to server " + QString::fromStdString(server->name()) + ". \ + Until it the connection regained this " + type_ + " cannot be modified!"); + messageLabel_->stopLoadLabel(); + setSuspended(true); + break; + case ConnectState::Normal: + messageLabel_->hide(); + messageLabel_->clear(); + messageLabel_->stopLoadLabel(); + setSuspended(false); + break; + default: + break; + } + } +} + +void AttributeEditor::doNotUseReset() +{ + if(QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset)) + { + resetPb->setEnabled(false); + resetPb->hide(); + } +} + +void AttributeEditor::disableCancel() +{ + if(QPushButton *cancelPb=buttonBox_->button(QDialogButtonBox::Cancel)) + cancelPb->hide(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,122 @@ + + + AttributeEditorDialog + + + + 0 + 0 + 281 + 171 + + + + Edit label + + + true + + + + 4 + + + + + TextLabel + + + Qt::NoTextInteraction + + + + + + + + + + Qt::Vertical + + + + 20 + 6 + + + + + + + + Qt::Vertical + + + + 20 + 6 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Reset|QDialogButtonBox::Save + + + + + + + + EditorInfoLabel + QLabel +
      EditorInfoLabel.hpp
      +
      + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      +
      + + + + buttonBox_ + accepted() + AttributeEditorDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox_ + rejected() + AttributeEditorDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorFactory.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorFactory.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorFactory.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorFactory.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AttributeEditorFactory.hpp" + +#include + +static std::map* makers = 0; + +AttributeEditorFactory::AttributeEditorFactory(const std::string& type) +{ + if(makers == 0) + makers = new std::map; + + (*makers)[type] = this; +} + +AttributeEditorFactory::~AttributeEditorFactory() +{ + // Not called +} + +AttributeEditor* AttributeEditorFactory::create(const std::string& type,VInfo_ptr info,QWidget* parent) +{ + std::map::iterator j = makers->find(type); + if(j != makers->end()) + return (*j).second->make(info,parent); + + return 0; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorFactory.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorFactory.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditorFactory.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditorFactory.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ATTRIBUTEEDITORFACTORY_HPP +#define ATTRIBUTEEDITORFACTORY_HPP + +#include +#include "VInfo.hpp" + +class AttributeEditor; +class QWidget; + +class AttributeEditorFactory +{ +public: + explicit AttributeEditorFactory(const std::string& type); + virtual ~AttributeEditorFactory(); + + virtual AttributeEditor* make(VInfo_ptr,QWidget*) = 0; + static AttributeEditor* create(const std::string&,VInfo_ptr,QWidget*); + +private: + explicit AttributeEditorFactory(const AttributeEditorFactory&); + AttributeEditorFactory& operator=(const AttributeEditorFactory&); +}; + +template +class AttributeEditorMaker : public AttributeEditorFactory +{ + AttributeEditor* make(VInfo_ptr info,QWidget* parent) { return new T(info,parent); } +public: + explicit AttributeEditorMaker(const std::string& t) : AttributeEditorFactory(t) {} +}; + +#endif // ATTRIBUTEEDITORFACTORY_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,77 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef ATTRIBUTEEDITOR_HPP +#define ATTRIBUTEEDITOR_HPP + +#include + +#include "NodeObserver.hpp" +#include "ServerObserver.hpp" +#include "VInfo.hpp" + +#include "ui_AttributeEditorDialog.h" + +class AttributeEditor : public QDialog, public ServerObserver, public NodeObserver, public VInfoObserver, protected Ui::AttributeEditorDialog +{ +Q_OBJECT + +public: + AttributeEditor(VInfo_ptr info,QString type,QWidget* parent); + virtual ~AttributeEditor(); + + //From VInfoObserver + void notifyDelete(VInfo*) {} + void notifyDataLost(VInfo*); + + //From NodeObserver + void notifyBeginNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&); + void notifyEndNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&) {} + + //From ServerObserver + void notifyDefsChanged(ServerHandler* server, const std::vector& a); + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + void notifyEndServerClear(ServerHandler* server) {} + void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + void notifyEndServerScan(ServerHandler* server); + void notifyServerConnectState(ServerHandler* server); + void notifyServerSuiteFilterChanged(ServerHandler* server) {} + void notifyServerSyncFinished(ServerHandler* server) {} + + static void edit(VInfo_ptr info,QWidget *parent); + +public Q_SLOTS: + void accept(); + void slotButton(QAbstractButton*); + +protected: + void attachInfo(); + void detachInfo(); + void checkButtonStatus(); + void setResetStatus(bool st); + void setSaveStatus(bool st); + void setSuspended(bool); + void addForm(QWidget* w); + void hideForm(); + void doNotUseReset(); + void disableCancel(); + virtual void apply()=0; + virtual void resetValue()=0; + virtual bool isValueChanged()=0; + virtual void nodeChanged(const std::vector& a) {} + + VInfo_ptr info_; + QStringList attrData_; + QWidget* form_; + QString type_; +}; + +#endif // ATTRIBUTEEDITOR_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeSearchPanel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeSearchPanel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeSearchPanel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeSearchPanel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,219 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "AttributeSearchPanel.hpp" + +#include "CaseSensitiveButton.hpp" +#include "NodeQuery.hpp" +#include "NodeQueryOptionEdit.hpp" +#include "StringMatchCombo.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +//====================================================== +// +// AttributeLineDesc +// +//====================================================== + +class AttrLineDesc +{ +public: + AttrLineDesc(QString name,int row) : name_(name), row_(row) {} + virtual ~AttrLineDesc() {} + + virtual QString value()=0; + QString name() const {return name_;} + int row() const {return row_;} + +protected: + QString name_; + int row_; +}; + +class AttrLineStringDesc : public AttrLineDesc +{ +public: + AttrLineStringDesc(QString name,int row,QLineEdit *le) : AttrLineDesc(name,row), le_(le) {} + + QString value() {return le_->text();} + +protected: + QLineEdit* le_; +}; + + +//====================================================== +// +// AttributeGroupDesc +// +//====================================================== + +class AttrGroupDesc +{ +public: + AttrGroupDesc(QString name,QGridLayout* grid) : name_(name), grid_(grid) {} + +#if 0 + QString query() const; +#endif + void hide(); + void show(); + void add(AttrLineDesc* line) {lines_ << line;} + +protected: + QString name_; + QList lines_; + QGridLayout* grid_; +}; + +void AttrGroupDesc::hide() +{ + Q_FOREACH(AttrLineDesc* item,lines_) + { + int row=item->row(); + for(int i=0; i < grid_->columnCount(); i++) + { + if(QLayoutItem *li=grid_->itemAtPosition(row,i)) + { + if(li->widget()) + { + li->widget()->hide(); + } + } + } + } +} + +void AttrGroupDesc::show() +{ + Q_FOREACH(AttrLineDesc* item,lines_) + { + int row=item->row(); + for(int i=0; i < grid_->columnCount(); i++) + { + if(QLayoutItem *li=grid_->itemAtPosition(row,i)) + { + if(li->widget()) + { + li->widget()->show(); + } + } + } + } +} + +//====================================================== +// +// AttributeSearchPanel +// +//====================================================== + +AttributeSearchPanel::AttributeSearchPanel(QWidget* parent) : + QWidget(parent), + query_(NULL) +{ + //setupUi(this); +} + +AttributeSearchPanel::~AttributeSearchPanel() +{ +} + +void AttributeSearchPanel::setQuery(NodeQuery* query) +{ + query_=query; +} + +void AttributeSearchPanel::setSelection(QStringList lst) +{ + if(lst == selection_) + return; + + QMapIterator > it(groups_); + while (it.hasNext()) + { + it.next(); + bool inNew=lst.contains(it.key()); + bool inCurrent=selection_.contains(it.key()); + + bool st=false; + if(inNew && !inCurrent) + { + st=true; + } + else if(!inNew && inCurrent) + { + st=false; + } + + Q_FOREACH(NodeQueryOptionEdit* e,it.value()) + { + e->setVisible(st); + } + + } + + if(lst.isEmpty()) + hide(); + else + show(); + + selection_=lst; + + buildQuery(); +} + +void AttributeSearchPanel::clearSelection() +{ + setSelection(QStringList()); +} + +void AttributeSearchPanel::slotOptionEditChanged() +{ + +} + +void AttributeSearchPanel::buildQuery() +{ + /*query_.clear(); + + QMapIterator it(groups_); + while (it.hasNext()) + { + it.next(); + QString name=it.key(); + if(selection_.contains(name)) + { + if(!query_.isEmpty()) + query_+=" or "; + query_+=it.value()->query(); + } + } + + Q_EMIT queryChanged();*/ +} + +QStringList AttributeSearchPanel::groupNames() const +{ + return groups_.keys(); +} + +void AttributeSearchPanel::init() +{ +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeSearchPanel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeSearchPanel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeSearchPanel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeSearchPanel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ +#define VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ + +#include +#include + +class QGridLayout; +class QStandardItemModel; +class AttrGroupDesc; +class NodeQuery; +class NodeQueryOptionEdit; + + +class AttributeSearchPanel : public QWidget +{ + Q_OBJECT + +public: + explicit AttributeSearchPanel(QWidget *parent = 0); + ~AttributeSearchPanel(); + //QString query() const {return query_;} + QStringList groupNames() const; + void setQuery(NodeQuery*); + void init(); + +public Q_SLOTS: + void buildQuery(); + void setSelection(QStringList); + void clearSelection(); + +protected Q_SLOTS: + void slotOptionEditChanged(); + +// void slotTextEdited(QString); +// void slotMatchChanged(int); +#if 0 + void slotCaseChanged(bool); +#endif + +Q_SIGNALS: + void queryChanged(); + +private: + //void addStringLine(QString labelTxt,QString text,QString group); + + NodeQuery* query_; + QMap > groups_; + QGridLayout* grid_; + QStringList selection_; + +}; + +#endif /* VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/AttributeView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/AttributeView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/AttributeView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/AttributeView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,9 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CaseSensitiveButton.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CaseSensitiveButton.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CaseSensitiveButton.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CaseSensitiveButton.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CaseSensitiveButton.hpp" + +CaseSensitiveButton::CaseSensitiveButton(QWidget *parent) : QToolButton(parent) +{ + connect(this,SIGNAL(clicked(bool)), + this,SLOT(slotClicked(bool))); + + setCheckable(true); + setIcon(QPixmap(":/viewer/case_sensitive.svg")); + + tooltip_[true]=tr("Case sensitivity is on. Toggle to turn it off."); + tooltip_[false]=tr("Case sensitivity is off. Toggle to turn it on."); + + setToolTip(tooltip_[false]); +} + +void CaseSensitiveButton::slotClicked(bool b) +{ + setToolTip(tooltip_[b]); + Q_EMIT changed(b); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CaseSensitiveButton.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CaseSensitiveButton.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CaseSensitiveButton.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CaseSensitiveButton.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,35 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ +#define VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ + +#include + +#include + +class CaseSensitiveButton : public QToolButton +{ +Q_OBJECT + +public: + explicit CaseSensitiveButton(QWidget* parent=0); + +protected Q_SLOTS: + void slotClicked(bool); + +Q_SIGNALS: + void changed(bool); + +private: + std::map tooltip_; +}; + +#endif /* VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotify.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotify.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotify.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotify.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,493 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ChangeNotify.hpp" + +#include "ChangeNotifyDialog.hpp" +#include "ChangeNotifyModel.hpp" +#include "ChangeNotifyWidget.hpp" +#include "ServerHandler.hpp" +#include "Sound.hpp" +#include "UiLog.hpp" +#include "VConfig.hpp" +#include "VConfigLoader.hpp" +#include "VNode.hpp" +#include "VNodeList.hpp" +#include "VProperty.hpp" + +#include + +//#ifdef ECFLOW_QT5 +//#include +//#endif + +#include +#include + +#include + +static std::map items; + +static AbortedNotify abortedNotify("aborted"); +static ChangeNotify restaredtNotify("restarted"); +static ChangeNotify lateNotify("late"); +static ChangeNotify zombieNotify("zombie"); +static ChangeNotify aliasNotify("alias"); + +ChangeNotifyDialog* ChangeNotify::dialog_=0; + +//============================================== +// +// ChangeNotify +// +//============================================== + +ChangeNotify::ChangeNotify(const std::string& id) : + id_(id), + enabled_(false), + data_(0), + model_(0), + proxyModel_(0), + prop_(0), + propEnabled_(0) +{ + data_=new VNodeList(); + model_=new ChangeNotifyModel(); + model_->setData(data_); + + proxyModel_=new QSortFilterProxyModel(); + proxyModel_->setSourceModel(model_); + proxyModel_->setDynamicSortFilter(true); + + items[id] = this; +} + +ChangeNotifyModel* ChangeNotify::model() const +{ + return model_; //proxyModel_; +} + +void ChangeNotify::add(VNode *node,bool popup,bool sound) +{ + data_->add(node); + //proxyModel_->invalidate(); + + if(popup) + { + dialog()->setCurrent(this); + dialog()->show(); + dialog()->raise(); + } + else + { + if(!dialog()->isVisible()) + dialog()->setCurrent(this); + else + dialog()->raise(); + } + + if(sound) + { + bool sys=true; + std::string fName; + int loop=0; + if(VProperty* p=prop_->findChild("sound_file_type")) + { + sys=(p->value() == "system"); + } + + if(sys) + { + if(VProperty* p=prop_->findChild("sound_system_file")) + { + fName=p->valueAsStdString(); + } + } + + if(fName.empty()) + return; + + if(VProperty* p=prop_->findChild("sound_loop")) + { + loop=p->value().toInt(); + } + + if(sys) + { + Sound::instance()->playSystem(fName,loop); + } + + } +} + +void ChangeNotify::remove(VNode *node) +{ + data_->remove(node); +} + +void ChangeNotify::setEnabled(bool en) +{ + enabled_=en; + + if(!enabled_) + { + data_->clear(); + } + + proxyModel_->invalidate(); + + if(dialog_) + { + dialog()->setEnabled(this,en); + } + + ChangeNotifyWidget::setEnabled(id_,en); +} + +void ChangeNotify::setProperty(VProperty* prop) +{ + prop_=prop; + + if(VProperty* p=prop->findChild("use_status_colour")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("fill_colour")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("text_colour")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("count_fill_colour")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("count_text_colour")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("sound_file_type")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("sound_system_file")) + { + p->addObserver(this); + + QStringList lst; + const std::vector& vals=Sound::instance()->sysSounds(); + for(std::vector::const_iterator it=vals.begin(); it != vals.end(); ++it) + { + lst << QString::fromStdString(*it); + } + p->setParam("values",lst.join("/")); + p->setParam("values_label",lst.join("/")); + p->setParam("dir",QString::fromStdString(Sound::instance()->sysDir())); + p->addObserver(this); + } + + if(VProperty* p=prop->findChild("sound_user_file")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("sound_volume")) + p->addObserver(this); + + if(VProperty* p=prop->findChild("sound_loop")) + p->addObserver(this); +} + +void ChangeNotify::notifyChange(VProperty* prop) +{ + Q_ASSERT(prop); + + if(prop->name().contains("sound",Qt::CaseInsensitive)) + return; + + else if(prop->name() == "max_item_num") + { + data_->setMaxNum(prop->value().toInt()); + } + //The central settings changed + else if(prop == propEnabled_) + { + //Check if there is any loaded server with this + //notification enabled + bool hasEnabledServer=ServerHandler::checkNotificationState(id_); + updateNotificationState(hasEnabledServer); + } + + dialog()->updateSettings(this); + ChangeNotifyWidget::updateSettings(id_); +} + +void ChangeNotify::updateNotificationState(bool hasEnabledServer) +{ + if(propEnabled_) + setEnabled(propEnabled_->value().toBool() || hasEnabledServer); + else + setEnabled(hasEnabledServer); +} + + +void ChangeNotify::clearData() +{ + data_->clear(); + //proxyModel_->invalidate(); +} + +void ChangeNotify::showDialog(ChangeNotify* notifier) +{ + if(notifier) + dialog()->setCurrent(notifier); + + dialog()->show(); + dialog()->raise(); +} + +QColor ChangeNotify::fillColour() const +{ + if(prop_) + if(VProperty* p=prop_->findChild("fill_colour")) + return p->value().value(); + + return QColor(); +} + +QColor ChangeNotify::textColour() const +{ + if(prop_) + if(VProperty* p=prop_->findChild("text_colour")) + return p->value().value(); + + return QColor(); +} + +QColor ChangeNotify::countFillColour() const +{ + if(prop_) + if(VProperty* p=prop_->findChild("count_fill_colour")) + return p->value().value(); + + return QColor(); +} + +QColor ChangeNotify::countTextColour() const +{ + if(prop_) + if(VProperty* p=prop_->findChild("count_text_colour")) + return p->value().value(); + + return QColor(); +} + +QString ChangeNotify::toolTip() const +{ + return (prop_)?(prop_->param("tooltip")):QString(); +} + +QString ChangeNotify::widgetText() const +{ + return (prop_)?(prop_->param("widgetText")):QString(); +} + +//----------------------------------- +// +// Static methods +// +//----------------------------------- + +void ChangeNotify::add(const std::string& id,VNode *node,bool popup,bool sound) +{ + if(ChangeNotify* obj=ChangeNotify::find(id)) + { + obj->add(node,popup,sound); + } +} + +void ChangeNotify::remove(const std::string& id,VNode *node) +{ + if(ChangeNotify* obj=ChangeNotify::find(id)) + { + obj->remove(node); + } +} + +void ChangeNotify::updateNotificationStateFromServer(const std::string& id,bool en) +{ + if(ChangeNotify* obj=ChangeNotify::find(id)) + { + obj->updateNotificationState(en); + } +} + +ChangeNotify* ChangeNotify::find(const std::string& id) +{ + std::map::iterator it=items.find(id); + if(it != items.end()) + return it->second; + + return 0; +} + +void ChangeNotify::load(VProperty* group) +{ +UI_FUNCTION_LOG + + if(group->name() == "notification") + { + for(int i=0; i < group->children().size(); i++) + { + VProperty *p=group->children().at(i); + if(ChangeNotify* obj=ChangeNotify::find(p->strName())) + { + obj->setProperty(p); + } + } + + //This should be observed by each notification object + if(VProperty* p=group->find("notification.settings.max_item_num")) + { + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + p->addObserver(it->second); + it->second->data_->setMaxNum(p->value().toInt()); + } + } + } + else if(group->name() == "server") + { + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + it->second->loadServerSettings(); + } + } +#if 0 + else if(group->name() == "nstate") + { + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + it->second->loadNodeState(); + } + } +#endif +} + +//Called only once during init +void ChangeNotify::loadServerSettings() +{ +UI_FUNCTION_LOG + + UiLog().dbg() << " id=" << id_; + + std::string v("server.notification." + id_ + ".enabled"); + + UiLog().dbg() << " property=" << v; + + if(VProperty *p=VConfig::instance()->find(v)) + { + propEnabled_=p; + p->addObserver(this); + setEnabled(p->value().toBool()); + } + else + { + UiLog().err() << " Error! Unable to find property: " << v; + } +} + +ChangeNotifyDialog* ChangeNotify::dialog() +{ + if(!dialog_) + { + dialog_=new ChangeNotifyDialog(); + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + dialog_->add(it->second); + } + + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + dialog_->setEnabled(it->second,it->second->isEnabled()); + } + } + + return dialog_; +} + +/* +void ChangeNotify::showDialog(ChangeNotifyconst std::string& id) +{ + dialog()->setCurrentTab(id); + dialog()->show(); + dialog()->raise(); +} +*/ +/*void ChangeNotify::clearData(const std::string& id) +{ + if(ChangeNotify* obj=ChangeNotify::find(id)) + { + obj->data()->clear(); + } +}*/ + +void ChangeNotify::populate(ChangeNotifyWidget* w) +{ + for(std::map::iterator it=items.begin(); it != items.end(); ++it) + { + w->addTb(it->second); + } +} + +//================================================== +// +// AbortedNotify +// +//================================================== + +QColor AbortedNotify::fillColour() const +{ + bool useState=false; + if(prop_) + if(VProperty* p=prop_->findChild("use_status_colour")) + useState=p->value().toBool(); + + if(useState) + { + if(VProperty *nsp=VConfig::instance()->find("nstate.aborted")) + { + if(VProperty* master=nsp->findChild("fill_colour")) + { + return master->value().value(); + } + } + } + + return ChangeNotify::fillColour(); +} + +QColor AbortedNotify::textColour() const +{ + bool useState=false; + if(prop_) + if(VProperty* p=prop_->findChild("use_status_colour")) + useState=p->value().toBool(); + + if(useState) + { + if(VProperty *nsp=VConfig::instance()->find("nstate.aborted")) + { + if(VProperty* master=nsp->findChild("font_colour")) + { + return master->value().value(); + } + } + } + + return ChangeNotify::textColour(); +} + +static SimpleLoader loaderNotify("notification"); +static SimpleLoader loaderServerNotify("server"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,533 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ChangeNotifyDialog.hpp" + +#include "ChangeNotify.hpp" +#include "ChangeNotifyModel.hpp" +#include "MainWindow.hpp" +#include "SessionHandler.hpp" +#include "TreeView.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "VConfig.hpp" +#include "VNodeList.hpp" +#include "VProperty.hpp" +#include "WidgetNameProvider.hpp" +#include "WmWorkspaceHandler.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +Q_DECLARE_METATYPE(QList) + +//=========================================================== +// +// ChangeNotifyDialogButton +// +//=========================================================== + +ChangeNotifyDialogButton::ChangeNotifyDialogButton(QWidget* parent) : + QToolButton(parent), + notifier_(0) +{ + setProperty("notify","1"); + setAutoRaise(true); + setIconSize(QSize(20,20)); + setCheckable(true); +} + +void ChangeNotifyDialogButton::setNotifier(ChangeNotify* notifier) +{ + notifier_=notifier; + + setText(notifier_->widgetText()); + setToolTip(notifier_->toolTip()); + + connect(notifier_->data(),SIGNAL(endAppendRow()), + this,SLOT(slotAppend())); + + connect(notifier_->data(),SIGNAL(endRemoveRow(int)), + this,SLOT(slotRemoveRow(int))); + + connect(notifier_->data(),SIGNAL(endReset()), + this,SLOT(slotReset())); + + updateSettings(); +} + +void ChangeNotifyDialogButton::slotAppend() +{ + updateSettings(); +} + +void ChangeNotifyDialogButton::slotRemoveRow(int) +{ + updateSettings(); +} + +void ChangeNotifyDialogButton::slotReset() +{ + updateSettings(); +} + +void ChangeNotifyDialogButton::updateSettings() +{ + setEnabled(notifier_->isEnabled()); + +#if 0 + + QString text; + QString numText; + + if(notifier_->prop()) + { + text=notifier_->prop()->param("widgetText"); + } + + int num=0; + if(notifier_->data()) + { + num=notifier_->data()->size(); + if(num > 0 && num < 10) + numText=QString::number(num); + else if(num > 10) + numText="9+"; + + } +#endif +} + +//=========================================================== +// +// ChangeNotifyDialogWidget +// +//=========================================================== + +ChangeNotifyDialogWidget::ChangeNotifyDialogWidget(QWidget *parent) : + QWidget(parent), + notifier_(0) +{ + setupUi(this); +} + +void ChangeNotifyDialogWidget::init(ChangeNotify* notifier) +{ + notifier_=notifier; + + tree_->setModel(notifier_->model()); + label_->setText(notifier_->widgetText()); + + +#if 0 + connect(notifier->data(),SIGNAL(endAppendRow()), + this,SLOT(slotAppend())); + + connect(notifier->data(),SIGNAL(endRemoveRow(int)), + this,SLOT(slotRemoveRow(int))); + + connect(notifier->data(),SIGNAL(endReset()), + this,SLOT(slotReset())); +#endif + + //Selection + connect(tree_,SIGNAL(clicked(const QModelIndex&)), + this,SLOT(slotSelectItem(const QModelIndex&))); + + connect(tree_,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClickItem(const QModelIndex&))); + + updateSettings(); +} + +#if 0 +void ChangeNotifyDialogWidget::slotAppend() +{ + Q_EMIT contentsChanged(); +} + +void ChangeNotifyDialogWidget::slotRemoveRow(int) +{ + Q_EMIT contentsChanged(); +} + +void ChangeNotifyDialogWidget::slotReset() +{ + Q_EMIT contentsChanged(); +} +#endif + +void ChangeNotifyDialogWidget::updateSettings() +{ + Q_ASSERT(notifier_); + QColor bgCol=notifier_->fillColour(); + QColor textCol=notifier_->textColour(); + QColor bgLight=bgCol.lighter(105); + + QString st="QLabel { \ + background: qlineargradient(x1 :0, y1: 0, x2: 0, y2: 1, \ + stop: 0 " + bgLight.name() + ", stop: 1 " + bgCol.name() + "); color: " + + textCol.name() + "; padding: 4px; border: 1px solid rgb(170,170,170);}"; + + UiLog().dbg() << bgCol << " " << textCol; + UiLog().dbg() << st; + + label_->setStyleSheet(st); +} + +void ChangeNotifyDialogWidget::slotSelectItem(const QModelIndex& idx) +{ + //QModelIndexList lst=tree_->selectedIndexes(); + //if(lst.count() > 0) + //{ + VInfo_ptr info=notifier_->model()->nodeInfo(idx); + if(info) + { + Q_EMIT selectionChanged(info); + } + //} +} + +void ChangeNotifyDialogWidget::slotDoubleClickItem(const QModelIndex&) +{ + +} + +void ChangeNotifyDialogWidget::writeSettings(QSettings& settings) +{ + QList wVec; + for(int i=0; i < tree_->model()->columnCount(QModelIndex()); i++) + { + wVec.push_back(tree_->columnWidth(i)); + } + settings.setValue("colWidth",QVariant::fromValue(wVec)); +} + +void ChangeNotifyDialogWidget::readSettings(const QSettings& settings) +{ + QList wVec; + if(settings.contains("colWidth")) + { + wVec=settings.value("colWidth").value >(); + if(wVec.count() >0 && wVec[0] < 1) + wVec.clear(); + } + + if(!wVec.isEmpty()) + { + for(int i=0; i < tree_->model()->columnCount(QModelIndex()); i++) + { + if(wVec.count() > i && wVec[i] > 0) + tree_->setColumnWidth(i,wVec[i]); + } + } + else + { + if(tree_->model()->columnCount(QModelIndex()) > 1) + { + QFont f; + QFontMetrics fm(f); + tree_->setColumnWidth(0,fm.width("serverserverserver")); + tree_->setColumnWidth(1,fm.width("/suite1/family1/family2/family3/family4/task")); + } + } +} + +//=========================================================== +// +// ChangeNotifyDialog +// +//=========================================================== + +ChangeNotifyDialog::ChangeNotifyDialog(QWidget *parent) : + QDialog(parent), + ignoreCurrentChange_(false), + switchWsProp_(0) +{ + setupUi(this); + + buttonHb_= new QHBoxLayout(buttonW_); + buttonHb_->setContentsMargins(0,0,0,0); + buttonHb_->setSpacing(2); + + buttonGroup_=new QButtonGroup(this); + connect(buttonGroup_,SIGNAL(buttonToggled(int,bool)), + this,SLOT(slotButtonToggled(int,bool))); + + clearOnCloseCb_->setChecked(true); + + connect(optionsTb_,SIGNAL(clicked()), + this,SLOT(slotOptions())); + + switchWsProp_=VConfig::instance()->find("notification.settings.switch_desktop"); + + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +ChangeNotifyDialog::~ChangeNotifyDialog() +{ + writeSettings(); +} + +void ChangeNotifyDialog::add(ChangeNotify* notifier) +{ + ChangeNotifyDialogWidget* w=new ChangeNotifyDialogWidget(this); + w->init(notifier); + + connect(w,SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChanged(VInfo_ptr))); + + ignoreCurrentChange_=true; + stacked_->addWidget(w); + ignoreCurrentChange_=false; + ntfWidgets_ << w; + + + + ChangeNotifyDialogButton *bw=new ChangeNotifyDialogButton(this); + bw->setNotifier(notifier); + buttonHb_->addWidget(bw); + int buttonId=buttonGroup_->buttons().count(); + buttonGroup_->addButton(bw,buttonId); + ntfButtons_ << bw; + + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + + readNtfWidgetSettings(stacked_->count()-1); +} + +void ChangeNotifyDialog::slotButtonToggled(int,bool) +{ + int idx=buttonGroup_->checkedId(); + if(idx != -1) + { + stacked_->setCurrentIndex(idx); + } +} + +void ChangeNotifyDialog::slotSelectionChanged(VInfo_ptr info) +{ + //Moves the dialogue to the virtual workspace of the first + //mainwindow and then switches the workspace + if(switchWsProp_ && switchWsProp_->value().toBool()) + { + if(WmWorkspaceHandler::switchTo(this,MainWindow::firstWindow())) + raise(); + } + + MainWindow::lookUpInTree(info); +} + +void ChangeNotifyDialog::slotOptions() +{ + QString op="notification"; + if(ChangeNotify* notifier=indexToNtf(stacked_->currentIndex())) + { + op+="." + QString::fromStdString(notifier->id()); + } + MainWindow::startPreferences(op); +} + +void ChangeNotifyDialog::setCurrent(ChangeNotify *ntf) +{ + int idx=ntfToIndex(ntf); + if(idx != -1) + { + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); + UI_ASSERT(idx >=0,"idx=" << idx); + + buttonGroup_->button(idx)->setChecked(true); + } +} + +void ChangeNotifyDialog::setEnabled(ChangeNotify* ntf,bool b) +{ + int idx=ntfToIndex(ntf); + if(idx != -1) + { + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); + UI_ASSERT(idx >=0,"idx=" << idx); + + buttonGroup_->button(idx)->setEnabled(b); + stacked_->widget(idx)->setEnabled(b); + } +} + +void ChangeNotifyDialog::clearCurrentData() +{ + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + + int idx=buttonGroup_->checkedId(); + if(idx != -1) + { + UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); + UI_ASSERT(idx >=0,"idx=" << idx); + if(ChangeNotify *ntf=indexToNtf(idx)) + ntf->clearData(); + } +} + +void ChangeNotifyDialog::on_closePb__clicked(bool) +{ + hide(); + + if(clearOnCloseCb_->isChecked()) + { + clearCurrentData(); + } + + writeSettings(); +} + +void ChangeNotifyDialog::on_clearPb__clicked(bool) +{ + clearCurrentData(); +} + +ChangeNotify* ChangeNotifyDialog::indexToNtf(int idx) +{ + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + + if(idx >=0 && idx < stacked_->count()) + { + UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); + UI_ASSERT(idx >=0,"idx=" << idx); + return ntfWidgets_[idx]->notifier(); + } + + return 0; +} + +int ChangeNotifyDialog::ntfToIndex(ChangeNotify* ntf) +{ + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + + for(int i=0; i < stacked_->count(); i++) + { + if(ntfWidgets_[i]->notifier() == ntf) + return i; + } + + return -1; +} + +void ChangeNotifyDialog::updateSettings(ChangeNotify* notifier) +{ + UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), + "stacked_->count()=" << stacked_->count() << + " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); + + int idx=ntfToIndex(notifier); + if(idx != -1) + { + UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); + UI_ASSERT(idx >=0,"idx=" << idx); + //if(stacked_->widget(idx)->isEnabled()) + //{ + ntfWidgets_[idx]->updateSettings(); + ntfButtons_[idx]->updateSettings(); + //} + } +} + +void ChangeNotifyDialog::closeEvent(QCloseEvent* e) +{ + if(clearOnCloseCb_->isChecked()) + { + clearCurrentData(); + } + writeSettings(); + e->accept(); +} + +void ChangeNotifyDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.setValue("clearOnClose",clearOnCloseCb_->isChecked()); + settings.endGroup(); + + for(int i=0; i < stacked_->count(); i++) + { + settings.beginGroup("tab_" + QString::number(i)); + ntfWidgets_[i]->writeSettings(settings); + settings.endGroup(); + } +} + +void ChangeNotifyDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(540,460)); + } + + if(settings.contains("clearOnClose")) + { + clearOnCloseCb_->setChecked(settings.value("clearOnClose").toBool()); + } + + settings.endGroup(); + + //The tab settings are read when the actual tabs are created later. +} + +void ChangeNotifyDialog::readNtfWidgetSettings(int idx) +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), + QSettings::NativeFormat); + + settings.beginGroup("tab_" + QString::number(idx)); + UI_ASSERT(stacked_->count() > idx,"stacked_->count()=" << stacked_->count() << " idx=" << idx); + ntfWidgets_[idx]->readSettings(settings); + settings.endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,116 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef CHANGENOTIFYDIALOG_HPP_ +#define CHANGENOTIFYDIALOG_HPP_ + +#include +#include +#include +#include +#include + +#include "ui_ChangeNotifyDialog.h" +#include "ui_ChangeNotifyDialogWidget.h" + +#include "VInfo.hpp" + +class ChangeNotify; +class VProperty; + +class QButtonGroup; +class QHBoxLayout; +class QLabel; + +class ChangeNotifyDialogButton : public QToolButton +{ +Q_OBJECT + +public: + explicit ChangeNotifyDialogButton(QWidget* parent=0); + + void setNotifier(ChangeNotify*); + void updateSettings(); + +public Q_SLOTS: + void slotAppend(); + void slotRemoveRow(int); + void slotReset(); + +protected: + ChangeNotify* notifier_; +}; + +class ChangeNotifyDialogWidget : public QWidget, protected Ui::ChangeNotifyDialogWidget +{ + Q_OBJECT + +public: + explicit ChangeNotifyDialogWidget(QWidget* parent=0); + ~ChangeNotifyDialogWidget() {} + + void init(ChangeNotify*); + void updateSettings(); + ChangeNotify* notifier() const {return notifier_;} + void writeSettings(QSettings& settings); + void readSettings(const QSettings& settings); + +protected Q_SLOTS: + void slotSelectItem(const QModelIndex&); + void slotDoubleClickItem(const QModelIndex&); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + +protected: + ChangeNotify* notifier_; +}; + +class ChangeNotifyDialog : public QDialog, protected Ui::ChangeNotifyDialog +{ +Q_OBJECT + +public: + explicit ChangeNotifyDialog(QWidget *parent=0); + ~ChangeNotifyDialog(); + + void add(ChangeNotify*); + void setCurrent(ChangeNotify*); + void setEnabled(ChangeNotify*,bool b); + void updateSettings(ChangeNotify*); + +public Q_SLOTS: + void on_closePb__clicked(bool b); + void on_clearPb__clicked(bool b); + +protected Q_SLOTS: + void slotSelectionChanged(VInfo_ptr); + void slotOptions(); + void slotButtonToggled(int,bool); + +protected: + ChangeNotify* indexToNtf(int idx); + int ntfToIndex(ChangeNotify* ntf); + void closeEvent(QCloseEvent*); + void clearCurrentData(); + void writeSettings(); + void readSettings(); + void readNtfWidgetSettings(int tabIndex); + + QList ntfWidgets_; + QList ntfButtons_; + QButtonGroup* buttonGroup_; + bool ignoreCurrentChange_; + QLinearGradient grad_; + QHBoxLayout* buttonHb_; + VProperty* switchWsProp_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,148 @@ + + + ChangeNotifyDialog + + + + 0 + 0 + 400 + 300 + + + + Change notification + + + + 2 + + + 0 + + + 2 + + + 0 + + + 2 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Configure notification options + + + &Preferences + + + + :/viewer/configure.svg:/viewer/configure.svg + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + + + + + + + C&lear current list on close + + + + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Clea&r + + + + + + + &Close + + + + + + + clearOnCloseCb_ + line + holderW + optionsTb_ + stacked_ + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialogWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialogWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyDialogWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyDialogWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,53 @@ + + + ChangeNotifyDialogWidget + + + + 0 + 0 + 502 + 508 + + + + Form + + + + 2 + + + 0 + + + + + true + + + + + + Qt::AlignCenter + + + + + + + false + + + true + + + true + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,368 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ChangeNotifyEditor.hpp" + +#include "PropertyLine.hpp" +#include "VConfig.hpp" +#include "VProperty.hpp" + +#include +#include + +#include + +ChangeNotifyEditor::ChangeNotifyEditor(QWidget* parent) : QWidget(parent) +{ + setupUi(this); + + model_=new ChangeNotifyEditorModel(this); + + tree_->setModel(model_); + + connect(tree_,SIGNAL(activated(QModelIndex)), + this,SLOT(slotRowSelected(QModelIndex))); + + connect(tree_,SIGNAL(clicked(QModelIndex)), + this,SLOT(slotRowSelected(QModelIndex))); + + assert(tab_->count()==0); + //assert(stacked_->count()==0); + + QFont f; + QFontMetrics fm(f); + //tree_->setFixedHeight((fm.height()+4)*5.5); + + tree_->setFixedWidth(fm.width("Restartedaaa")+30); + + //tab_->hide(); +} + +void ChangeNotifyEditor::addRow(QString label,QList lineLst,QWidget *stackContents) +{ + PropertyLine* enabledLine=0; + PropertyLine* popupLine=0; + PropertyLine* soundLine=0; + + QList propLst; + Q_FOREACH(PropertyLine* pl,lineLst) + { + if(pl) + { + propLst << pl->property(); + + if(pl->property()->name() == "enabled") + { + enabledLine=pl; + } + if(pl->property()->name() == "popup") + { + popupLine=pl; + } + if(pl->property()->name() == "sound") + { + soundLine=pl; + } + + } + } + + if(enabledLine) + { + connect(model_,SIGNAL(enabledChanged(VProperty*,QVariant)), + enabledLine,SLOT(slotReset(VProperty*,QVariant))); + + connect(enabledLine,SIGNAL(changed(QVariant)), + model_,SLOT(slotEnabledChanged(QVariant))); + + connect(enabledLine,SIGNAL(masterChanged(bool)), + model_,SLOT(slotEnabledMasterChanged(bool))); + + if(popupLine) + { + connect(enabledLine,SIGNAL(changed(QVariant)), + popupLine,SLOT(slotEnabled(QVariant))); + //init + popupLine->slotEnabled(enabledLine->property()->value()); + } + if(soundLine) + { + connect(enabledLine,SIGNAL(changed(QVariant)), + soundLine,SLOT(slotEnabled(QVariant))); + + //init + soundLine->slotEnabled(enabledLine->property()->value()); + } + } + + model_->add(label,propLst); + + QWidget* w=new QWidget(this); + QVBoxLayout* vb=new QVBoxLayout(); + vb->setContentsMargins(0,5,0,5); + w->setLayout(vb); + vb->addWidget(stackContents); + vb->addStretch(1); + + //stacked_->addWidget(w); + tab_->addTab(w,label); + + tree_->setCurrentIndex(model_->index(0,0)); + + for(int i=0; i < model_->columnCount()-1; i++) + { + tree_->resizeColumnToContents(i); + } +} + +void ChangeNotifyEditor::slotRowSelected(const QModelIndex& idx) +{ + //if(idx.row() == stacked_->currentIndex()) + if(idx.row() == tab_->currentIndex()) + return; + + if(idx.row() >= 0 && idx.row() < tab_->count()) + { + tab_->setCurrentIndex(idx.row()); + } + + /*if(idx.row() >= 0 && idx.row() < stacked_->count()) + { + stacked_->setCurrentIndex(idx.row()); + }*/ +} + +ChangeNotifyEditorModel::ChangeNotifyEditorModel(QObject *parent) : + QAbstractItemModel(parent) +{ + +} + +ChangeNotifyEditorModel::~ChangeNotifyEditorModel() +{ +} + +void ChangeNotifyEditorModel::add(QString label,QList propLst) +{ + beginResetModel(); + + ChangeNotifyEditorModelData d; + + d.label_=label; + + Q_FOREACH(VProperty* p,propLst) + { + if(p) + { + if(p->name() == "enabled") + { + d.enabled_=p; + d.enabledVal_=p->value().toBool(); + d.enabledMaster_=(p->master() && p->useMaster()); + + //Get the description + if(p->parent()) + { + if(VProperty* np=VConfig::instance()->find("notification."+ p->parent()->strName())) + d.desc_=np->param("description"); + } + } + } + } + + data_ << d; + + endResetModel(); +} + +int ChangeNotifyEditorModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return 2; +} + +int ChangeNotifyEditorModel::rowCount( const QModelIndex& parent) const +{ + //Parent is the root: + if(!parent.isValid()) + { + return data_.count(); + } + + return 0; +} + +QVariant ChangeNotifyEditorModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid()) + { + return QVariant(); + } + + int row=index.row(); + if(row < 0 || row >= data_.count()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case 1: + return data_.at(row).label_; + case 2: + return data_.at(row).desc_; + default: + return QVariant(); + } + } + + else if(role == Qt::CheckStateRole) + { + switch(index.column()) + { + case 0: + return (data_.at(row).enabledVal_)?QVariant(Qt::Checked):QVariant(Qt::Unchecked); + default: + return QVariant(); + } + } + + return QVariant(); +} + +bool ChangeNotifyEditorModel::setData(const QModelIndex& index,const QVariant& value, int role) +{ + if(index.column() == 0) + { + int row=index.row(); + if(row <0 || row >= data_.count()) + return false; + + if(role == Qt::CheckStateRole) + { + bool checked=(value.toInt() == Qt::Checked)?true:false; + data_[row].enabledVal_=checked; + Q_EMIT dataChanged(index,index); + Q_EMIT enabledChanged(data_[row].enabled_,checked); + return true; + } + } + + return false; +} + + +QVariant ChangeNotifyEditorModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || role != Qt::DisplayRole) + return QAbstractItemModel::headerData( section, orient, role ); + + if(role == Qt::DisplayRole) + { + switch ( section ) + { + case 0: return tr("E"); + case 1: return tr("Title"); + case 2: return tr("Description"); + default: return QVariant(); + } + } + + return QVariant(); +} + +QModelIndex ChangeNotifyEditorModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex ChangeNotifyEditorModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + +Qt::ItemFlags ChangeNotifyEditorModel::flags( const QModelIndex & index) const +{ + int row=index.row(); + if(row >=0 && row <= data_.count() && + index.column() ==0 && data_.at(row).enabledMaster_) + { + return Qt::ItemIsUserCheckable| Qt::ItemIsSelectable; + } + + Qt::ItemFlags defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if(index.isValid() && index.column() ==0) + defaultFlags=defaultFlags | Qt::ItemIsUserCheckable; + + return defaultFlags; +} + +int ChangeNotifyEditorModel::lineToRow(PropertyLine* line) const +{ + for(int i=0; i < data_.count(); i++) + { + if(data_.at(i).enabled_ == line->property()) + { + return i; + } + } + return -1; +} + + +void ChangeNotifyEditorModel::slotEnabledChanged(QVariant v) +{ + PropertyLine *line=static_cast(sender()); + assert(line); + + int row=lineToRow(line); + if(row != -1) + { + //We want to avoid circular dependencies (e.g. this function is triggered from + //setData. So we have to check if the value is different from the stored one! + if(data_.at(row).enabledVal_ != v.toBool()) + { + data_[row].enabledVal_=v.toBool(); + QModelIndex idx=index(row,0); + Q_EMIT dataChanged(idx,idx); + } + } +} + +void ChangeNotifyEditorModel::slotEnabledMasterChanged(bool b) +{ + PropertyLine *line=static_cast(sender()); + assert(line); + + int row=lineToRow(line); + if(row != -1) + { + QModelIndex idx=index(row,0); + data_[row].enabledMaster_=b; + if(b) + { + data_[row].enabledMaster_=b; + } + Q_EMIT dataChanged(idx,idx); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,90 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ +#define VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ + +#include +#include + +#include +#include "VProperty.hpp" + +#include "ui_ChangeNotifyEditor.h" + +class QGridlayout; + +class ChangeNotifyEditorModel; +class PropertyLine; + +class ChangeNotifyEditor : public QWidget, protected Ui::ChangeNotifyEditor +{ + Q_OBJECT + +public: + explicit ChangeNotifyEditor(QWidget* parent=0); + void addRow(QString,QList,QWidget*); + +protected Q_SLOTS: + void slotRowSelected(const QModelIndex& idx); + +private: + ChangeNotifyEditorModel* model_; +}; + + +class ChangeNotifyEditorModelData +{ +public: + ChangeNotifyEditorModelData() : enabled_(NULL), enabledMaster_(false), enabledVal_(false) {} + + QString label_; + QString desc_; + VProperty* enabled_; + bool enabledMaster_; + bool enabledVal_; +}; + + +class ChangeNotifyEditorModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit ChangeNotifyEditorModel(QObject *parent=0); + ~ChangeNotifyEditorModel(); + + void add(QString,QList); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + bool setData(const QModelIndex & index, const QVariant& value, int role = Qt::EditRole); + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + Qt::ItemFlags flags ( const QModelIndex & index) const; + +public Q_SLOTS: + void slotEnabledChanged(QVariant v); + void slotEnabledMasterChanged(bool b); + +Q_SIGNALS: + void enabledChanged(VProperty*,QVariant); + +protected: + int lineToRow(PropertyLine* line) const; + + QList data_; +}; + + +#endif /* VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.ui ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyEditor.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyEditor.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ + + + ChangeNotifyEditor + + + + 0 + 0 + 502 + 508 + + + + Form + + + + + + false + + + true + + + true + + + + + + + -1 + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotify.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotify.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotify.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotify.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,91 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef CHANGENOTIFYHANDLER_HPP_ +#define CHANGENOTIFYHANDLER_HPP_ + +#include +#include + +#include "VProperty.hpp" + +class ChangeNotifyDialog; +class ChangeNotifyModel; +class ChangeNotifyWidget; +class VProperty; +class VNode; +class VNodeList; + +class QAbstractItemModel; +class QSortFilterProxyModel; + +class ChangeNotify : public VPropertyObserver +{ +public: + explicit ChangeNotify(const std::string& id); + + const std::string& id() const {return id_;} + VNodeList* data() const {return data_;} + VProperty* prop() const {return prop_;} + ChangeNotifyModel* model() const; + QSortFilterProxyModel* proxyModel() const {return proxyModel_;} + bool isEnabled() const {return enabled_;} + void clearData(); + + virtual QColor fillColour() const; + virtual QColor textColour() const; + QColor countFillColour() const; + QColor countTextColour() const; + QString toolTip() const; + QString widgetText() const; + + static void showDialog(ChangeNotify* notifier=0); + + //Form VPropertyObserver + void notifyChange(VProperty*); + + static void add(const std::string&,VNode*,bool,bool); + static void remove(const std::string&,VNode*); + static void populate(ChangeNotifyWidget* w); + static void updateNotificationStateFromServer(const std::string& id,bool hasEnabledServer); + + //Called from VConfigLoader + static void load(VProperty* group); + +protected: + void add(VNode*,bool,bool); + void remove(VNode*); + void setEnabled(bool); + void updateNotificationState(bool hasEnabledServer); + void setProperty(VProperty* prop); + void loadServerSettings(); + + static ChangeNotify* find(const std::string&); + static ChangeNotifyDialog* dialog(); + + std::string id_; + bool enabled_; + VNodeList* data_; + ChangeNotifyModel* model_; + QSortFilterProxyModel* proxyModel_; + VProperty* prop_; + VProperty* propEnabled_; //central settings in config GUI + static ChangeNotifyDialog* dialog_; +}; + +class AbortedNotify : public ChangeNotify +{ +public: + explicit AbortedNotify(const std::string& id) : ChangeNotify(id) {} + QColor fillColour() const; + QColor textColour() const; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,241 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ChangeNotifyModel.hpp" + +#include "VNode.hpp" +#include "VNodeList.hpp" + +#include + +ChangeNotifyModel::ChangeNotifyModel(QObject *parent) : + QAbstractItemModel(parent), + data_(0) +{ +} + +ChangeNotifyModel::~ChangeNotifyModel() +{ +} + +VNodeList* ChangeNotifyModel::data() +{ + return data_; +} + +void ChangeNotifyModel::setData(VNodeList *data) +{ + beginResetModel(); + + data_=data; + + connect(data_,SIGNAL(beginAppendRow()), + this,SLOT(slotBeginAppendRow())); + + connect(data_,SIGNAL(endAppendRow()), + this,SLOT(slotEndAppendRow())); + + connect(data_,SIGNAL(beginRemoveRow(int)), + this,SLOT(slotBeginRemoveRow(int))); + + connect(data_,SIGNAL(endRemoveRow(int)), + this,SLOT(slotEndRemoveRow(int))); + + connect(data_,SIGNAL(beginRemoveRows(int,int)), + this,SLOT(slotBeginRemoveRows(int,int))); + + connect(data_,SIGNAL(endRemoveRows(int,int)), + this,SLOT(slotEndRemoveRows(int,int))); + + connect(data_,SIGNAL(beginReset()), + this,SLOT(slotBeginReset())); + + connect(data_,SIGNAL(endReset()), + this,SLOT(slotEndReset())); + + endResetModel(); +} + +bool ChangeNotifyModel::hasData() const +{ + return (data_ && data_->size() >0); +} + +int ChangeNotifyModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return 3; +} + +int ChangeNotifyModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + //Parent is the root: + if(!parent.isValid()) + { + return data_->size(); + } + + return 0; +} + + +QVariant ChangeNotifyModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid() || !hasData()) + { + return QVariant(); + } + int row=index.row(); + if(row < 0 || row >= data_->size()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + VNodeListItem *item=data_->itemAt(row); + assert(item); + + switch(index.column()) + { + case 0: + return QString::fromStdString(item->server()); + break; + case 1: + return QString::fromStdString(item->path()); + break; + case 2: + return item->time(); + break; + default: + break; + } + } + + return QVariant(); +} + +QVariant ChangeNotifyModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QAbstractItemModel::headerData( section, orient, role ); + + if(role == Qt::DisplayRole) + { + switch ( section ) + { + case 0: return tr("Server"); + case 1: return tr("Node"); + case 2: return tr("Time of change"); + default: return QVariant(); + } + } + else if(role== Qt::ToolTipRole) + { + switch ( section ) + { + case 0: return tr("Server"); + case 1: return tr("Node"); + case 2: return tr("Time of change"); + default: return QVariant(); + } + } + return QVariant(); +} + +QModelIndex ChangeNotifyModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex ChangeNotifyModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + +VInfo_ptr ChangeNotifyModel::nodeInfo(const QModelIndex& index) const +{ + VInfo_ptr res; + + if(!index.isValid() || !hasData()) + { + return res; + } + + int row=index.row(); + if(row < 0 || row >= data_->size()) + return res; + + VNodeListItem *item=data_->itemAt(row); + Q_ASSERT(item); + VNode* vnode=item->node(); + Q_ASSERT(vnode); + + if(vnode->isServer()) + return VInfoServer::create(vnode->server()); + else + return VInfoNode::create(vnode); + + return res; +} + +void ChangeNotifyModel::slotBeginAppendRow() +{ + beginInsertRows(QModelIndex(),data_->size(),data_->size()); +} + +void ChangeNotifyModel::slotEndAppendRow() +{ + endInsertRows(); +} + +void ChangeNotifyModel::slotBeginRemoveRow(int row) +{ + beginRemoveRows(QModelIndex(),row,row); +} + +void ChangeNotifyModel::slotEndRemoveRow(int row) +{ + endRemoveRows(); +} + +void ChangeNotifyModel::slotBeginRemoveRows(int rowStart,int rowEnd) +{ + beginRemoveRows(QModelIndex(),rowStart,rowEnd); +} + +void ChangeNotifyModel::slotEndRemoveRows(int,int) +{ + endRemoveRows(); +} + + +void ChangeNotifyModel::slotBeginReset() +{ + beginResetModel(); +} + +void ChangeNotifyModel::slotEndReset() +{ + endResetModel(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,58 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef CHANGENOTIFYMODEL_HPP_ +#define CHANGENOTIFYMODEL_HPP_ + +#include + +#include + +#include "VInfo.hpp" + +class VNodeList; + +class ChangeNotifyModel : public QAbstractItemModel +{ + Q_OBJECT +public: + explicit ChangeNotifyModel(QObject *parent=0); + ~ChangeNotifyModel(); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + void setData(VNodeList *); + bool hasData() const; + VNodeList* data(); + VInfo_ptr nodeInfo(const QModelIndex&) const; + +public Q_SLOTS: + void slotBeginAppendRow(); + void slotEndAppendRow(); + void slotBeginRemoveRow(int); + void slotEndRemoveRow(int); + void slotBeginRemoveRows(int,int); + void slotEndRemoveRows(int,int); + void slotBeginReset(); + void slotEndReset(); + +protected: + VNodeList* data_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,252 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ChangeNotifyWidget.hpp" + +#include +#include +#include +#include +#include + +#include "ChangeNotify.hpp" +#include "VNodeList.hpp" +//#include "VProperty.hpp" + +#include + +std::vector ChangeNotifyWidget::widgets_; + +ChangeNotifyButton::ChangeNotifyButton(QWidget* parent) : + QToolButton(parent), + notifier_(0) +{ + setProperty("notify","1"); + setAutoRaise(true); + setIconSize(QSize(20,20)); + + grad_.setCoordinateMode(QGradient::ObjectBoundingMode); + grad_.setStart(0,0); + grad_.setFinalStop(0,1); +} + +void ChangeNotifyButton::setNotifier(ChangeNotify* notifier) +{ + notifier_=notifier; + + setToolTip(notifier_->toolTip()); + + connect(this,SIGNAL(clicked(bool)), + this,SLOT(slotClicked(bool))); + + connect(notifier_->data(),SIGNAL(endAppendRow()), + this,SLOT(slotAppend())); + + connect(notifier_->data(),SIGNAL(endRemoveRow(int)), + this,SLOT(slotRemoveRow(int))); + + connect(notifier_->data(),SIGNAL(endReset()), + this,SLOT(slotReset())); + + updateIcon(); +} + +void ChangeNotifyButton::slotAppend() +{ + updateIcon(); +} + +void ChangeNotifyButton::slotRemoveRow(int) +{ + updateIcon(); +} + +void ChangeNotifyButton::slotReset() +{ + updateIcon(); +} + +void ChangeNotifyButton::slotClicked(bool) +{ + ChangeNotify::showDialog(notifier_); +} + +void ChangeNotifyButton::updateIcon() +{ + QString text=notifier_->widgetText(); + QString numText; + + int num=0; + if(notifier_->data()) + { + num=notifier_->data()->size(); + if(num > 0 && num < 10) + numText=QString::number(num); + else if(num > 10) + numText="9+"; + + } + + QColor bgCol(198,198,199); + QColor fgCol(20,20,20); + QColor countBgCol(58,126,194); + QColor countFgCol(Qt::white); + + if(num > 0) + { + bgCol=notifier_->fillColour(); + fgCol=notifier_->textColour(); + countBgCol=notifier_->countFillColour(); + countFgCol=notifier_->countTextColour(); + } + + QFont f; + //f.setBold(true); + f.setPointSize(f.pointSize()); + QFontMetrics fm(f); + + QFont fNum; + fNum.setBold(true); + fNum.setPointSize(f.pointSize()-1); + QFontMetrics fmNum(fNum); + + int w; + if(!numText.isEmpty()) + w=fm.width(text) + 6 + fm.width(numText) + 2; + else + w=fm.width(text) + 6; + + int h=fm.height()+2; + + QPixmap pix(w,h); + pix.fill(QColor(255,255,255,0)); + QPainter painter(&pix); + painter.setRenderHint(QPainter::Antialiasing,true); + painter.setRenderHint(QPainter::TextAntialiasing,true); + + QRect textRect(0,0,fm.width(text)+6,h); + + QColor bgLight=bgCol.lighter(110); + grad_.setColorAt(0,bgLight); + grad_.setColorAt(1,bgCol); + + painter.setBrush(QBrush(grad_)); + //painter.setBrush(bgCol); + painter.setPen(bgCol.darker(170)); + painter.drawRoundedRect(textRect,2,2); + painter.setPen(fgCol); + painter.setFont(f); + painter.drawText(textRect,Qt::AlignHCenter|Qt::AlignVCenter,text); + + if(!numText.isEmpty()) + { + QRect numRect(textRect.right()-1,0,fmNum.width(numText)+4,fmNum.ascent()+2); + painter.setBrush(countBgCol); + painter.setPen(countFgCol); + painter.drawRoundedRect(numRect,4,4); + painter.setFont(fNum); + painter.drawText(numRect,Qt::AlignHCenter|Qt::AlignVCenter,numText); + } + + setIconSize(QSize(w,h)); + setIcon(pix); + +} + +ChangeNotifyWidget::ChangeNotifyWidget(QWidget *parent) : QWidget(parent) +{ + layout_=new QHBoxLayout(this); + layout_->setContentsMargins(0,0,0,0); + layout_->setSpacing(0); + + QLabel* label=new QLabel("Notifications: ",this); + label->setStyleSheet("QLabel{color: " + QColor(60,60,60).name() + ";}"); + //QFont f; + //f.setBold(true); + //f.setPointSize(f.pointSize()-1); + //label->setFont(f); + layout_->addWidget(label); + + ChangeNotify::populate(this); + + widgets_.push_back(this); +} + +ChangeNotifyWidget::~ChangeNotifyWidget() +{ + std::vector::iterator it=std::find(widgets_.begin(),widgets_.end(),this); + if(it != widgets_.end()) + widgets_.erase(it); +} + +ChangeNotifyButton* ChangeNotifyWidget::findButton(const std::string& id) +{ + std::map::const_iterator it=buttons_.find(id); + if(it != buttons_.end()) + return it->second; + + return 0; +} + +void ChangeNotifyWidget::addTb(ChangeNotify* notifier) +{ + ChangeNotifyButton *tb=new ChangeNotifyButton(this); + tb->setNotifier(notifier); + layout_->addWidget(tb); + if(!notifier->isEnabled()) + { + tb->setEnabled(false); + tb->hide(); + } + + buttons_[notifier->id()]=tb; + updateVisibility(); +} + +void ChangeNotifyWidget::setEnabled(const std::string& id,bool b) +{ + for(std::vector::iterator it=widgets_.begin(); it!= widgets_.end(); ++it) + { + if(ChangeNotifyButton* tb=(*it)->findButton(id)) + { + tb->setEnabled(b); + tb->setVisible(b); + (*it)->updateVisibility(); + } + + } +} + +void ChangeNotifyWidget::updateVisibility() +{ + setVisible(hasVisibleButton()); +} + +bool ChangeNotifyWidget::hasVisibleButton() const +{ + for(std::map::const_iterator it=buttons_.begin(); + it != buttons_.end(); ++it) + { + if(it->second->isEnabled()) + return true; + } + return false; +} + +void ChangeNotifyWidget::updateSettings(const std::string& id) +{ + for(std::vector::iterator it=widgets_.begin(); it!= widgets_.end(); ++it) + { + if(ChangeNotifyButton* tb=(*it)->findButton(id)) + { + tb->updateIcon(); + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ChangeNotifyWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ChangeNotifyWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,76 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ +#define VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ + +#include +#include +#include + +#include +#include +#include + +class QHBoxLayout; +class QLabel; +class QSignalMapper; + +class ChangeNotify; +class VProperty; +class ChangeNotifyWidget; + +class ChangeNotifyButton : public QToolButton +{ +Q_OBJECT + +friend class ChangeNotifyWidget; + +public: + explicit ChangeNotifyButton(QWidget* parent=0); + + void setNotifier(ChangeNotify*); + +public Q_SLOTS: + void slotAppend(); + void slotRemoveRow(int); + void slotReset(); + void slotClicked(bool); + +protected: + void updateIcon(); + + ChangeNotify* notifier_; + QLinearGradient grad_; +}; + +class ChangeNotifyWidget : public QWidget +{ +friend class ChangeNotify; + +public: + explicit ChangeNotifyWidget(QWidget *parent=0); + ~ChangeNotifyWidget(); + + void updateVisibility(); + static void setEnabled(const std::string& id,bool b); + static void updateSettings(const std::string& id); + +protected: + void addTb(ChangeNotify*); + ChangeNotifyButton* findButton(const std::string& id); + bool hasVisibleButton() const; + + QHBoxLayout* layout_; + std::map buttons_; + static std::vector widgets_; +}; + +#endif /* VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ClockWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ClockWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ClockWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ClockWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,119 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ClockWidget.hpp" +#include "PropertyMapper.hpp" +#include "VConfig.hpp" + +#include +#include + +#include +#include + +ClockWidget::ClockWidget(QWidget* parent) : QLabel(parent), showSec_(false) +{ + timer_=new QTimer(this); + connect(timer_,SIGNAL(timeout()), + this,SLOT(slotTimeOut())); + + QString sh="QLabel {color: rgb(34,107,138); margin-left: 5px; }"; + setStyleSheet(sh); + + std::vector propVec; + propVec.push_back("view.clock.showClock"); + propVec.push_back("view.clock.clockFormat"); + prop_=new PropertyMapper(propVec,this); + Q_ASSERT(prop_); + prop_->initObserver(this); + + adjustTimer(); +} + +ClockWidget::~ClockWidget() +{ + delete prop_; +} + +void ClockWidget::renderTime() +{ + if(isHidden()) + return; + + QTime t=QTime::currentTime(); + if(showSec_) + { + setText("" + t.toString(" HH:mm:ss ") + ""); + } + else + { + setText("" +t.toString(" HH:mm ") + ""); + } +} + +void ClockWidget::slotTimeOut() +{ + renderTime(); + if(!showSec_) + adjustTimer(); +} + +void ClockWidget::adjustTimer() +{ + if(isHidden()) + { + timer_->stop(); + } + else + { + if(showSec_) + { + timer_->start(1000); + } + else + { + int sec=QTime::currentTime().second(); + int interval=(sec <=1)?(60):(60-sec+1); + interval*=1000; + if(timer_->interval() != interval || !timer_->isActive()) + { + timer_->start(interval); + } + } + } +} + +void ClockWidget::notifyChange(VProperty* p) +{ + if(p->path() == "view.clock.showClock") + { + bool v=p->value().toBool(); + if(v!= isVisible()) + { + setVisible(v); + renderTime(); + adjustTimer(); + } + } + else if(p->path() == "view.clock.clockFormat") + { + QString v=p->valueAsString(); + bool showSec=false; + if(v=="hhmmss") + showSec=true; + + if(showSec != showSec_) + { + showSec_=showSec; + renderTime(); + adjustTimer(); + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ClockWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ClockWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ClockWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ClockWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef CLOCKWIDGET_HPP +#define CLOCKWIDGET_HPP + +#include + +#include "VProperty.hpp" + +class QTimer; +class PropertyMapper; + +class ClockWidget : public QLabel, public VPropertyObserver +{ + Q_OBJECT +public: + ClockWidget(QWidget* parent=0); + ~ClockWidget(); + + void notifyChange(VProperty*); + +protected Q_SLOTS: + void slotTimeOut(); + +protected: + void renderTime(); + void adjustTimer(); + + PropertyMapper* prop_; + QTimer* timer_; + bool showSec_; +}; + +#endif // CLOCKWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CMakeLists.txt ecflow-4.11.1/Viewer/ecflowUI/src/CMakeLists.txt --- ecflow-4.9.0/Viewer/ecflowUI/src/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CMakeLists.txt 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,530 @@ + +set(viewer_srcs + ViewerMain.cpp + AboutDialog.cpp + AbstractNodeModel.cpp + AbstractNodeView.cpp + AbstractSearchLine.cpp + AbstractTextEditSearchInterface.cpp + ActionHandler.cpp + AddModelColumnDialog.cpp + Animation.cpp + AstCollateVNodesVisitor.cpp + AttributeEditor.cpp + AttributeEditorFactory.cpp + CaseSensitiveButton.cpp + ChangeNotify.cpp + ChangeNotifyDialog.cpp + ChangeNotifyEditor.cpp + ChangeNotifyModel.cpp + ChangeNotifyWidget.cpp + ClockWidget.cpp + CodeItemWidget.cpp + ComboMulti.cpp + CommandDesignerWidget.cpp + CommandHandler.cpp + CommandOutput.cpp + CommandOutputDialog.cpp + CommandOutputWidget.cpp + CompactView.cpp + ConfigListDelegate.cpp + ConnectState.cpp + CustomCommandDialog.cpp + CustomCommandHandler.cpp + CustomListWidget.cpp + CustomTabWidget.cpp + Dashboard.cpp + DashboardDialog.cpp + DashboardDock.cpp + DashboardTitle.cpp + DashboardWidget.cpp + DiagData.cpp + EditItemWidget.cpp + EditProvider.cpp + EditorInfoLabel.cpp + ExpandState.cpp + ExpandStateNode.cpp + FlagSet.hpp + FileInfoLabel.cpp + FileWatcher.cpp + FilterWidget.cpp + FontMetrics.cpp + GotoLineDialog.cpp + Highlighter.cpp + HistoryItemWidget.cpp + HtmlEdit.cpp + HtmlItemWidget.cpp + InfoPanel.cpp + InfoPanelItem.cpp + InfoPanelHandler.cpp + InfoProvider.cpp + InputEventLog.cpp + JobItemWidget.cpp + LabelEditor.cpp + LimitEditor.cpp + LineEdit.cpp + LogProvider.cpp + LogViewerCom.cpp + MainWindow.cpp + ManualItemWidget.cpp + MenuConfigDialog.cpp + MenuHandler.cpp + MessageItemWidget.cpp + MeterEditor.cpp + ModelColumn.cpp + NodeExpression.cpp + NodeFilterDialog.cpp + NodePanel.cpp + NodePathWidget.cpp + NodeQuery.cpp + NodeQueryCombo.cpp + NodeQueryEditor.cpp + NodeQueryEngine.cpp + NodeQueryHandler.cpp + NodeQueryOption.cpp + NodeQueryOptionEdit.cpp + NodeQueryResult.cpp + NodeQueryResultModel.cpp + NodeQueryResultView.cpp + NodeQueryViewDelegate.cpp + NodeSearchDialog.cpp + NodeSearchWidget.cpp + NodeViewBase.cpp + NodeViewDelegate.cpp + NodeWidget.cpp + OneLineTextEdit.cpp + OutputBrowser.cpp + OutputCache.cpp + OutputClient.cpp + OutputFetchInfo.cpp + OutputFileClient.cpp + OutputDirClient.cpp + OutputModel.cpp + OutputItemWidget.cpp + OutputFileProvider.cpp + OutputDirProvider.cpp + OverviewItemWidget.cpp + OverviewProvider.cpp + PlainTextEdit.cpp + PlainTextSearchLine.cpp + PlainTextSearchInterface.cpp + PropertyDialog.cpp + PropertyEditor.cpp + PropertyLine.cpp + PropertyMapper.cpp + RectMetrics.cpp + RepeatEditor.cpp + RichTextEdit.cpp + RichTextSearchInterface.cpp + RichTextSearchLine.cpp + SaveSessionAsDialog.cpp + ScriptItemWidget.cpp + ServerComInfoWidget.cpp + ServerComQueue.cpp + ServerComThread.cpp + ServerDefsAccess.cpp + ServerHandler.cpp + ServerFilter.cpp + ServerItem.cpp + ServerList.cpp + ServerListDialog.cpp + ServerListSyncWidget.cpp + ServerLoadItemWidget.cpp + ServerSettingsItemWidget.cpp + SessionDialog.cpp + SessionRenameDialog.cpp + SessionHandler.cpp + ShellCommand.cpp + Sound.cpp + StandardView.cpp + StringMatchCombo.cpp + StringMatchMode.cpp + SuiteItemWidget.cpp + SuiteFilter.cpp + SuiteFilterObserver.hpp + SuiteModel.cpp + TabWidget.cpp + TableFilterWidget.cpp + TableNodeModel.cpp + TableNodeSortModel.cpp + TableNodeView.cpp + TableNodeViewDelegate.cpp + TableNodeWidget.cpp + TextEditSearchLine.cpp + TextFilterHandler.cpp + TextFilterHandlerDialog.cpp + TextFilterWidget.cpp + TimeItemWidget.cpp + TreeNodeModel.cpp + TreeNodeView.cpp + TreeNodeViewDelegate.cpp + TreeNodeWidget.cpp + TreeView.cpp + TriggerEditor.cpp + TriggerItemWidget.cpp + TriggerTextWidget.cpp + TriggerCollector.cpp + TriggerTableModel.cpp + TriggerTableView.cpp + TriggerTableWidget.cpp + TriggerViewDelegate.cpp + TriggeredScanner.cpp + UiLogS.cpp + UpdateTimer.cpp + VAttribute.cpp + VAttributeType.cpp + VConfig.cpp + VConfigLoader.cpp + VDateAttr.cpp + VDir.cpp + VEventAttr.cpp + VFilter.cpp + VIcon.cpp + VInfo.cpp + VFile.cpp + VFileInfo.cpp + VItem.cpp + VItemPathParser.cpp + VLabelAttr.cpp + VLateAttr.cpp + VLimitAttr.cpp + VLimiterAttr.cpp + VMeterAttr.cpp + VModelData.cpp + VNode.cpp + VNodeList.cpp + VNodeMover.cpp + VNodeStateDiag.cpp + VNState.cpp + VParam.cpp + VProperty.cpp + VRepeatAttr.cpp + VReply.cpp + VReportMaker.cpp + VServerSettings.cpp + VSettings.cpp + VSettingsLoader.cpp + VSState.cpp + VTask.cpp + VTaskNode.cpp + VTimeAttr.cpp + VTree.cpp + VTriggerAttr.cpp + VGenVarAttr.cpp + VUserVarAttr.cpp + VariableEditor.cpp + VariableModel.cpp + VariableModelData.cpp + VariableItemWidget.cpp + VariableSearchLine.cpp + VariableView.cpp + ViewerUtil.cpp + WhyItemWidget.cpp + WidgetNameProvider.cpp + WmWorkspaceHandler.cpp + ZombieItemWidget.cpp + ZombieModel.cpp + TextPager/TextPagerCursor.cpp + TextPager/TextPagerDocument.cpp + TextPager/TextPagerEdit.cpp + TextPager/TextPagerLayout_p.cpp + TextPager/TextPagerSearchHighlighter.cpp + TextPager/TextPagerSearchInterface.cpp + TextPager/TextPagerSection.cpp + TextPager/TextPagerWidget.cpp + TextPager/syntaxhighlighter.cpp) + +if(ECFLOW_QT5) + include_directories(${ECFLOW_QT_INCLUDE_DIR}) +else() + include(${QT_USE_FILE}) + set(ECFLOW_QT_LIBRARIES ${QT_LIBRARIES}) +endif() + + +set(viewer_moc_files AbstractNodeModel.hpp + AbstractNodeView.hpp + AbstractSearchLine.hpp + ActionHandler.hpp + AddModelColumnDialog.hpp + Animation.hpp + AttributeEditor.hpp + CaseSensitiveButton.hpp + ChangeNotifyDialog.hpp + ChangeNotifyEditor.hpp + ChangeNotifyModel.hpp + ChangeNotifyWidget.hpp + ClockWidget.hpp + CodeItemWidget.hpp + ComboMulti.hpp + CommandDesignerWidget.hpp + CommandOutput.hpp + CommandOutputDialog.hpp + CommandOutputWidget.hpp + CustomCommandDialog.hpp + CustomListWidget.hpp + Dashboard.hpp + DashboardDialog.hpp + DashboardDock.hpp + DashboardTitle.hpp + DashboardWidget.hpp + EditItemWidget.hpp + FileWatcher.hpp + FilterWidget.hpp + GotoLineDialog.hpp + HistoryItemWidget.hpp + HtmlItemWidget.hpp + InfoPanel.hpp + InputEventLog.hpp + LabelEditor.hpp + LimitEditor.hpp + LineEdit.hpp + LogProvider.hpp + MainWindow.hpp + MenuConfigDialog.hpp + MessageItemWidget.hpp + MeterEditor.hpp + ModelColumn.hpp + NodeFilterDialog.hpp + NodePanel.hpp + NodePathWidget.hpp + NodeQueryCombo.hpp + NodeQueryEditor.hpp + NodeQueryOptionEdit.hpp + NodeQueryEngine.hpp + NodeQueryResult.hpp + NodeQueryResultModel.hpp + NodeQueryResultView.hpp + NodeSearchDialog.hpp + NodeSearchWidget.hpp + NodeWidget.hpp + OneLineTextEdit.hpp + OutputBrowser.hpp + OutputCache.hpp + OutputClient.hpp + OutputFileClient.hpp + OutputDirClient.hpp + OutputItemWidget.hpp + OutputFileProvider.hpp + OutputDirProvider.hpp + PlainTextEdit.hpp + PropertyDialog.hpp + PropertyEditor.hpp + PropertyLine.hpp + SaveSessionAsDialog.hpp + RepeatEditor.hpp + RichTextEdit.hpp + ServerComInfoWidget.hpp + ServerComQueue.hpp + ServerComThread.hpp + ServerHandler.hpp + ServerListDialog.hpp + ServerListSyncWidget.hpp + ServerLoadItemWidget.hpp + ServerSettingsItemWidget.hpp + SessionDialog.hpp + SessionRenameDialog.hpp + ShellCommand.hpp + StringMatchCombo.hpp + SuiteItemWidget.hpp + SuiteModel.hpp + TabWidget.hpp + TableFilterWidget.hpp + TableNodeModel.hpp + TableNodeView.hpp + TableNodeViewDelegate.hpp + TableNodeWidget.hpp + TextEditSearchLine.hpp + TextFilterHandlerDialog.hpp + TextFilterWidget.hpp + TreeNodeModel.hpp + TreeNodeView.hpp + TreeNodeViewDelegate.hpp + TreeNodeWidget.hpp + TriggerEditor.hpp + TriggerItemWidget.hpp + TriggerTableView.hpp + TriggerTableWidget.hpp + TriggeredScanner.hpp + VariableEditor.hpp + VariableItemWidget.hpp + VariableModel.hpp + VariableModelData.hpp + VariableSearchLine.hpp + VFilter.hpp + VModelData.hpp + VNodeList.hpp + WhyItemWidget.hpp + ZombieItemWidget.hpp + TextPager/TextPagerCursor_p.hpp + TextPager/TextPagerDocument.hpp + TextPager/TextPagerDocument_p.hpp + TextPager/TextPagerEdit.hpp + TextPager/TextPagerEdit_p.hpp + TextPager/TextPagerSection_p.hpp + TextPager/TextPagerWidget.hpp + TextPager/syntaxhighlighter.hpp +) + +set(viewer_wrap_ui_files + AboutDialog.ui + AddModelColumnDialog.ui + AttributeEditorDialog.ui + ChangeNotifyDialog.ui + ChangeNotifyDialogWidget.ui + ChangeNotifyEditor.ui + CommandDesignerWidget.ui + CommandOutputDialog.ui + CommandOutputWidget.ui + CustomCommandDialog.ui + CodeItemWidget.ui + DashboardDialog.ui + DashboardDockTitleWidget.ui + EditItemWidget.ui + GotoLineDialog.ui + HistoryItemWidget.ui + HtmlItemWidget.ui + InfoPanel.ui + LabelEditorWidget.ui + LimitEditorWidget.ui + #LogLoadWidget.ui + MainWindow.ui + MessageItemWidget.ui + MenuConfigDialog.ui + MeterEditorWidget.ui + NodeFilterDialog.ui + NodeQueryEditor.ui + NodeQuerySaveDialog.ui + NodeSearchDialog.ui + NodeSearchWidget.ui + OutputItemWidget.ui + PropertyDialog.ui + PropertyEditor.ui + RepeatEditorWidget.ui + SaveSessionAsDialog.ui + SearchLineWidget.ui + ServerAddDialog.ui + ServerEditDialog.ui + ServerListDialog.ui + ServerListSyncWidget.ui + ServerLoadItemWidget.ui + ServerSettingsItemWidget.ui + SessionDialog.ui + SessionRenameDialog.ui + SuiteItemWidget.ui + TableFilterWidget.ui + TableNodeWidget.ui + TextFilterAddDialog.ui + TextFilterHandlerDialog.ui + TextFilterWidget.ui + TimeItemWidget.ui + TreeNodeWidget.ui + TriggerEditorWidget.ui + TriggerItemWidget.ui + TriggerTableWidget.ui + VariableAddDialog.ui + VariableEditorWidget.ui + VariablePropDialog.ui + VariableItemWidget.ui + ZombieItemWidget.ui +) + +if(ECFLOW_QT5) + QT5_WRAP_CPP(VIEWER_MOC ${viewer_moc_files} ) + QT5_ADD_RESOURCES (VIEWER_RES viewer.qrc) + QT5_WRAP_UI (VIEWER_FORMS_HEADERS ${viewer_wrap_ui_files}) +else() + QT4_WRAP_CPP(VIEWER_MOC ${viewer_moc_files} ) + QT4_ADD_RESOURCES (VIEWER_RES viewer.qrc) + QT4_WRAP_UI (VIEWER_FORMS_HEADERS ${viewer_wrap_ui_files}) +endif() + + +# add all the images as dependencies of the resource file so that it is +# automatically recompiled when an image changes +file( GLOB image_files "${CMAKE_CURRENT_SOURCE_DIR}/../images/*.*" ) +ADD_CUSTOM_TARGET(Qt_resource_cpp DEPENDS ${VIEWER_RES}) +ADD_DEPENDENCIES(Qt_resource_cpp ${image_files}) + + + +add_definitions( -DECFLOW_SHARED_DIR="${CMAKE_INSTALL_PREFIX}/share/ecflow" ) + + +ecbuild_add_executable( TARGET ecflow_ui.x + SOURCES ${viewer_srcs} ${VIEWER_MOC} ${VIEWER_RES} ${VIEWER_FORMS_HEADERS} + INCLUDES . + ../../libViewer/src + ../../../ACore/src + ../../../ANattr/src + ../../../ANode/src + ../../../ANode/test + ../../../Base/src + ../../../Base/src/cts + ../../../Base/src/stc + ../../../Client/src + TextPager + ${Boost_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + LIBS viewer core nodeattr node base libclient + pthread + m dl + ${ECFLOW_QT_LIBRARIES} + ${OPENSSL_LIBRARIES} +) + +# This ensures that for debug config, we only link with debug boost libs, for other configs, we link with optimised boost libs +target_link_libraries(ecflow_ui.x debug ${Boost_REGEX_LIBRARY_DEBUG} ${Boost_REGEX_LIBRARY_RELEASE} ) + + +if(0) +set(logviewer_srcs + AboutDialog.cpp + ClockWidget.cpp + DirectoryHandler.cpp + FlagSet.hpp + IconProvider.cpp + InfoPanel.cpp + InfoPanelItem.cpp + InfoPanelHandler.cpp + InfoProvider.cpp + LogData.cpp + LogLoadWidget.cpp + LogModel.cpp + LogTruncator.cpp + LogView.cpp + LogViewerMain.cpp + ServerLoadItemWidget.cpp + UIDebug.cpp + UiLog.cpp + UserMessage.cpp + VConfig.cpp + VConfigLoader.cpp + ) +endif() + +set(build_ecflow_ui_log 0) + +if(${build_ecflow_ui_log}) + list(REMOVE_ITEM viewer_srcs ViewerMain.cpp) + ecbuild_add_executable( TARGET ecflow_ui_log + SOURCES LogEvent.cpp + ${viewer_srcs} ${VIEWER_MOC} ${VIEWER_RES} ${VIEWER_FORMS_HEADERS} + INCLUDES . + ../../ACore/src + ../../ANattr/src + ../../ANode/src + ../../ANode/test + ../../Base/src + ../../Base/src/cts + ../../Base/src/stc + ../../Client/src + ${Boost_INCLUDE_DIRS} + ${CMAKE_CURRENT_BINARY_DIR} + LIBS core nodeattr node base libclient + pthread + m + ${ECFLOW_QT_LIBRARIES} + DEFINITIONS MAIN_LOG + ) +endif() diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,119 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CodeItemWidget.hpp" + +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#else +#include +#endif + +#include + +#include +#include + +CodeItemWidget::CodeItemWidget(QWidget *parent) : + QWidget(parent) +{ + setupUi(this); + + externalTb_->hide(); + + fileLabel_->setProperty("fileInfo","1"); + + searchLine_->setEditor(textEdit_); + searchLine_->setVisible(false); + + copyPathTb_->setEnabled(false); +} + +CodeItemWidget::~CodeItemWidget() +{ +} + +void CodeItemWidget::removeSpacer() +{ + //Remove the first spcer item!! + for(int i=0; horizontalLayout->count(); i++) + { + if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) + { + horizontalLayout->takeAt(i); + delete sp; + break; + } + } +} + +void CodeItemWidget::on_searchTb__clicked() +{ + searchLine_->setVisible(true); + searchLine_->setFocus(); + searchLine_->selectAll(); +} + +void CodeItemWidget::on_gotoLineTb__clicked() +{ + textEdit_->gotoLine(); +} + +void CodeItemWidget::on_fontSizeUpTb__clicked() +{ + //We need to call a custom slot here instead of "zoomIn"!!! + textEdit_->slotZoomIn(); +} + +void CodeItemWidget::on_fontSizeDownTb__clicked() +{ + //We need to call a custom slot here instead of "zoomOut"!!! + textEdit_->slotZoomOut(); +} + +void CodeItemWidget::on_reloadTb__clicked() +{ + reloadRequested(); +} + +//----------------------------------------- +// Copy file path +//----------------------------------------- + +void CodeItemWidget::on_copyPathTb__clicked() +{ + if(!currentFileName_.empty()) + { + QString txt=QString::fromStdString(currentFileName_); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif + } +} + +void CodeItemWidget::setCurrentFileName(const std::string& fname) +{ + currentFileName_=fname; + copyPathTb_->setEnabled(!currentFileName_.empty()); +} + +void CodeItemWidget::clearCurrentFileName() +{ + currentFileName_.clear(); + copyPathTb_->setEnabled(false); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef CODEITEMWIDGET_HPP_ +#define CODEITEMWIDGET_HPP_ + +#include + +#include "ui_CodeItemWidget.h" + +class CodeItemWidget : public QWidget, protected Ui::CodeItemWidget +{ +Q_OBJECT + +public: + explicit CodeItemWidget(QWidget *parent=0); + ~CodeItemWidget(); + +protected Q_SLOTS: + void on_searchTb__clicked(); + void on_gotoLineTb__clicked(); + void on_fontSizeUpTb__clicked(); + void on_fontSizeDownTb__clicked(); + void on_reloadTb__clicked(); + void on_copyPathTb__clicked(); + +Q_SIGNALS: + void editorFontSizeChanged(); + +protected: + void removeSpacer(); + virtual void reloadRequested()=0; + void setCurrentFileName(const std::string&); + void clearCurrentFileName(); + +private: + std::string currentFileName_; + + +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/CodeItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CodeItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,247 @@ + + + CodeItemWidget + + + + 0 + 0 + 510 + 465 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + false + + + QFrame::StyledPanel + + + + + + 2 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy file path + + + Copy file path + + + + :/viewer/copy_path.svg:/viewer/copy_path.svg + + + true + + + + + + + Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg + + + Ctrl++ + + + true + + + + + + + Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg + + + Ctrl+- + + + true + + + + + + + ... + + + + + + + Show search bar (CTRL-F) + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + Goto line number (CTRL-L) + + + ... + + + + :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg + + + Ctrl+L + + + true + + + + + + + Reload + + + ... + + + + :/viewer/sync_black.svg:/viewer/sync_black.svg + + + true + + + + + + + + + + + + + 0 + 1 + + + + QPlainTextEdit::NoWrap + + + true + + + + + + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + FileInfoLabel + QLabel +
      FileInfoLabel.hpp
      +
      + + PlainTextSearchLine + QWidget +
      PlainTextSearchLine.hpp
      + 1 +
      + + PlainTextEdit + QPlainTextEdit +
      PlainTextEdit.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ComboMulti.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ComboMulti.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ComboMulti.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ComboMulti.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,267 @@ +#include "ComboMulti.hpp" + +#include +#include +#include +#include +#include +#include +#include + +//========================================================== +// +// ComboMulti +// +//========================================================== + +ComboMulti::ComboMulti(QWidget *widget) : + QComboBox(widget), + mode_(BasicMode), + dpyText_("") +{ + setSizeAdjustPolicy(QComboBox::AdjustToContents); + + ComboMultiDelegate* del=new ComboMultiDelegate(this); + + connect(del,SIGNAL(itemChecked()), + this,SLOT(slotChecked())); + + // set delegate items view + view()->setItemDelegate(del); + //view()->setStyleSheet(" padding: 15px; "); + // Enable editing on items view + view()->setEditTriggers(QAbstractItemView::CurrentChanged); + + // set "CheckBoxList::eventFilter" as event filter for items view + view()->viewport()->installEventFilter(this); + + // it just cool to have it as defualt ;) + view()->setAlternatingRowColors(true); +} + + +ComboMulti::~ComboMulti() +{ + +} + +bool ComboMulti::eventFilter(QObject *object, QEvent *event) +{ + // don't close items view after we release the mouse button + // by simple eating MouseButtonRelease in viewport of items view + if(event->type() == QEvent::MouseButtonRelease && object==view()->viewport()) + { + return true; + } + return QComboBox::eventFilter(object,event); +} + +void ComboMulti::paintEvent(QPaintEvent *) +{ + QStylePainter painter(this); + painter.setPen(palette().color(QPalette::Text)); + + // draw the combobox frame, focusrect and selected etc. + QStyleOptionComboBox opt; + initStyleOption(&opt); + + // if no display text been set , use "..." as default + if(dpyText_.isEmpty()) + if(mode_ == FilterMode) + opt.currentText = "ALL"; + else + opt.currentText="NONE"; + else + { + opt.currentText = dpyText_; + } + painter.drawComplexControl(QStyle::CC_ComboBox, opt); + + // draw the icon and text + painter.drawControl(QStyle::CE_ComboBoxLabel, opt); + +} + +void ComboMulti::slotChecked() +{ + QString s; + selection_.clear(); + + for(int i=0; i < model()->rowCount(); i++) + { + if(model()->data(model()->index(i,0),Qt::CheckStateRole).toBool()) + { + selection_ << model()->data(model()->index(i,0),Qt::DisplayRole).toString(); + } + } + + if(selection_.count() == 0) + s=""; + else + s=selection_.join(", "); + + setDisplayText(s); + + update(); + + Q_EMIT selectionChanged(); +} + +void ComboMulti::setSelection(QStringList lst) +{ + for(int i=0; i < count(); i++) + { + setItemData(i,false,Qt::CheckStateRole); + if(lst.contains(itemText(i))) + setItemData(i,true,Qt::CheckStateRole); + } + + slotChecked(); +} + +void ComboMulti::setSelectionByData(QStringList lst) +{ + for(int i=0; i < count(); i++) + { + setItemData(i,false,Qt::CheckStateRole); + if(lst.contains(itemData(i).toString())) + setItemData(i,true,Qt::CheckStateRole); + } + + slotChecked(); +} + +void ComboMulti::clearSelection() +{ + for(int i=0; i < count(); i++) + { + setItemData(i,false,Qt::CheckStateRole); + } + + slotChecked(); +} + +void ComboMulti::selectSoleItem() +{ + if(count() == 1) + { + setItemData(0,true,Qt::CheckStateRole); + slotChecked(); + } +} + +QStringList ComboMulti::selectionData() const +{ + QStringList lst; + for(int i=0; i < count(); i++) + { + if(itemData(i,Qt::CheckStateRole).toBool()) + lst << itemData(i,Qt::UserRole).toString(); + } + return lst; +} + +void ComboMulti::setDisplayText(QString text) +{ + dpyText_ = text; +} + +QString ComboMulti::displayText() const +{ + return dpyText_; +} + +QStringList ComboMulti::all() const +{ + QStringList lst; + for(int i=0; i < count(); i++) + lst << itemText(i); + + return lst; +} + +void ComboMulti::setMode(Mode mode) +{ + mode_=mode; +} + + +//========================================================== +// +// ComboMultiDelegate +// +//========================================================== + +ComboMultiDelegate::ComboMultiDelegate(QObject *parent) + : QItemDelegate(parent) +{ +} + +void ComboMultiDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const +{ + //Get item data + bool value = index.data(Qt::CheckStateRole).toBool(); + QString text = index.data(Qt::DisplayRole).toString(); + + // fill style options with item data + const QStyle *style = QApplication::style(); + QStyleOptionButton opt; + opt.state |= value ? QStyle::State_On : QStyle::State_Off; + opt.state |= QStyle::State_Enabled; + opt.text = text; + opt.rect = option.rect; + + style->drawControl(QStyle::CE_CheckBox,&opt,painter); +} + +QWidget* ComboMultiDelegate::createEditor(QWidget *parent, + const QStyleOptionViewItem & option , + const QModelIndex & index ) const +{ + QCheckBox *editor = new QCheckBox(parent); + return editor; +} + +void ComboMultiDelegate::setEditorData(QWidget *editor, + const QModelIndex &index) const +{ + //set editor data + QCheckBox *myEditor = static_cast(editor); + myEditor->setText(index.data(Qt::DisplayRole).toString()); + myEditor->setChecked(index.data(Qt::CheckStateRole).toBool()); + + connect(myEditor,SIGNAL(stateChanged(int)), + this,SLOT(slotEdited(int))); + +} + +void ComboMultiDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, + const QModelIndex &index) const +{ + //get the value from the editor (CheckBox) + QCheckBox *myEditor = static_cast(editor); + bool value = myEditor->isChecked(); + + if(model->data(index,Qt::CheckStateRole).toBool() != value) + { + model->setData(index,value,Qt::CheckStateRole); + Q_EMIT itemChecked(); + } + +} + +void ComboMultiDelegate::updateEditorGeometry(QWidget *editor, + const QStyleOptionViewItem &option, const QModelIndex &index ) const +{ + editor->setGeometry(option.rect); +} + +void ComboMultiDelegate::slotEdited(int) +{ + QCheckBox* cb = static_cast(sender()); + if(cb) + { + Q_EMIT commitData(cb); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ComboMulti.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ComboMulti.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ComboMulti.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ComboMulti.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,71 @@ +#ifndef COMBOMULTI_HPP_ +#define COMBOMULTI_HPP_ + +#include +#include + +class ComboMulti: public QComboBox +{ +Q_OBJECT; + +public: + enum Mode {BasicMode,FilterMode}; + + enum CustomItemRole {SelectRole = Qt::UserRole+1}; + + explicit ComboMulti(QWidget *widget = 0); + virtual ~ComboMulti(); + bool eventFilter(QObject *object, QEvent *event); + virtual void paintEvent(QPaintEvent *); + void setDisplayText(QString text); + QString displayText() const; + bool hasSelection() const {return !selection_.isEmpty();} + QStringList selection() const {return selection_;} + QStringList all() const; + QStringList selectionData() const; + void selectSoleItem(); + void setSelection(QStringList); + void setSelectionByData(QStringList); + void setMode(Mode); + +public Q_SLOTS: + void slotChecked(); + void clearSelection(); + +Q_SIGNALS: + void selectionChanged(); + +private: + Mode mode_; + bool elide_; + QString dpyText_; + QStringList selection_; +}; + +class ComboMultiDelegate : public QItemDelegate +{ +Q_OBJECT + +public: + explicit ComboMultiDelegate(QObject *parent); + + void paint(QPainter *painter, const QStyleOptionViewItem &option, + const QModelIndex &index) const; + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & option, + const QModelIndex & index ) const; + + void setEditorData(QWidget *editor,const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const; + void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index ) const; + +protected Q_SLOTS: + void slotEdited(int); + +Q_SIGNALS: + void itemChecked() const; +}; + + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,673 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include + +#include + +#include "CommandDesignerWidget.hpp" +#include "CustomCommandHandler.hpp" +#include "Child.hpp" +#include "Str.hpp" +#include "NodeQueryResult.hpp" +#include "NodeExpression.hpp" + +using namespace boost; +namespace po = boost::program_options; + + +CommandDesignerWidget::CommandDesignerWidget(QWidget *parent) : QWidget(parent), menuItem_("") +{ + setupUi(this); + + + //at least for now, all commands will start with 'ecflow_client' and end with '' + commandLineEdit_->setText("ecflow_client "); + //commandLineEdit_->installEventFilter(this); + commandLineEdit_->setFocus(); + + haveSetUpDefaultCommandLine_ = false; + inCommandEditMode_ = false; + saveCommandsOnExit_ = false; + + addToContextMenuCb_->setChecked(true); // by default, suggest saving to context menu + + // ensure the Save button is in the right state + on_commandLineEdit__textChanged(); + + saveNameLineEdit_->setPlaceholderText(tr("Unnamed")); + + currentCommandSaved_ = false; + refreshSavedCommandList(); + + + setSaveOptionsState(false, true); + + + // ensure we start on the command-builder tab + changeToTab(TAB_BUILD); + on_tabWidget__currentChanged(TAB_BUILD); // trigger the callback to ensure the correct visibility of the save buttons + + // set up and populate our list of options to the ecflow client + // - this would be more efficient if we did it only once, in a singleton, but + // it seems pretty fast so we'll leave it like this for now + initialiseComponentListDetails(); + clientOptionsDescriptions_ = new po::options_description("help" , po::options_description::m_default_line_length + 80); + cmdRegistry_.addAllOptions(*clientOptionsDescriptions_); + addClientCommandsToComponentList(); + + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + commandLineEdit_->setClearButtonEnabled(true); +#endif + + + infoLabel_->setShowTypeTitle(false); + infoLabel_->showInfo(tr("Click command for help, double-click to insert")); + + nodeSelectionView_->enableContextMenu(false); + + + nodeListLinkLabel_->setOpenExternalLinks(false); + + + connect(nodeSelectionView_, SIGNAL(selectionChanged()), this, SLOT(on_nodeSelectionChanged())); + + + setSavedCommandsButtonStatus(); + + + // temporary + //saveCommandGroupBox_->setVisible(false); + //tabWidget_->setTabEnabled(2, false); + //savedCommandsGroupBox_->setVisible(false); + +} + +CommandDesignerWidget::~CommandDesignerWidget() +{ + delete clientOptionsDescriptions_; + + if (saveCommandsOnExit_) + CustomSavedCommandHandler::instance()->writeSettings(); + + MenuHandler::refreshCustomMenuCommands(); +} + + +void CommandDesignerWidget::initialiseComponentListDetails() +{ + + // we don't want all the available commands to appear in the component list because they + // are not all relevant for one reason or another + + componentBlacklist_.clear(); + + // The following rely on standard out: + componentBlacklist_.push_back("help"); + componentBlacklist_.push_back("get"); + componentBlacklist_.push_back("get_state"); + componentBlacklist_.push_back("ch_suites"); + componentBlacklist_.push_back("migrate"); + componentBlacklist_.push_back("ping"); + componentBlacklist_.push_back("server_version"); + componentBlacklist_.push_back("stats"); + componentBlacklist_.push_back("status"); + componentBlacklist_.push_back("suites"); + componentBlacklist_.push_back("version"); + componentBlacklist_.push_back("zombie_get"); + + // The following only make sense for a persistent client. (like GUI, python): + componentBlacklist_.push_back("news"); + componentBlacklist_.push_back("sync"); + componentBlacklist_.push_back("sync_full"); + + // The following require a prompt: + componentBlacklist_.push_back("delete"); + componentBlacklist_.push_back("terminate"); + componentBlacklist_.push_back("halt"); + + // The following only make sense in a group: + componentBlacklist_.push_back("show"); + componentBlacklist_.push_back("why"); +} + + +void CommandDesignerWidget::initialiseCommandLine() +{ + if (!haveSetUpDefaultCommandLine_) + { + // put the cursor between the two pre-defined strings - this is where the command will go + commandLineEdit_->home(false); + commandLineEdit_->setCursorPosition(14); + commandLineEdit_->deselect(); + + haveSetUpDefaultCommandLine_ = true; + } +} + + +void CommandDesignerWidget::changeToTab(TabIndexes i) +{ + tabWidget_->setCurrentIndex(i); +} + + +void CommandDesignerWidget::setNodes(std::vector &nodes) +{ + nodes_ = nodes; + + + // populate the list of nodes + nodeSelectionView_->setSourceModel(&nodeModel_); + nodeModel_.slotBeginReset(); + nodeModel_.data()->add(nodes); + nodeModel_.slotEndReset(); + + // all should be selected at first + nodeSelectionView_->selectAll(); + + + on_nodeSelectionChanged(); // get the number of selected nodes and act accordingly +} + + + +// when the user clicks on the hyperlinked label which tells them how many nodes +// will be acted on, we want to switch to the Nodes tab +void CommandDesignerWidget::on_nodeListLinkLabel__linkActivated(const QString &link) +{ + if (link == "#nodes") + { + changeToTab(TAB_NODES); + } +} + + +void CommandDesignerWidget::setNodeNumberLinkText(int numNodes) +{ + QString s; + + s = (numNodes == 1) ? "" : "s"; + + nodeListLinkLabel_->setText(tr("Command will be run on %1 node%2: click here for list").arg(numNodes).arg(s)); +} + +// triggered when the user changes their node selection +void CommandDesignerWidget::on_nodeSelectionChanged() +{ + setNodeNumberLinkText(selectedNodes().size()); + on_commandLineEdit__textChanged(); // trigger the enabling/disabling of the Run button +} + + +std::vector& CommandDesignerWidget::selectedNodes() +{ + nodeSelectionView_->getListOfSelectedNodes(nodes_); + return nodes_; +} + +void CommandDesignerWidget::addClientCommandsToComponentList() +{ + // sort the commands into alphabetical order + std::vector< boost::shared_ptr > options = clientOptionsDescriptions_->options(); + + std::sort(options.begin(),options.end(), + boost::bind(std::less(), + boost::bind(&po::option_description::long_name,_1), + boost::bind(&po::option_description::long_name,_2))); + + // loop through the sorted commands and add them to the list + size_t numOptions = options.size(); + + for(size_t i = 0; i < numOptions; i++) + { + std::string longName(options[i]->long_name()); + if (!ecf::Child::valid_child_cmd(longName)) // do not show the 'child' options + { + if (std::find(componentBlacklist_.begin(), componentBlacklist_.end(), longName) == componentBlacklist_.end()) // not in blacklist? + { + componentsList_->addItem(QString("--") + QString::fromStdString(longName)); + QString statusTip(QString::fromStdString(longName)); + componentsList_->item(componentsList_->count()-1)->setStatusTip(statusTip); + } + } + } + + // ensure the itemEntered slot is triggered + componentsList_->setMouseTracking(true); + + // when the mouse hovers over an item, set the background colour of that item + componentsList_->setStyleSheet("QListWidget::item:hover {background-color:#FFFFDD;color:black}"); +} + + +void CommandDesignerWidget::showCommandHelp(QListWidgetItem *item, bool showFullHelp) +{ + // get the command name + QString qCommand(item->text()); + std::string command = qCommand.toStdString(); + ecf::Str::replace_all(command, "--", ""); // remove the "--" from the start + + + // try to find it in our list of commands + const po::option_description* od = clientOptionsDescriptions_->find_nothrow(command, + false, /*approx, will find nearest match*/ + false, /*long_ignore_case = false*/ + false /*short_ignore_case = false*/ + ); + + if (od) + { + // get the description, but only take the first line + std::vector< std::string > lines; + ecf::Str::split(od->description(),lines,"\n"); + if (!lines.empty()) + { + QString text = qCommand + QString(": "); + commandHelpLabel_->setText(text + QString::fromStdString(lines[0])); + } + + if (showFullHelp) + { + commandManPage_->setText(qCommand + "\n\n" + QString::fromStdString(od->description())); + } + } + else + { + // not a command that we have help text for + commandHelpLabel_->setText(""); + } +} + + +void CommandDesignerWidget::on_tabWidget__currentChanged(int index) +{ + //bool onSaveTab = (index == TAB_SAVE); + //saveCommandGroupBox_->setVisible(onSaveTab); + //saveOptionsButton_->setVisible(!onSaveTab); +} + + + +// when the mouse moves over a command, display the help text for it +void CommandDesignerWidget::on_componentsList__itemEntered(QListWidgetItem *item) +{ + showCommandHelp(item, false); + initialiseCommandLine(); +} + +// when the mouse is clicked on a command, display the help text for it +void CommandDesignerWidget::on_componentsList__itemClicked(QListWidgetItem *item) +{ + showCommandHelp(item, true); + commandLineEdit_->setFocus(); // to keep the text cursor visible +} + +// when the mouse is double-clicked on a command, insert it into the command line box +void CommandDesignerWidget::on_componentsList__itemDoubleClicked(QListWidgetItem *item) +{ + insertComponent(item); + commandLineEdit_->setFocus(); // to keep the text cursor visible +} + + +void CommandDesignerWidget::insertComponent(QListWidgetItem *item) +{ + commandLineEdit_->insert(item->text() + " "); +} + + + +void CommandDesignerWidget::on_commandLineEdit__textChanged() +{ + // only allow to run a non-empty command, and on 1 or more nodes + runButton_->setEnabled((!commandLineEdit_->text().isEmpty()) && nodes_.size() > 0); + + currentCommandSaved_ = false; + updateSaveButtonStatus(); +} + +void CommandDesignerWidget::on_saveNameLineEdit__textChanged() +{ + currentCommandSaved_ = false; + updateSaveButtonStatus(); +} + +void CommandDesignerWidget::on_addToContextMenuCb__stateChanged() +{ + currentCommandSaved_ = false; + updateSaveButtonStatus(); +} + + +void CommandDesignerWidget::updateSaveButtonStatus() +{ + // inCommandEditMode_ means we're editing a command from the saved list + if (inCommandEditMode_) + { + overwriteButton_->setEnabled(true); + saveAsNewButton_->setEnabled(false); + } + else + { + overwriteButton_->setEnabled(false); + saveAsNewButton_->setEnabled(true); + } + + // the cancel button is only available if we're in edit mode + cancelSaveButton_->setEnabled(inCommandEditMode_); +} + + +void CommandDesignerWidget::setSavedCommandsButtonStatus() +{ + int row = savedCommandsTable_->currentRow(); + bool isRowSelected = (row != -1); + deleteCommandButton_ ->setEnabled(isRowSelected); + editCommandButton_ ->setEnabled(isRowSelected); + duplicateCommandButton_->setEnabled(isRowSelected); + useCommandButton_ ->setEnabled(isRowSelected); + + upButton_ ->setEnabled(isRowSelected && row != 0); // not the first row + downButton_->setEnabled(isRowSelected && row != savedCommandsTable_->rowCount()-1); // not the last row +} + + +bool CommandDesignerWidget::validSaveName(const std::string &name) +{ + // name empty? + if (name.empty()) + { + QMessageBox::critical(0,QObject::tr("Custom command"), tr("Please enter a name for the command")); + return false; + } + + + // is there already a command with this name? + int commandWithThisName = CustomSavedCommandHandler::instance()->findIndexFromName(name); + bool nameUnique; + if (inCommandEditMode_) + nameUnique = (commandWithThisName == -1 || commandWithThisName == savedCommandsTable_->currentRow()); + else + nameUnique = (commandWithThisName == -1); + + if (!nameUnique) + { + QMessageBox::critical(0,QObject::tr("Custom command"), tr("A command with that name already exists - please choose another name")); + return false; + } + else + { + return true; + } +} + +void CommandDesignerWidget::selectRow(int row) +{ + savedCommandsTable_->setCurrentCell(row, 0); +} + + +void CommandDesignerWidget::selectLastSavedCommand() +{ + int lastRow = savedCommandsTable_->rowCount()-1; + selectRow(lastRow); +} + +// swap the commands in these two positions and select the one which will end up in the second position +void CommandDesignerWidget::swapSavedCommands(int i1, int i2) +{ + CustomSavedCommandHandler::instance()->swapCommandsByIndex(i1, i2); + refreshSavedCommandList(); + selectRow(i2); + setSavedCommandsButtonStatus(); + saveCommandsOnExit_ = true; // we won't save the commands yet, but mark for save on exit +} + + +void CommandDesignerWidget::on_saveAsNewButton__clicked() +{ + std::string name, command; + bool context; + + name = saveNameLineEdit_->text().toStdString(); + command = commandLineEdit_->text().toStdString(); + context = addToContextMenuCb_->isChecked(); + + if (validSaveName(name)) + { + CustomSavedCommandHandler::instance()->add(name, command, context, true); + refreshSavedCommandList(); + currentCommandSaved_ = true; + updateSaveButtonStatus(); + changeToTab(TAB_SAVE); + selectLastSavedCommand(); + setSavedCommandsButtonStatus(); + setSaveOptionsState(true, true); + } +} + +void CommandDesignerWidget::on_overwriteButton__clicked() +{ + std::string name, command; + bool context; + + name = saveNameLineEdit_->text().toStdString(); + command = commandLineEdit_->text().toStdString(); + context = addToContextMenuCb_->isChecked(); + + if (validSaveName(name)) + { + CustomSavedCommandHandler::instance()->replace(savedCommandsTable_->currentRow(), name, command, context); + savedCommandsTable_->setEnabled(true); // to show that we are no longer busy editing an entry + inCommandEditMode_ = false; + refreshSavedCommandList(); + currentCommandSaved_ = true; + updateSaveButtonStatus(); + setSaveOptionsState(true, true); + } +} + + +void CommandDesignerWidget::on_runButton__clicked() +{ + std::string command = commandLineEdit_->text().toStdString(); + + // save this in the command history + CustomCommandHistoryHandler::instance()->add(command, command, true, true); + + + // close the dialogue - the calling function will call the command() function + // to retrieve the user's command + //accept(); +} + +void CommandDesignerWidget::setSaveOptionsState(bool optionsVisible, bool saveOptionsButtonEnabled) +{ + // we just switch to the Saved Commands tab + //changeToTab(TAB_SAVE); + + saveCommandGroupBox_->setVisible(optionsVisible); + QString buttonText = (optionsVisible) ? "Save Options <<" : "Save Options >>"; + saveOptionsButton_->setText(buttonText); + saveOptionsVisible_ = optionsVisible; + saveOptionsButton_->setEnabled(saveOptionsButtonEnabled); +} + + +void CommandDesignerWidget::on_saveOptionsButton__clicked() +{ + setSaveOptionsState(!saveOptionsVisible_, true); + + // we just switch to the Saved Commands tab + //changeToTab(TAB_SAVE); +} + + +void CommandDesignerWidget::on_useCommandButton__clicked() +{ + // just put the command into the command edit box + int row = savedCommandsTable_->currentRow(); + QTableWidgetItem *commandItem = savedCommandsTable_->item(row, 2); + commandLineEdit_->setText(commandItem->text()); +} + + +void CommandDesignerWidget::on_editCommandButton__clicked() +{ + int row = savedCommandsTable_->currentRow(); + + // get the details of this command from the table + QTableWidgetItem *nameItem = savedCommandsTable_->item(row, 0); + QTableWidgetItem *contextItem = savedCommandsTable_->item(row, 1); + QTableWidgetItem *commandItem = savedCommandsTable_->item(row, 2); + + + inCommandEditMode_ = true; + + // insert the details into the edit boxes + commandLineEdit_->setText(commandItem->text()); + saveNameLineEdit_->setText(nameItem->text()); + std::string context = contextItem->text().toStdString(); + addToContextMenuCb_->setChecked(CustomSavedCommandHandler::instance()->stringToBool(context)); + + savedCommandsTable_ ->setEnabled(false); // to show that we are busy editing an entry + deleteCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry + editCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry + duplicateCommandButton_->setEnabled(false); // to show that we are busy editing an entry + useCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry + upButton_ ->setEnabled(false); // to show that we are busy editing an entry + downButton_ ->setEnabled(false); // to show that we are busy editing an entry + + updateSaveButtonStatus(); + + // users should have the Save Options visible + setSaveOptionsState(true, false); +} + + +void CommandDesignerWidget::on_savedCommandsTable__cellDoubleClicked(int row, int column) +{ + on_editCommandButton__clicked(); // same as selecting a cell and clicking 'edit' +} + +void CommandDesignerWidget::on_duplicateCommandButton__clicked() +{ + CustomSavedCommandHandler::instance()->duplicate(savedCommandsTable_->currentRow()); + refreshSavedCommandList(); +} + + +void CommandDesignerWidget::on_deleteCommandButton__clicked() +{ + CustomSavedCommandHandler::instance()->remove(savedCommandsTable_->currentRow()); + refreshSavedCommandList(); +} + +void CommandDesignerWidget::on_upButton__clicked() +{ + int row = savedCommandsTable_->currentRow(); + swapSavedCommands(row, row-1); +} + +void CommandDesignerWidget::on_downButton__clicked() +{ + int row = savedCommandsTable_->currentRow(); + swapSavedCommands(row, row+1); +} + + +void CommandDesignerWidget::on_cancelSaveButton__clicked() +{ + savedCommandsTable_->setEnabled(true); // to show that we are no longer busy editing an entry + inCommandEditMode_ = false; + updateSaveButtonStatus(); + refreshSavedCommandList(); + setSaveOptionsState(true, true); +} + + +void CommandDesignerWidget::refreshSavedCommandList() +{ + int n = CustomSavedCommandHandler::instance()->numCommands(); + + savedCommandsTable_->clearContents(); + + for (int i = 0; i < n; i++) + { + CustomCommand *command = CustomSavedCommandHandler::instance()->commandFromIndex(i); + addCommandToSavedList(command, i); + } + savedCommandsTable_->setRowCount(n); + setSavedCommandsButtonStatus(); +} + + +void CommandDesignerWidget::addCommandToSavedList(CustomCommand *command, int row) +{ + QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(command->name())); + QTableWidgetItem *contextItem = new QTableWidgetItem(QString::fromStdString(command->contextString())); + QTableWidgetItem *commandItem = new QTableWidgetItem(QString::fromStdString(command->command())); + + // if the command already exists (by name) then we will replaced it; + // otherwise add a new row to the table + +// int thisRow = CustomCommandHandler::instance()->findIndex(command->name()); +// if (thisRow == -1) +// thisRow = savedCommandsTable_->rowCount(); + + //contextItem->setCheckable(); + + int lastRow = savedCommandsTable_->rowCount()-1; + + if (row > lastRow) + savedCommandsTable_->insertRow(row); + + savedCommandsTable_->setItem(row, 0, nameItem); + savedCommandsTable_->setItem(row, 1, contextItem); + savedCommandsTable_->setItem(row, 2, commandItem); +} + + +void CommandDesignerWidget::on_savedCommandsTable__cellClicked(int row, int column) +{ + setSavedCommandsButtonStatus(); +} + + +// the caller has asked for a 'fake' menu item from the dialogue, so we create it here +MenuItem &CommandDesignerWidget::menuItem() +{ + // put the right information into our menu item and return a reference to it + menuItem_.setCommand(commandLineEdit_->text().toStdString()); + menuItem_.setCustom(true); + + if (menuItem_.visibleCondition() == NULL) + { + BaseNodeCondition *trueCond = new TrueNodeCondition(); + menuItem_.setEnabledCondition(trueCond); + menuItem_.setVisibleCondition(trueCond); + menuItem_.setQuestionCondition(trueCond); + } + return menuItem_; +} + + +/* +bool CommandDesignerWidget::eventFilter(QObject* object, QEvent* event) +{ + if(object == commandLineEdit_ && event->type() == QEvent::FocusIn) + { + initialiseCommandLine(); + return false; + } + return false; +} +*/ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,103 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + + +#ifndef COMMANDDESIGNERWIDGET_HPP_ +#define COMMANDDESIGNERWIDGET_HPP_ + +#include + +class CommandLineEdit; + +#include "ui_CommandDesignerWidget.h" +#include "CustomCommandHandler.hpp" +#include "NodeQueryResultModel.hpp" +#include "MenuHandler.hpp" + +#include "CtsCmdRegistry.hpp" + +class CommandDesignerWidget : public QWidget, private Ui::commandDesignerWidget +{ + Q_OBJECT + +public: + explicit CommandDesignerWidget(QWidget *parent = 0); + ~CommandDesignerWidget(); + + MenuItem &menuItem(); + void setNodes(std::vector &nodes); + std::vector& selectedNodes(); + +public Q_SLOTS: + void insertComponent(QListWidgetItem *); + void on_commandLineEdit__textChanged(); + void on_saveNameLineEdit__textChanged(); + void on_addToContextMenuCb__stateChanged(); + void on_overwriteButton__clicked(); + void on_saveAsNewButton__clicked(); + void on_runButton__clicked(); + void on_saveOptionsButton__clicked(); + void on_editCommandButton__clicked(); + void on_useCommandButton__clicked(); + void on_duplicateCommandButton__clicked(); + void on_deleteCommandButton__clicked(); + void on_upButton__clicked(); + void on_downButton__clicked(); + void on_cancelSaveButton__clicked(); + void on_savedCommandsTable__cellClicked(int row, int column); + void on_savedCommandsTable__cellDoubleClicked(int row, int column); + void on_componentsList__itemEntered(QListWidgetItem *item); + void on_componentsList__itemClicked(QListWidgetItem *item); + void on_componentsList__itemDoubleClicked(QListWidgetItem *item); + void on_nodeListLinkLabel__linkActivated(const QString &link); + void on_nodeSelectionChanged(); + void on_tabWidget__currentChanged(int index); + QPushButton *runButton() {return runButton_;} + + +private: + enum TabIndexes {TAB_BUILD, TAB_NODES, TAB_SAVE}; + + void initialiseComponentListDetails(); + void updateSaveButtonStatus(); + void addCommandToSavedList(CustomCommand *command, int row); + void refreshSavedCommandList(); + void addClientCommandsToComponentList(); + void showCommandHelp(QListWidgetItem *item, bool showFullHelp); + void initialiseCommandLine(); + void setNodeNumberLinkText(int numNodes); + void setSavedCommandsButtonStatus(); + bool validSaveName(const std::string &name); + void changeToTab(TabIndexes i); + void selectRow(int row); + void selectLastSavedCommand(); + void swapSavedCommands(int i1, int i2); + void setSaveOptionsState(bool optionsVisible, bool saveOptionsButtonEnabled); + + //bool eventFilter(QObject* object, QEvent* event); + + bool currentCommandSaved_; + bool haveSetUpDefaultCommandLine_; + bool inCommandEditMode_; + bool saveCommandsOnExit_; + bool saveOptionsVisible_; + std::vector componentBlacklist_; + MenuItem menuItem_; + + std::vector nodes_; + NodeQueryResultModel nodeModel_; + + CtsCmdRegistry cmdRegistry_; + boost::program_options::options_description* clientOptionsDescriptions_; +}; + + + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandDesignerWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandDesignerWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,398 @@ + + + commandDesignerWidget + + + + 0 + 0 + 940 + 579 + + + + Form + + + + + + + + Components + + + + + + true + + + 2 + + + + Build command + + + + + + + + + + 0 + 1 + + + + QFrame::NoFrame + + + QFrame::Raised + + + + + + + 0 + 0 + + + + + ecflow_client + + + + + <node_name> + + + + + <full_name> + + + + + + + + + Monospace + + + + + + + + + + + Description + + + + + + + + Selected Nodes + + + + + + + + + + Saved commands + + + + + + QAbstractItemView::NoEditTriggers + + + true + + + false + + + QAbstractItemView::NoDragDrop + + + Qt::IgnoreAction + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + true + + + false + + + true + + + + Name + + + + + Context menu + + + + + Command + + + + + + + + + + &Use + + + + + + + &Edit + + + + + + + Du&plicate + + + + + + + &Remove + + + + + + + + + + + :/viewer/images/arrow_up.svg:/viewer/images/arrow_up.svg + + + + + + + + + + + :/viewer/images/arrow_down.svg:/viewer/images/arrow_down.svg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + + + + + + Command + + + + + + + + + + Command: + + + + + + + + + + &Run + + + + + + + + 100 + 16777215 + + + + &Save ... + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p><a href="#nodes"><span style=" text-decoration: underline; color:#0057ae;">Nodes selected</span></a></p></body></html> + + + + + + + + 0 + 0 + + + + + 500 + 16777215 + + + + + + + + + + + + Name: + + + + + + + + + + + + Add to context menu + + + + + + + + + &Save + + + + + + + Save As &New + + + + + + + &Cancel + + + + + + + + + + + + + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + NodeQueryResultView + QTreeView +
      NodeQueryResultView.hpp
      +
      +
      + + saveNameLineEdit_ + tabWidget_ + savedCommandsTable_ + addToContextMenuCb_ + saveAsNewButton_ + + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,233 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CommandHandler.hpp" + +//#include "File.hpp" +//#include "NodeFwd.hpp" +//#include "ArgvCreator.hpp" +#include "Str.hpp" + +#include "ServerHandler.hpp" +#include "ShellCommand.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "VNode.hpp" + +#include +#include + +#include + +//Send the same command for a list of objects (nodes/servers) specified in a VInfo vector. +//The command is specified as a string. + +void CommandHandler::run(std::vector info, const std::string& cmd) +{ + UI_FUNCTION_LOG + + std::string realCommand(cmd); + std::vector targetServers; + + if(realCommand.empty()) + { + //UiLog().err() << " command is not recognised. Check the menu definition."; + UserMessage::message(UserMessage::ERROR, true, "command " + cmd + + " is not recognised. Check the menu definition."); + return; + } + + UiLog().dbg() << "command=" << cmd; + + std::map targetNodeNames; + std::map targetNodeFullNames; + std::map targetParentFullNames; + + //Figure out what objects (node/server) the command should be applied to + for(std::size_t i=0; i < info.size(); i++) + { + std::string nodeFullName; + std::string nodeName; + std::string parentFullName; + + if(realCommand.find("") != std::string::npos) + { + nodeName=info[i]->name(); + } + + if(realCommand.find("") != std::string::npos) + { + if(info[i]->isNode()) + nodeFullName = info[i]->node()->absNodePath(); + else if(info[i]->isServer()) + info[i]->server()->longName(); + else if(info[i]->isAttribute()) + parentFullName = info[i]->node()->absNodePath(); + } + + if(realCommand.find("") != std::string::npos) + { + if(info[i]->isNode()) + { + if(VNode *p=info[i]->node()->parent()) + parentFullName = p->absNodePath(); + } + else if(info[i]->isAttribute()) + parentFullName = info[i]->node()->absNodePath(); + } + + //Store the names per target servers + targetNodeNames[info[i]->server()] += " " + nodeName; + targetNodeFullNames[info[i]->server()] += " " + nodeFullName; + targetParentFullNames[info[i]->server()] += " " + parentFullName; + + // add this to our list of target servers? + if(std::find(targetServers.begin(), targetServers.end(), info[i]->server()) == targetServers.end()) + { + targetServers.push_back(info[i]->server()); + } + } + + // for each target server, construct and send its command + for(size_t s = 0; s < targetServers.size(); s++) + { + ServerHandler* serverHandler = targetServers[s]; + + // replace placeholders with real node names + std::string placeholder(""); + ecf::Str::replace_all(realCommand, placeholder, targetNodeFullNames[serverHandler]); + + placeholder = ""; + ecf::Str::replace_all(realCommand, placeholder, targetNodeNames[serverHandler]); + + placeholder = ""; + ecf::Str::replace_all(realCommand, placeholder, targetParentFullNames[serverHandler]); + + //Shell command + if(realCommand.find("sh ") == 0) + { + substituteVariables(realCommand,info); + UiLog().dbg() << " final command: " << realCommand; + ShellCommand::run(realCommand,cmd); + return; + } + + UiLog().dbg() << " final command: " << realCommand; + + // get the command into the right format by first splitting into tokens + // and then converting to argc, argv format + std::vector strs; + std::string delimiters(" "); + ecf::Str::split(realCommand, strs, delimiters); + + // set up and run the thread for server communication + serverHandler->runCommand(strs); + } +} + +//Send a command to a server. The command is specified as a string vector, while the node or server for that +//the command will be applied is specified in a VInfo object. +void CommandHandler::run(VInfo_ptr info,const std::vector& cmd) +{ + UI_FUNCTION_LOG + + std::vector realCommand=cmd; + + if(realCommand.empty()) + { + //UiLog().err() << " command is not recognised!"; + UserMessage::message(UserMessage::ERROR, true, "command is not recognised."); + } + + UiLog().dbg() << "command: " << commandToString(realCommand); + + //Get the name of the object for that the command will be applied + std::string nodeFullName; + std::string nodeName; + ServerHandler* serverHandler = info->server(); + + if(info->isNode() || info->isAttribute()) + { + nodeFullName = info->node()->node()->absNodePath(); + nodeName = info->node()->node()->name(); + //UserMessage::message(UserMessage::DBG, false, std::string(" --> for node: ") + nodeFullName + " (server: " + info[i]->server()->longName() + ")"); + } + else if(info->isServer()) + { + nodeFullName = "/"; + nodeName = "/"; + //UserMessage::message(UserMessage::DBG, false, std::string(" --> for server: ") + nodeFullName); + } + + //Replace placeholders with real node names + for(std::size_t i=0; i < cmd.size(); i++) + { + if(realCommand[i]=="") + realCommand[i]=nodeFullName; + else if(realCommand[i]=="") + realCommand[i]=nodeName; + } + + UiLog().dbg() << " final command: " << commandToString(realCommand); + + // get the command into the right format by first splitting into tokens + // and then converting to argc, argv format + + //std::vector strs; + //std::string delimiters(" "); + //ecf::Str::split(realCommand, strs, delimiters); + + // set up and run the thread for server communication + serverHandler->runCommand(realCommand); +} + +void CommandHandler::run(VInfo_ptr info, const std::string& cmd) +{ + std::vector commands; + + ecf::Str::split(cmd, commands); + run(info, commands); +} + +std::string CommandHandler::commandToString(const std::vector& cmd) +{ + std::string s; + for(std::vector::const_iterator it=cmd.begin(); it != cmd.end(); ++it) + { + if(!s.empty()) s+=" "; + s+=*it; + } + return s; +} + +void CommandHandler::substituteVariables(std::string& cmd,const std::vector& info) +{ + if(info.size() > 0) + { + VNode *n=info[0]->node(); + if(!n || n->isAttribute()) + return; + + QString txt=QString::fromStdString(cmd); + QString txtRes=txt; + QRegExp rx("%(.*)%"); + rx.setMinimal(true); + int pos=0; + while ((pos = rx.indexIn(txt, pos)) != -1) + { + QString name=rx.cap(1); + pos += rx.matchedLength(); + std::string value=n->findInheritedVariable(name.toStdString(),true); + txtRes.replace("%" + name + "%",QString::fromStdString(value)); + } + + cmd = txtRes.toStdString(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef COMMANDHANDLER_HPP +#define COMMANDHANDLER_HPP + +#include "VInfo.hpp" + +//Class to interpret generic commands and send them +// 1. to ServerHandler if they are "ecflow_client" commands +// 2. to ShellCommand if they are shell commands + +class CommandHandler +{ +public: + static void run(std::vector,const std::string&); + static void run(VInfo_ptr,const std::vector&); + static void run(VInfo_ptr,const std::string&); + +protected: + static std::string commandToString(const std::vector& cmd); + static void substituteVariables(std::string& cmd,const std::vector& info); +}; + +#endif // COMMANDHANDLER_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutput.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutput.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutput.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutput.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,199 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CommandOutput.hpp" + +#include "CommandOutputDialog.hpp" + +CommandOutputHandler* CommandOutputHandler::instance_=0; + +//=============================================== +// +// CommandOutput +// +//============================================== + +CommandOutput::CommandOutput(QString cmd,QString cmdDef,QDateTime runTime) : + enabled_(true), command_(cmd), commandDef_(cmdDef), + runTime_(runTime), status_(RunningStatus) +{ + +} + +void CommandOutput::appendOutput(QString txt,int maxSize,bool& trimmed) +{ + output_+=txt; + trimmed=false; + if(output_.size() > maxSize) + { + output_=output_.right(maxSize-100); + trimmed=true; + } +} + +void CommandOutput::appendError(QString txt,int maxSize,bool& trimmed) +{ + error_+=txt; + trimmed=false; + if(error_.size() > maxSize) + { + error_=error_.right(maxSize-100); + trimmed=true; + } +} + +QString CommandOutput::statusStr() const +{ + static QString finishedStr("finished"); + static QString failedStr("failed"); + static QString runningStr("running"); + + switch(status_) + { + case FinishedStatus: + return finishedStr; + case FailedStatus: + return failedStr; + case RunningStatus: + return runningStr; + default: + return QString(); + } + + return QString(); +} + +QColor CommandOutput::statusColour() const +{ + static QColor redColour(255,0,0); + static QColor greenColour(9,160,63); + static QColor blackColour(0,0,0); + + switch(status_) + { + case CommandOutput::FinishedStatus: + return blackColour; + case CommandOutput::FailedStatus: + return redColour; + case CommandOutput::RunningStatus: + return greenColour; + default: + return blackColour; + } + + return blackColour; +} + +//=============================================== +// +// CommandOutputHandler +// +//=============================================== + +CommandOutputHandler::CommandOutputHandler(QObject* parent) : + QObject(parent), + maxNum_(25), + maxOutputSize_(1000000), + maxErrorSize_(30000) +{ + +} + +CommandOutputHandler* CommandOutputHandler::instance() +{ + if(!instance_) + instance_=new CommandOutputHandler(0); + + return instance_; +} + +int CommandOutputHandler::indexOfItem(CommandOutput_ptr item) const +{ + if(!item) + return -1; + + for(int i=0; i < items_.count(); i++) + { + if(item.get() == items_[i].get()) + return i; + } + + return -1; +} + +void CommandOutputHandler::appendOutput(CommandOutput_ptr item,QString txt) +{ + if(item) + { + bool trimmed=false; + item->appendOutput(txt,maxOutputSize_,trimmed); + CommandOutputDialog::showDialog(); + if(trimmed==false) + Q_EMIT itemOutputAppend(item,txt); + else + Q_EMIT itemOutputReload(item); + } +} + +void CommandOutputHandler::appendError(CommandOutput_ptr item,QString txt) +{ + if(item) + { + bool trimmed=false; + item->appendError(txt,maxErrorSize_,trimmed); + CommandOutputDialog::showDialog(); + if(trimmed==false) + Q_EMIT itemErrorAppend(item,txt); + else + Q_EMIT itemErrorReload(item); + } +} + +void CommandOutputHandler::finished(CommandOutput_ptr item) +{ + if(item) + { + item->setStatus(CommandOutput::FinishedStatus); + Q_EMIT itemStatusChanged(item); + } +} + +void CommandOutputHandler::failed(CommandOutput_ptr item) +{ + if(item) + { + item->setStatus(CommandOutput::FailedStatus); + Q_EMIT itemStatusChanged(item); + } +} + +CommandOutput_ptr CommandOutputHandler::addItem(QString cmd,QString cmdDef,QDateTime runTime) +{ + CommandOutputDialog::showDialog(); + CommandOutput_ptr item= + CommandOutput_ptr(new CommandOutput(cmd,cmdDef,runTime)); + Q_EMIT itemAddBegin(); + items_ << item; + checkItems(); + Q_EMIT itemAddEnd(); + return item; +} + +void CommandOutputHandler::checkItems() +{ + Q_ASSERT(maxNum_ >0); + while(items_.count() > maxNum_) + { + Q_ASSERT(items_.count() > 0); + CommandOutput_ptr item=items_.first(); + item->setEnabled(false); + items_.remove(0); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,118 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include +#include + +#include "CommandOutputDialog.hpp" +#include "SessionHandler.hpp" +#include "VConfig.hpp" +#include "WidgetNameProvider.hpp" + +CommandOutputDialog* CommandOutputDialog::dialog_=0; + +CommandOutputDialog::CommandOutputDialog(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose); + + QString wt=windowTitle(); + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + //connect(queryWidget_,SIGNAL(closeClicked()), + // this,SLOT(accept())); + + //Read the qt settings + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +CommandOutputDialog::~CommandOutputDialog() +{ +} + +void CommandOutputDialog::closeEvent(QCloseEvent * event) +{ + //queryWidget_->slotStop(); //The search thread might be running!! + dialog_=0; + event->accept(); + writeSettings(); +} + +void CommandOutputDialog::accept() +{ + dialog_=0; + writeSettings(); + QDialog::accept(); +} + +void CommandOutputDialog::reject() +{ + dialog_=0; + writeSettings(); + QDialog::reject(); +} + +void CommandOutputDialog::showDialog() +{ + if(!dialog_) + { + dialog_=new CommandOutputDialog(0); + dialog_->show(); + } + dialog_->raise(); +} + +//------------------------------------------ +// Settings read/write +//------------------------------------------ + +void CommandOutputDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("CommandOutputDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + widget_->writeSettings(settings); + settings.endGroup(); +} + +void CommandOutputDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("CommandOutputDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(550,540)); + } + + widget_->readSettings(settings); + + settings.endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef COMMANDOUTPUTDIALOG_HPP +#define COMMANDOUTPUTDIALOG_HPP + +#include + +#include "ui_CommandOutputDialog.h" + +class ShellCommand; + +class CommandOutputDialog : public QDialog, protected Ui::CommandOutputDialog +{ + Q_OBJECT + +public: + static void showDialog(); + +protected Q_SLOTS: + void accept(); + void reject(); + +protected: + explicit CommandOutputDialog(QWidget *parent = 0); + ~CommandOutputDialog(); + + void closeEvent(QCloseEvent * event); + +private: + void readSettings(); + void writeSettings(); + + static CommandOutputDialog* dialog_; +}; + +#endif // COMMANDOUTPUTDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,90 @@ + + + CommandOutputDialog + + + + 0 + 0 + 400 + 300 + + + + Shell command output + + + + 5 + + + 4 + + + 2 + + + 4 + + + 4 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + CommandOutputWidget + QWidget +
      CommandOutputWidget.hpp
      + 1 +
      +
      + + + + buttonBox + accepted() + CommandOutputDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + CommandOutputDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutput.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutput.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutput.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutput.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,100 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef COMMANDOUTPUT_HPP +#define COMMANDOUTPUT_HPP + +#include +#include +#include +#include +#include + +#include +#include + +class CommandOutput; +typedef boost::shared_ptr CommandOutput_ptr; + +class CommandOutputHandler; + +class CommandOutput +{ + friend class CommandOutputHandler; + +public: + enum Status {RunningStatus,FinishedStatus,FailedStatus}; + + QString command() const {return command_;} + QString commandDefinition() const {return commandDef_;} + QDateTime runTime() const {return runTime_;} + QString output() const {return output_;} + QString error() const {return error_;} + Status status() const {return status_;} + QString statusStr() const; + QColor statusColour() const; + bool isEnabled() const {return enabled_;} + +protected: + CommandOutput(QString cmd,QString cmdDef,QDateTime runTime); + + void appendOutput(QString,int,bool&); + void appendError(QString,int,bool&); + void setStatus(Status s) {status_=s;} + void setEnabled(bool b) {enabled_=b;} + + bool enabled_; + QString command_; + QString commandDef_; + QDateTime runTime_; + QString output_; + QString error_; + Status status_; +}; + +class CommandOutputHandler : public QObject +{ + Q_OBJECT +public: + static CommandOutputHandler* instance(); + + void appendOutput(CommandOutput_ptr,QString); + void appendError(CommandOutput_ptr,QString); + void finished(CommandOutput_ptr); + void failed(CommandOutput_ptr); + + CommandOutput_ptr addItem(QString cmd,QString cmdDef,QDateTime runTime); + QVector items() const {return items_;} + int itemCount() const {return items_.count();} + int indexOfItem(CommandOutput_ptr) const; + +Q_SIGNALS: + void itemAddBegin(); + void itemAddEnd(); + void itemOutputAppend(CommandOutput_ptr,QString); + void itemErrorAppend(CommandOutput_ptr,QString); + void itemOutputReload(CommandOutput_ptr); + void itemErrorReload(CommandOutput_ptr); + void itemStatusChanged(CommandOutput_ptr); + void itemsReloaded(); + +protected: + CommandOutputHandler(QObject*); + void checkItems(); + + static CommandOutputHandler* instance_; + int maxNum_; + int maxOutputSize_; + int maxErrorSize_; + QVector items_; +}; + + +#endif // COMMANDOUTPUT_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,424 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CommandOutputWidget.hpp" + +#include +#include + +#include "ModelColumn.hpp" +#include "CommandOutput.hpp" +#include "TextFormat.hpp" +#include "ViewerUtil.hpp" + +CommandOutputModel::CommandOutputModel(QObject *parent) : + QAbstractItemModel(parent), + columns_(0) +{ + columns_=ModelColumn::def("output_columns"); + + assert(columns_); +} + +CommandOutputModel::~CommandOutputModel() +{ +} + +bool CommandOutputModel::hasData() const +{ + return CommandOutputHandler::instance()->itemCount() > 0; +} + +void CommandOutputModel::dataIsAboutToChange() +{ + beginResetModel(); +} + +void CommandOutputModel::dataChanged() +{ + endResetModel(); +} + +int CommandOutputModel::columnCount( const QModelIndex& /*parent */) const +{ + return columns_->count(); +} + +int CommandOutputModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + //Parent is the root: + if(!parent.isValid()) + { + return CommandOutputHandler::instance()->itemCount(); + } + + return 0; +} + +Qt::ItemFlags CommandOutputModel::flags ( const QModelIndex & index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QVariant CommandOutputModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid() || !hasData()) + { + return QVariant(); + } + + int pos=CommandOutputHandler::instance()->itemCount()-index.row()-1; + if(pos < 0 || pos >= CommandOutputHandler::instance()->itemCount()) + return QVariant(); + + QString id=columns_->id(index.column()); + + CommandOutput_ptr item=CommandOutputHandler::instance()->items()[pos]; + if(!item) + return QVariant(); + + if(role == Qt::DisplayRole) + { + if(id == "command") + return item->command(); + else if(id == "status") + { + return item->statusStr(); + } + else if(id == "runtime") + return item->runTime().toString("yyyy-MM-dd hh:mm:ss"); + else + return QVariant(); + } + else if(role == Qt::ForegroundRole) + { + if(id == "status") + { + return item->statusColour(); + } + return QVariant(); + } + return QVariant(); +} + +QVariant CommandOutputModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::UserRole )) + return QAbstractItemModel::headerData( section, orient, role ); + + if(role == Qt::DisplayRole) + return columns_->label(section); + else if(role == Qt::UserRole) + return columns_->id(section); + + return QVariant(); +} + +QModelIndex CommandOutputModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex CommandOutputModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + + +CommandOutput_ptr CommandOutputModel::indexToItem(const QModelIndex& idx) const +{ + if(idx.isValid() && hasData()) + { + int pos=CommandOutputHandler::instance()->itemCount()-idx.row()-1; + if(pos >= 0 || pos < CommandOutputHandler::instance()->itemCount()) + return CommandOutputHandler::instance()->items()[pos]; + } + + CommandOutput_ptr r; + return r; +} + +QModelIndex CommandOutputModel::itemToStatusIndex(CommandOutput_ptr item) const +{ + if(item) + { + int pos=CommandOutputHandler::instance()->indexOfItem(item); + if(pos != -1) + { + int row=CommandOutputHandler::instance()->itemCount()-pos-1; + if(row >=0 && row < rowCount()) + return index(row,columns_->indexOf("status")); + } + } + + return QModelIndex(); +} + +//============================================== +// +// CommandOutputWidget +// +//============================================== + +CommandOutputWidget::CommandOutputWidget(QWidget *parent) : + QWidget(parent) +{ + setupUi(this); + + infoLabel_->setProperty("fileInfo","1"); + + messageLabel_->hide(); + + model_=new CommandOutputModel(this); + + tree_->setModel(model_); + tree_->setRootIsDecorated(false); + + //Adjust the tree columns + QFont f; + QFontMetrics fm(f); + tree_->setColumnWidth(0,fm.width(" sh ecflow_client --port %ECF_PORT% --host %ECF_HOST% --stats--port %ECF_PORT%")); + tree_->setColumnWidth(1,fm.width(" running ")); + + textEdit_->setShowLineNumbers(false); + + searchLine_->setEditor(textEdit_); + searchLine_->setVisible(false); + + CommandOutputHandler* handler=CommandOutputHandler::instance(); + Q_ASSERT(handler); + + connect(handler,SIGNAL(itemAddBegin()), + this,SLOT(slotItemAddBegin())); + + connect(handler,SIGNAL(itemAddEnd()), + this,SLOT(slotItemAddEnd())); + + connect(handler,SIGNAL(itemOutputAppend(CommandOutput_ptr,QString)), + this,SLOT(slotItemOutputAppend(CommandOutput_ptr,QString))); + + connect(handler,SIGNAL(itemErrorAppend(CommandOutput_ptr,QString)), + this,SLOT(slotItemErrorAppend(CommandOutput_ptr,QString))); + + connect(handler,SIGNAL(itemOutputReload(CommandOutput_ptr)), + this,SLOT(slotItemOutputReload(CommandOutput_ptr))); + + connect(handler,SIGNAL(itemErrorReload(CommandOutput_ptr)), + this,SLOT(slotItemErrorReload(CommandOutput_ptr))); + + connect(handler,SIGNAL(itemStatusChanged(CommandOutput_ptr)), + this,SLOT(slotItemStatusChanged(CommandOutput_ptr))); + + splitter_->setCollapsible(1,false); + + //The selection changes in the view + connect(tree_->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); + + if(model_->rowCount() > 0) + tree_->setCurrentIndex(model_->index(0,0)); +} + +CommandOutputWidget::~CommandOutputWidget() +{ +} + +bool CommandOutputWidget::isCurrent(CommandOutput_ptr item) +{ + return (item && item.get() == model_->indexToItem(tree_->currentIndex()).get()); +} + +void CommandOutputWidget::slotItemSelected(const QModelIndex&,const QModelIndex&) +{ + CommandOutput_ptr current=model_->indexToItem(tree_->currentIndex()); + loadItem(current); +} + +void CommandOutputWidget::slotItemAddBegin() +{ + Q_ASSERT(model_); + model_->dataIsAboutToChange(); +} + +void CommandOutputWidget::slotItemAddEnd() +{ + Q_ASSERT(model_); + model_->dataChanged(); + if(model_->rowCount() > 0) + { + tree_->setCurrentIndex(model_->index(0,0)); + } +} + +void CommandOutputWidget::slotItemOutputAppend(CommandOutput_ptr item,QString txt) +{ + if(isCurrent(item)) + { + textEdit_->appendPlainText(txt); + } +} + +void CommandOutputWidget::slotItemErrorAppend(CommandOutput_ptr item,QString txt) +{ + if(isCurrent(item)) + { + messageLabel_->appendError(txt); + } +} + +void CommandOutputWidget::slotItemOutputReload(CommandOutput_ptr item) +{ + if(isCurrent(item)) + { + textEdit_->setPlainText(item->output()); + } +} + +void CommandOutputWidget::slotItemErrorReload(CommandOutput_ptr item) +{ + if(isCurrent(item)) + { + messageLabel_->showError(item->error()); + } +} + +void CommandOutputWidget::slotItemStatusChanged(CommandOutput_ptr item) +{ + if(item) + { + QModelIndex idx=model_->itemToStatusIndex(item); + if(idx.isValid()) + { + tree_->update(idx); + } + if(item == model_->indexToItem(tree_->currentIndex())) + { + updateInfoLabel(item); + } + } +} + +void CommandOutputWidget::loadItem(CommandOutput_ptr item) +{ + if(item) + { + textEdit_->clear(); + messageLabel_->clear(); + messageLabel_->hide(); + updateInfoLabel(item); + + //Set output text + textEdit_->setPlainText(item->output()); + + //Set error text + QString err=item->error(); + if(!err.isEmpty()) + { + messageLabel_->showError(err); + } + //return true; + } +} + +void CommandOutputWidget::updateInfoLabel(CommandOutput_ptr item) +{ + if(!item) + { + infoLabel_->clear(); + return; + } + + QColor boldCol(39,49,101); + QColor defCol(90,90,90); + QString s=Viewer::formatBoldText("Command: ",boldCol) + item->command() + "
      " + + Viewer::formatBoldText("Definition: ",boldCol) + + Viewer::formatText(item->commandDefinition(),defCol) + "
      " + + Viewer::formatBoldText("Started at: ",boldCol) + + item->runTime().toString("yyyy-MM-dd hh:mm:ss") + + Viewer::formatBoldText("  Status: ", boldCol) + + Viewer::formatText(item->statusStr(),item->statusColour()); + + infoLabel_->setText(s); +} + +void CommandOutputWidget::removeSpacer() +{ + //Remove the first spacer item!! + for(int i=0; horizontalLayout->count(); i++) + { + if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) + { + horizontalLayout->takeAt(i); + delete sp; + break; + } + } +} + +void CommandOutputWidget::on_searchTb__clicked() +{ + searchLine_->setVisible(true); + searchLine_->setFocus(); + searchLine_->selectAll(); +} + +void CommandOutputWidget::on_gotoLineTb__clicked() +{ + textEdit_->gotoLine(); +} + +void CommandOutputWidget::on_fontSizeUpTb__clicked() +{ + //We need to call a custom slot here instead of "zoomIn"!!! + textEdit_->slotZoomIn(); +} + +void CommandOutputWidget::on_fontSizeDownTb__clicked() +{ + //We need to call a custom slot here instead of "zoomOut"!!! + textEdit_->slotZoomOut(); +} + +void CommandOutputWidget::writeSettings(QSettings& settings) +{ + settings.beginGroup("widget"); + settings.setValue("splitter",splitter_->saveState()); + ViewerUtil::saveTreeColumnWidth(settings,"treeColumnWidth",tree_); + settings.endGroup(); +} + +void CommandOutputWidget::readSettings(QSettings& settings) +{ + settings.beginGroup("widget"); + + ViewerUtil::initTreeColumnWidth(settings,"treeColumnWidth",tree_); + + if(settings.contains("splitter")) + { + splitter_->restoreState(settings.value("splitter").toByteArray()); + } + + settings.endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,89 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef COMMANDOUTPUTWIDGET_HPP +#define COMMANDOUTPUTWIDGET_HPP + +#include +#include +#include + +#include "ui_CommandOutputWidget.h" + +#include "CommandOutput.hpp" + +class ModelColumn; + +class CommandOutputModel : public QAbstractItemModel +{ +public: + explicit CommandOutputModel(QObject *parent=0); + ~CommandOutputModel(); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + void dataIsAboutToChange(); + void dataChanged(); + bool updateData(); + bool hasData() const; + CommandOutput_ptr indexToItem(const QModelIndex& idx) const; + QModelIndex itemToStatusIndex(CommandOutput_ptr item) const; + +protected: + ModelColumn* columns_; +}; + +class CommandOutputWidget : public QWidget, protected Ui::CommandOutputWidget +{ +Q_OBJECT + +public: + explicit CommandOutputWidget(QWidget *parent=0); + ~CommandOutputWidget(); + + void readSettings(QSettings&); + void writeSettings(QSettings&); + + +protected Q_SLOTS: + void slotItemSelected(const QModelIndex&,const QModelIndex&); + void slotItemAddBegin(); + void slotItemAddEnd(); + void slotItemOutputAppend(CommandOutput_ptr,QString); + void slotItemErrorAppend(CommandOutput_ptr,QString); + void slotItemOutputReload(CommandOutput_ptr); + void slotItemErrorReload(CommandOutput_ptr); + void slotItemStatusChanged(CommandOutput_ptr); + void on_searchTb__clicked(); + void on_gotoLineTb__clicked(); + void on_fontSizeUpTb__clicked(); + void on_fontSizeDownTb__clicked(); + +Q_SIGNALS: + void editorFontSizeChanged(); + +protected: + bool isCurrent(CommandOutput_ptr item); + void loadItem(CommandOutput_ptr); + void updateInfoLabel(CommandOutput_ptr); + void removeSpacer(); + + CommandOutputModel* model_; +}; + +#endif // COMMANDOUTPUTWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/CommandOutputWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CommandOutputWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,207 @@ + + + CommandOutputWidget + + + + 0 + 0 + 557 + 671 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + false + + + QFrame::StyledPanel + + + + + + 2 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg + + + Ctrl++ + + + true + + + + + + + Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg + + + Ctrl+- + + + true + + + + + + + Show search bar (CTRL-F) + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + Goto line number (CTRL-L) + + + ... + + + + :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg + + + Ctrl+L + + + true + + + + + + + + + + + + Qt::Vertical + + + + + 0 + 1 + + + + QPlainTextEdit::NoWrap + + + true + + + + + + + + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + PlainTextEdit + QPlainTextEdit +
      PlainTextEdit.hpp
      +
      + + PlainTextSearchLine + QWidget +
      PlainTextSearchLine.hpp
      + 1 +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CompactView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CompactView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CompactView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CompactView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1045 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CompactView.hpp" + +#include "ExpandState.hpp" +#include "TreeNodeModel.hpp" +#include "TreeNodeViewDelegate.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define _UI_COMPACTVIEW_DEBUG + +CompactView::CompactView(TreeNodeModel* model,QWidget* parent) : + AbstractNodeView(model,parent) +{ + //This is needed for making the context menu work + setProperty("view","tree"); + + //we cannot call it from the constructor of the base class + //because it calls a pure virtual method + reset(); +} + +CompactView::~CompactView() +{ + +} + +//Creates and initialize the viewItem structure of the children of the element +// parentId: the items whose children are to be expanded +// recursiveExpanding: all the children will be expanded +// afterIsUninitialized: when we recurse from layout(-1) it indicates +// the items after 'i' are not yet initialized and need not to be moved + +void CompactView::layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated) +{ + //This is the root item. + if(parentId == -1) + { + rowCount_=0; + maxRowWidth_=0; + } + + QModelIndex parentIndex = (parentId < 0) ? root_ : modelIndex(parentId); + + if(parentId >=0 && !parentIndex.isValid()) + { + //modelIndex() should never return something invalid for the real items. + //This can happen if columncount has been set to 0. + //To avoid infinite loop we stop here. + return; + } + + int count=model_->rowCount(parentIndex); + bool expanding=true; + + //This is the root item. viewItems must be empty at this point. + if(parentId == -1) + { + Q_ASSERT(viewItems_.empty()); + Q_ASSERT(preAllocated == false); + viewItems_.resize(count); + afterIsUninitialized = true; //It can only be true when we expand from the root! + } + //The count of the stored children does not match the actual count + else if(viewItems_[parentId].total != (uint)count) + { + //Expand + if(!afterIsUninitialized) + { + //We called expandall for a non-root item. All the new items need must be + //already instered at this point. This is the duty of the caller routine. + //const int itemsCount = viewItems_.size(); + //if(recursiveExpanding) + if(preAllocated) + { + //We called expandAll() for a non-root item. All the needed items need must already be + //inserted at this point. This is the duty of the caller routine! + //When layout() is finished we need to adjust the parent of all the items + //after the insertion position. This is the duty of the caller routine. We + //have chosen this solution for performance reasons! + } + else + { + insertViewItems(parentId + 1, count, TreeNodeViewItem()); + } + } + //ExpandAll from the root + else if(count > 0) + viewItems_.resize(viewItems_.size() + count); + } + else + { + expanding=false; + } + + int first = parentId + 1; + int last = 0; + int children = 0; + int level=(parentId >=0?viewItems_[parentId].level+1:0); + TreeNodeViewItem *item=0; + + std::vector itemWidthVec; + std::vector itemHeightVec; + int widest=0; + for(int i=first; i < first+count; i++) + { + int w,h; + QModelIndex currentIndex=model_->index(i-first,0,parentIndex); + delegate_->sizeHint(currentIndex,w,h); + itemWidthVec.push_back(w); + itemHeightVec.push_back(h); + + if(parentId >=0 && !model_->isAttribute(currentIndex)) + if(w > widest) widest=w; +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " item=" << currentIndex.data().toString() << " w=" << w; +#endif + } + +#ifdef _UI_COMPACTVIEW_DEBUG + if(parentId >=0) + UiLog().dbg() << "layout parent=" << viewItems_[parentId].index.data().toString() << + " widest child=" << widest; +#endif + + //Iterate through the direct children of parent item. At this point all the items + //needed in the loop below are pre-allocated but not yet initialised. + for(int i=first; i < first+count; i++) + { + QModelIndex currentIndex=model_->index(i-first,0,parentIndex); + + last = i + children; + item = &viewItems_[last]; + item->parentItem = parentId; + item->index=currentIndex; + item->hasMoreSiblings=(i < first+count-1); + item->level=level; + item->expanded = false; + item->total = 0; + item->widestInSiblings=widest; + + //We compute the size of the item. For attributes we delay the width computation until we + //actually paint them and we set their width to 300. + item->width=itemWidthVec[i-first]; + item->height=itemHeightVec[i-first]; + + int xp=leftMargin_; + if(parentId >=0) + { + item->widestInSiblings=widest; + xp=viewItems_[parentId].alignedRight()+itemGap_; + } + else + { + item->widestInSiblings=item->width; + } + + item->x=xp; + + if(item->alignedRight() > maxRowWidth_) + maxRowWidth_=item->alignedRight(); + + //We need to expand the item + if(recursiveExpanding || isIndexExpanded(currentIndex)) + { + if(recursiveExpanding) + expandedIndexes.insert(currentIndex); + + item->expanded = true; + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " before " << item->index.data().toString() << " total=" << item->total; +#endif + //Add the children to the layout + layout(last,recursiveExpanding,afterIsUninitialized,preAllocated); + + item = &viewItems_[last]; + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " after " << item->index.data().toString() << " total=" << item->total; +#endif + children+=item->total; + item->hasChildren = item->total > 0; + } + else + { + item->hasChildren = model_->hasChildren(currentIndex); + } + } + + if(!expanding) + return; // nothing changed + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " update parent total"; +#endif + + int pp=parentId; + while (pp > -1) + { + viewItems_[pp].total += count; + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " parent=" << viewItems_[pp].index.data().toString() << + " total=" << viewItems_[pp].total; +#endif + + pp = viewItems_[pp].parentItem; + } +} + +//Paint the rows intersecting with the given region +void CompactView::paint(QPainter *painter,const QRegion& region) +{ + //Even though the viewport palette is set correctly at the + //beginning something sets it to another value. Here we try + //to detect it and correct the palette with the right colour. + if(expectedBg_.isValid()) + { + QPalette p=viewport()->palette(); + if(p.color(QPalette::Window) != expectedBg_) + { + p.setColor(QPalette::Window,expectedBg_); + viewport()->setPalette(p); + viewport()->update(); + expectedBg_=QColor(); + return; + } + } + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "CompactView::paint -->"; + //UiLog().dbg() << "sizeof(TreeNodeViewItem)=" << sizeof(TreeNodeViewItem); + //UiLog().dbg() << "region=" << region; +#endif + + int firstVisibleOffset=0; + + //The first visible item at the top of the viewport + int firstVisible=firstVisibleItem(firstVisibleOffset); +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "firstVisible " << firstVisible; +#endif + + if(firstVisible<0) + return; + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "scrollX" << horizontalScrollBar()->value() << " " << viewport()->width(); +#endif + + int xOffset=0; + if(horizontalScrollBar()->value() > 0) + { + xOffset=horizontalScrollBar()->value(); + painter->translate(-xOffset,0); + } + + const int itemsCount = viewItems_.size(); + const int viewportWidth = viewport()->width(); + QVector rects = region.rects(); + QVector drawn; + bool multipleRects = (rects.size() > 1); + + //Iterate through the rectangles in the region + for(int a = 0; a < rects.size(); ++a) + { + const QRect area = (multipleRects + ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height()) + : rects.at(a)); +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " area=" << area; +#endif + + //Initialise indentVec. For each indentation level it tells us if + //a connector line is to be drawn. Here we scan up to the + //toplevel item in the firstVisible item's branch. + std::vector indentVec(1000,0); + if(firstVisible >0) + { + TreeNodeViewItem* item=&viewItems_[firstVisible]; + int level=item->level; + while(item->parentItem >= 0 && level >0) + { + TreeNodeViewItem* pt=&viewItems_[item->parentItem]; + if(item->hasMoreSiblings) + { + indentVec[item->level]=connectorPos(item,pt); + } + UI_ASSERT(pt->level == level-1, "item->parentItem=" << item->parentItem << + " pt->level=" << pt->level << " level=" << level); + item=pt; + level--; + } + } + + int i = firstVisible; // the first item at the top of the viewport + int y = firstVisibleOffset; // we may only see part of the first item + + //start at the top of the viewport and iterate down through the update area + int itemsInRow=1; + for (; i < itemsCount; i+=itemsInRow) + { + int itemHeight; + rowProperties(i,itemHeight,itemsInRow,indentVec); + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "row: " << i << " " << itemHeight << " " << itemsInRow; +#endif + //Try to find the first item int the current rect + if(y + itemHeight > area.top()) + break; + y += itemHeight; + } + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "y: " << y << " " << area.bottom(); +#endif + + //Paint the visible rows in the current rect + for (; i < itemsCount && y <= area.bottom(); i+=itemsInRow) + { + if(!multipleRects || !drawn.contains(i)) + { + //Draw a whole row. It will update y,itemsInRow and indentVec!! + drawRow(painter,i,xOffset,y,itemsInRow,indentVec); + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " row rendered - item=" << i << " y=" << y << " itemsInRow=" << itemsInRow; +#endif + } + else + { + int rh=rowHeight(i,1,itemsInRow); + y+=rh; +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " row skipped - item=" << i << " y=" << y << " itemsInRow=" << itemsInRow; +#endif + } + + if(multipleRects) + drawn.append(i); + } + } +} + +//Draw a whole row starting at item "start". +void CompactView::drawRow(QPainter* painter,int start,int xOffset,int& yp,int& itemsInRow,std::vector& indentVec) +{ + itemsInRow=0; + bool leaf=false; + const int itemsCount = static_cast(viewItems_.size()); + + //Get the rowheight + int iir=0; + int rh=rowHeight(start,1,iir); + + //See if there are no multiline items in this row + bool singleRow=delegate_->isSingleHeight(rh); + + int firstLevel=0; + const int viewportWidth = viewport()->width(); + + //We iterate through the items in the row + for(int i=start; i < itemsCount && !leaf; ++i ) + { + TreeNodeViewItem* item=&(viewItems_[i]); +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " item=" << i << " " << item->index.data().toString(); +#endif + leaf=(item->total == 0); + + //Find out the first indentation level in the row + if(firstLevel==0) + firstLevel=item->level; + + //Init style option + QStyleOptionViewItem opt; + if(selectionModel_->isSelected(item->index)) + opt.state |= QStyle::State_Selected; + + int optWidth=2000; + if(item->width > optWidth) + optWidth=item->width; + opt.rect=QRect(item->x,yp,optWidth,item->height); + + //We do not render the item if it is outisde the viewport and + //its parent's right is also outside the viewport. Here we considered that + //the connector line is always drawn from the child to the parent. + bool needToDraw=true; + if(item->parentItem >=0) + { + if(viewItems_[item->parentItem].right() >= translation() + viewportWidth) + needToDraw=false; + } + + if(needToDraw) + { + //For single rows we center items halfway through the rowHeight + if(singleRow) + { + if(item->height < rh) + { + opt.rect.moveTop(yp+(rh-item->height)/2); + } + } + + //QRect vr=visualRect(item->index); + //painter->fillRect(vr,QColor(120,120,120,120)); + +//#ifdef _UI_COMPACTVIEW_DEBUG +// UiLog().dbg() << " optRect=" << opt.rect << " visRect=" << vr; +//#endif + + //Draw the item with the delegate + QSize paintedSize; + delegate_->paint(painter,opt,item->index,paintedSize); + + //we have to know if the item width/height is the same that we expected. + //This can happen when: + // -we set a fixed initial width for the item (e.g. for an attribute) + // and now we got the real width + // -the number of icons or additional extra information + // changed for a node (so the width changed) + // -the number of lines changed in a multiline label (so the height changed) + bool wChanged=paintedSize.width() != item->width; + bool hChanged=paintedSize.height() != item->height; + bool wIncreased=paintedSize.width() > item->width; + + if(wChanged || hChanged) + { + //set new size + item->width=paintedSize.width(); + item->height=paintedSize.height(); + + if(item->right() > maxRowWidth_) + { + maxRowWidth_=item->right(); + doDelayedWidthAdjustment(); + } + else if(hChanged) + { + doDelayedWidthAdjustment(); + } + } + + //The width changed + if(wChanged) + { + bool sameAsWidest=(item->width == item->widestInSiblings); + item->width=paintedSize.width(); + + //servers + if(item->parentItem ==-1) + { + adjustWidthInParent(i); + doDelayedWidthAdjustment(); + } + //Nodes + else if(model_->isNode(item->index)) + { + //widestInSiblings has to be adjusted + if(sameAsWidest || paintedSize.width() > static_cast(item->widestInSiblings)) + { + adjustWidthInParent(i); + doDelayedWidthAdjustment(); + } + //we just need to update the item + else if( paintedSize.width() < static_cast(item->widestInSiblings)) + { + doDelayedWidthAdjustment(); + } + } + //Attributes + else + { + if(item->right() > maxRowWidth_) + { + maxRowWidth_=item->right(); + doDelayedWidthAdjustment(); + } + else if(wIncreased && !hChanged) + { + //we need to repaint the attribute + doDelayedWidthAdjustment(); + } + } + } + //the height changed (can only be a multiline label) + if(hChanged) + { + //set new size + item->height=paintedSize.height(); + doDelayedWidthAdjustment(); + } + + //QRect rr=opt.rect; + //rr.setWidth(item->width); + //painter->drawRect(rr); + + //UiLog().dbg() << i << " " << viewItems_[i]->index << " " << viewItems_[i]->index.data().toString() << " " + // << viewItems_[i]->x << " " << viewItems_[i]->height << " " << leaf; + + painter->setPen(connectorColour_); + + //If not a top level item (i.e. not a server) + if(item->parentItem >=0) + { + //The parent item. It is always a node. + TreeNodeViewItem* pt=&(viewItems_[item->parentItem]); + + //The horizontal line connecting the item to its parent + int lineX1=pt->right()+connectorGap_; + int lineX2=item->x-connectorGap_; + int lineX=(pt->right()+item->x)/2; + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " lineX=" << lineX << " " << item->x << " " << connectorPos(item,pt); +#endif + UI_ASSERT(lineX==connectorPos(item,pt),"lineX=" << lineX << " i=" << i << + " item->x=" << item->x << " connectorPos=" << connectorPos(item,pt)); + + //First child - in the same row as its parent + if(item->index.row() == 0) + { + int lineY=yp+pt->height/2; + + //horizontal line to the parent + painter->drawLine(lineX1,lineY,lineX2,lineY); + + //line towards the siblings - downwards + if(item->hasMoreSiblings) + { + //painter->drawLine(lineX,lineY,lineX,lineY+rh/2); + painter->drawLine(lineX,lineY,lineX,yp+rh); + indentVec[item->level]=lineX; + } + else + indentVec[item->level]=0; + } + //Child in the middle - has sibling both upwards and downwards + else if(item->hasMoreSiblings) + { + int lineY=yp+item->height/2; + + painter->drawLine(lineX,lineY,lineX2,lineY); + //painter->drawLine(lineX,lineY+rh/2,lineX,lineY-rh/2); + painter->drawLine(lineX,yp,lineX,yp+rh); + indentVec[item->level]=lineX; + } + + //The last child - has sibling only upwards + else + { + int lineY=yp+item->height/2; + painter->drawLine(lineX,lineY,lineX2,lineY); + //painter->drawLine(lineX,lineY,lineX,lineY-rh/2); + painter->drawLine(lineX,lineY,lineX,yp); + indentVec[item->level]=0; + } + } + + //indicate if a node is exandable + if(item->hasChildren && !item->expanded) + { + int lineY=yp+item->height/2; + int lineX=item->right()+connectorGap_; + QPen oriPen=painter->pen(); + painter->setPen(QPen(connectorColour_,1,Qt::DashLine)); + painter->drawLine(lineX,lineY,lineX+expandConnectorLenght_,lineY); + painter->setPen(oriPen); + } + } + + //When we reach a leaf item we move one row down. + if(leaf) + { + //Draw the vertical connector lines for all the levels + //preceding the first level in the row! + painter->setPen(connectorColour_); + for(int j=0; j < firstLevel; j++) + { + int xp=indentVec[j]; + if(xp != 0) + painter->drawLine(xp,yp,xp,yp+rh); + } + + yp+=rh; + rh=0; + firstLevel=0; + } + itemsInRow++; + } + + if(itemsInRow == 0) + itemsInRow=1; +} + +void CompactView::adjustWidthInParent(int start) +{ + //The parent index of the start item + int parentItem=viewItems_[start].parentItem; + + //The current max width in the start item's siblings + int prevWidest=viewItems_[start].widestInSiblings; + + //If the parent is not the root ie the start item is not a server + if(parentItem >=0) + { + int w=0, h=0, widest=0; + QModelIndex parentIndex=viewItems_[parentItem].index; + + //Determine the max width in the siblings of the start + //item, ie in the children of the parent item + int rowCount=model_->rowCount(parentIndex); + for(int i=0; i < rowCount; i++) + { + QModelIndex idx=model_->index(i,0,parentIndex); + if(model_->isNode(idx)) + { + delegate_->sizeHint(idx,w,h); + if(w >widest) widest=w; + } + } + + //If there is a new max width we need to adjust all the children of + //the parent item + int delta=widest-prevWidest; + if(delta != 0) + { + int n=parentItem+viewItems_[parentItem].total; + for(int i=parentItem+1; i <= n; i++) + { + //For a direct child of the parent item we just + //set the max width to its new value + if(viewItems_[i].parentItem == parentItem) + { + viewItems_[i].widestInSiblings = widest; + } + //The other items are shifted + else + { + viewItems_[i].x+=delta; + } + + //Check if the total width changed + if(viewItems_[i].right() > maxRowWidth_) + maxRowWidth_=viewItems_[i].right(); + } + } + } + + //If the parent is the root ie the start item is a server + else + { + //Determine the diff between the current and the previous width + int delta=viewItems_[start].width-prevWidest; + + //for server widestInSiblings is set to the width + viewItems_[start].widestInSiblings=viewItems_[start].width; + + //Shift all the children with the diff + if(delta != 0) + { + int n=start+viewItems_[start].total; + for(int i=start+1; i <= n; i++) + { + //shifted + viewItems_[i].x+=delta; + + //Check if the total width changed + if(viewItems_[i].right() > maxRowWidth_) + maxRowWidth_=viewItems_[i].right(); + } + } + } + +} + +int CompactView::connectorPos(TreeNodeViewItem* item, TreeNodeViewItem* parent) const +{ + return (parent->right()+item->x)/2; +} + + +//Get the rowheight. There are three kinds of row heights. +// 1. nodes (fixed height) +// 2. attributes (fixed height) +// 3. multiline label attributes (variable height!!!) +void CompactView::rowProperties(int start,int& rowHeight,int &itemsInRow,std::vector& indentVec) const +{ + rowHeight=0; + itemsInRow=0; + const int itemsCount = static_cast(viewItems_.size()); + + for(int i=start; i < itemsCount; i++) + { + TreeNodeViewItem* item=&(viewItems_[i]); + rowHeight=qMax(rowHeight,static_cast(item->height)); + itemsInRow++; + if(item->total == 0) + { + indentVec[item->level]=0; + break; + } + + if(item->parentItem >=0) + { + //The parent item. It is always a node. + TreeNodeViewItem* pt=&(viewItems_[item->parentItem]); + + if(item->hasMoreSiblings) + { + int lineX1=pt->right()+2; + int lineX2=item->x-2; + int lineX=(lineX1+lineX2)/2; + indentVec[item->level]=lineX; + } + else + { + indentVec[item->level]=0; + } + } + } + + UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); +} + +int CompactView::rowHeight(int start,int forward, int &itemsInRow) const +{ + uint rh=0; + itemsInRow=0; + const int itemsCount = static_cast(viewItems_.size()); + + if(forward == 1) + { + for(int i=start; i < itemsCount; i++) + { + rh=qMax(rh,viewItems_[i].height); + itemsInRow++; + if(viewItems_[i].total == 0) + break; + } + } + else + { + UI_ASSERT(start >= 0,"start=" << start << " total=" << viewItems_.size()); + UI_ASSERT(start < static_cast(viewItems_.size()),"start=" << start << " total=" << viewItems_.size()); + rh=qMax(rh,viewItems_[start].height); + itemsInRow++; + for(int i=start-1; i >= 0; i--) + { + if(viewItems_[i].total == 0) + break; + rh=qMax(rh,viewItems_[i].height); + itemsInRow++; + } + } + + UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); + return rh; +} + +int CompactView::itemCountInRow(int start) const +{ + const std::size_t itemsCount = viewItems_.size(); + int itemsInRow=0; + for(std::size_t i=start; i < itemsCount; i++) + { + itemsInRow++; + if(viewItems_[i].total == 0) + return itemsInRow; + } + + UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); + return itemsInRow; +} + +int CompactView::itemRow(int item) const +{ + if(item < 0 || item >= static_cast(viewItems_.size())) + return -1; + + int row=-1; + int itemsInRow=0; + for(int i=0; i <= item; i+=itemsInRow) + { + row++; + itemsInRow=itemCountInRow(i); + } + + return row; +} + +int CompactView::firstVisibleItem(int &offset) const +{ + const int value = verticalScrollBar()->value(); +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "CompactNodeView::firstVisibleItem --> value=" << value; +#endif + + if (verticalScrollMode_ == ScrollPerItem) + { + offset = 0; + //value is the row number + + if(value <0 || value >= rowCount_) + return -1; + + int cnt=0; + int itemsInRow=0; + const std::size_t itemsCount=viewItems_.size(); + for (std::size_t i=0; i < itemsCount; i+=itemsInRow) + { + if(cnt == value) + { +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " i=" << i << " itemsInRow=" << itemsInRow; +#endif + return i; + } + itemsInRow=itemCountInRow(i); + cnt++; + } + //return (value < 0 || value >= viewItems_.count()) ? -1 : value; + } + + return -1; +} + + +//This has to be very quick. Called after each collapse/expand. +void CompactView::updateRowCount() +{ + rowCount_=0; + const int itemsCount = static_cast(viewItems_.size()); + for(int i=0; i < itemsCount; i++) + { + if(viewItems_[i].total == 0) + rowCount_++; + } + +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "CompactNodeView::updateRowCount --> " << rowCount_; +#endif +} + +void CompactView::updateScrollBars() +{ +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << "CompactNodeView::updateScrollBars -->"; +#endif + + QSize viewportSize = viewport()->size(); + if(!viewportSize.isValid()) + viewportSize = QSize(0, 0); + + if(viewItems_.empty()) + { + //doItemsLayout(); + } + + int itemsInViewport = 0; + + const int itemsCount = viewItems_.size(); + if(itemsCount ==0) + return; + + const int viewportHeight = viewportSize.height(); + int itemsInRow=1; + for(int height = 0, item = itemsCount - 1; item >= 0; item-=itemsInRow) + { + //UiLog().dbg() << "item=" << item; + height +=rowHeight(item,-1,itemsInRow); + if(height > viewportHeight) + break; + itemsInViewport++; + } +#ifdef _UI_COMPACTVIEW_DEBUG + UiLog().dbg() << " itemsCount=" << itemsCount << " rowCount=" << rowCount_; + UiLog().dbg() << " itemsInViewport " << itemsInViewport; +#endif + + if(verticalScrollMode_ == ScrollPerItem) + { + if(!viewItems_.empty()) + itemsInViewport = qMax(1, itemsInViewport); + + //verticalScrollBar()->setRange(0, itemsCount - itemsInViewport); + verticalScrollBar()->setRange(0, rowCount_ - itemsInViewport); + verticalScrollBar()->setPageStep(itemsInViewport); + verticalScrollBar()->setSingleStep(1); + } + else + { + // scroll per pixel + } + + //Horizontal scrollbar + if(viewportSize.width() < maxRowWidth_) + { + horizontalScrollBar()->setRange(0,maxRowWidth_+10-viewportSize.width()); + horizontalScrollBar()->setPageStep(viewportSize.width()); + horizontalScrollBar()->setSingleStep(1); + } + else + { + horizontalScrollBar()->setRange(0,0); + } +} + +/* + Returns the rectangle on the viewport occupied by the item at \a index. + If the index is not visible or explicitly hidden, the returned rectangle is invalid. +*/ +QRect CompactView::visualRect(const QModelIndex &index) const +{ + //if (!d->isIndexValid(index) || isIndexHidden(index)) + // return QRect(); + + //d->executePostedLayout(); + + int vi = viewIndex(index); + if (vi < 0) + return QRect(); + + int y = -1; + int rh=0; + coordinateForItem(vi,y,rh); + if(y >=0) + { + //return QRect(viewItems_[vi].x, y, viewItems_[vi].width,rh); //TODO: optimise it + return QRect(viewItems_[vi].x-1-translation(), y, viewItems_[vi].width+2,rh); + } + return QRect(); +} + +//Returns the viewport y coordinate for item. +void CompactView::coordinateForItem(int item,int& itemY,int& itemRowHeight) const +{ + itemY=-1; + if(verticalScrollMode_ == ScrollPerItem) + { + int offset = 0; + //firstVisibleItem must always start a row!!!! + int topViewItemIndex=firstVisibleItem(offset); + if (item >= topViewItemIndex) + { + // search in the visible area first and continue down + // ### slow if the item is not visible + + const int itemsCount = viewItems_.size(); + const int viewportHeight = viewport()->size().height(); + int itemsInRow=1; + + for(int height = 0, viewItemIndex = topViewItemIndex; + height <= viewportHeight && viewItemIndex < itemsCount; viewItemIndex+=itemsInRow) + { + int h=rowHeight(viewItemIndex,1,itemsInRow); + if(viewItemIndex <=item && item < viewItemIndex+itemsInRow) + { + itemY=height; + itemRowHeight=h; + return; + } + height +=h; + } + } + } +} + +//coordinate is in viewport coordinates +int CompactView::itemAtCoordinate(const QPoint& coordinate) const +{ + const std::size_t itemCount = viewItems_.size(); + if(itemCount == 0) + return -1; + + if(verticalScrollMode_ == ScrollPerItem) + { + //int topRow = verticalScrollBar()->value(); + + int offset = 0; + int topViewItemIndex=firstVisibleItem(offset); + + if(coordinate.y() >= 0) + { + // the coordinate is in or below the viewport + int viewItemCoordinate = 0; + int itemsInRow=0; + for(std::size_t viewItemIndex = topViewItemIndex; viewItemIndex < itemCount; viewItemIndex+=itemsInRow) + { + viewItemCoordinate += rowHeight(viewItemIndex,1,itemsInRow); + if (viewItemCoordinate > coordinate.y()) + { + viewItemIndex=itemAtRowCoordinate(viewItemIndex,itemsInRow,coordinate.x()+translation()); + return (viewItemIndex >= itemCount ? -1 : viewItemIndex); + } + } + } + } + + return -1; +} + +//return the item index at the absolute x coordinate (i.e. not viewport x coordinate) +int CompactView::itemAtRowCoordinate(int start,int count,int logicalXPos) const +{ + for(int i=start; i < start+count; i++) + { + int left=viewItems_[i].x-1; + int right=viewItems_[i].right()+2; + if(!viewItems_[i].expanded && viewItems_[i].hasChildren) + right=viewItems_[i].right()+connectorGap_+expandConnectorLenght_+3; + + if(left <= logicalXPos && right >= logicalXPos) + { + return i; + } + } + return -1; +} + +void CompactView::updateViewport(const QRect rect) +{ + viewport()->update(rect); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CompactView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CompactView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CompactView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CompactView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,65 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef COMPACTVIEW_HPP +#define COMPACTVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AbstractNodeView.hpp" + +class TreeNodeModel; +class GraphNodeViewItem; +class QStyledItemDelegate; + +class CompactView : public AbstractNodeView +{ + +public: + explicit CompactView(TreeNodeModel* model,QWidget *parent=0); + ~CompactView(); + + QRect visualRect(const QModelIndex &index) const; + +protected: + void paint(QPainter *painter,const QRegion& region); + void drawRow(QPainter* painter,int start,int xOffset,int &yp,int &itemsInRow,std::vector&); + + void layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated); + + int itemRow(int item) const; + int itemCountInRow(int start) const; + void rowProperties(int start,int& rowHeight,int &itemsInRow,std::vector& indentVec) const; + int rowHeight(int start,int forward,int &itemsInRow) const; + void coordinateForItem(int item,int& itemY,int& itemRowHeight) const; + int itemAtCoordinate(const QPoint& coordinate) const; + int itemAtRowCoordinate(int start,int count,int xPos) const; + bool isPointInExpandIndicator(int,QPoint) const {return false;} + + int firstVisibleItem(int &offset) const; + void updateRowCount(); + void updateScrollBars(); + void updateViewport(const QRect rect); + + void adjustWidthInParent(int start); + +private: + int connectorPos(TreeNodeViewItem* item, TreeNodeViewItem* parent) const; +}; + +#endif // COMPACTVIEW_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ConfigListDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ConfigListDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ConfigListDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ConfigListDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,86 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ConfigListDelegate.hpp" + +#include +#include +#include + +//======================================================== +// +// VariableViewDelegate +// +//======================================================== + +ConfigListDelegate::ConfigListDelegate(int iconSize,int maxWidth,QWidget *parent) : + QStyledItemDelegate(parent), + iconSize_(iconSize), + maxTextWidth_(maxWidth), + margin_(2), + gap_(5) +{ +} + +void ConfigListDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ + QStyleOptionViewItem vopt(option); + initStyleOption(&vopt, index); + + QPixmap pix=index.data(Qt::DecorationRole).value(); + + //Save painter state + painter->save(); + + //The background rect + QRect bgRect=option.rect.adjusted(0,1,0,-1); + + //Paint selection. This should be transparent. + if(option.state & QStyle::State_Selected) + { + painter->fillRect(bgRect,QColor(200,222,250)); + } + + //pixmap + QRect pixRect(bgRect.center().x()-iconSize_/2,bgRect.top()+margin_,iconSize_,iconSize_); + painter->drawPixmap(pixRect,pix); + + //text + QString text=index.data(Qt::DisplayRole).toString(); + QFont f; + f.setBold(true); + QFontMetrics fm(f); + int textW=fm.width(text); + QRect textRect(bgRect.center().x()-textW/2,pixRect.bottom()+gap_,textW,fm.height()); + + painter->drawText(textRect,Qt::AlignHCenter| Qt::AlignVCenter,text); + + //Restore painter state + painter->restore(); + + +} + +QSize ConfigListDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QSize size=QStyledItemDelegate::sizeHint(option,index); + + int w=(maxTextWidth_ > iconSize_)?maxTextWidth_:iconSize_; + w+=4; + + QFont f; + QFontMetrics fm(f); + size=QSize(w,2*margin_+iconSize_+gap_+fm.height()+2); + + return size; +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ConfigListDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ConfigListDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ConfigListDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ConfigListDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,38 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ +#define VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ + +#include +#include +#include +#include + +#include "TreeView.hpp" + +#include + +class ConfigListDelegate : public QStyledItemDelegate +{ +public: + explicit ConfigListDelegate(int,int,QWidget *parent=0); + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + +protected: + int iconSize_; + int maxTextWidth_; + int margin_; + int gap_; +}; + +#endif /* VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ConnectState.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ConnectState.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ConnectState.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ConnectState.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,96 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ConnectState.hpp" + +#include + +static std::map descMap; + +ConnectState::ConnectState() : + state_(Undef), + lastConnect_(0), + lastFailed_(0), + lastDisconnect_(0) +{ + init(); +} + +void ConnectState::init() +{ + if(descMap.empty()) + { + descMap[Undef]=""; + descMap[Lost]="Connection to server lost"; + descMap[Disconnected]="Server is disconnected"; + descMap[Normal]="Server is connected"; + } +} + +const std::string& ConnectState::describe() const +{ + static std::string empty=""; + + std::map::const_iterator it=descMap.find(state_); + if(it != descMap.end()) + { + return it->second; + } + return empty; + +} +void ConnectState::state(State state) +{ + state_=state; + + switch(state_) + { + case Normal: + logConnect(); + break; + case Lost: + logFailed(); + break; + case Disconnected: + logDisconnect(); + break; + default: + break; + } +} + +void ConnectState::logConnect() +{ + lastConnect_=time(0); +} + +void ConnectState::logFailed() +{ + lastFailed_=time(0); +} + +void ConnectState::logDisconnect() +{ + lastDisconnect_=time(0); +} + +void ConnectState::errorMessage(const std::string& str) +{ + errMsg_=str; + + std::size_t pos = str.find("Client environment:"); + if(pos != std::string::npos) + { + shortErrMsg_=str.substr(0,pos); + } + else + { + shortErrMsg_=str; + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ConnectState.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ConnectState.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ConnectState.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ConnectState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,47 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef CONNECTSTATE_HPP_ +#define CONNECTSTATE_HPP_ + +#include +#include + +class ConnectState +{ +public: + ConnectState(); + + enum State {Undef,Normal,Disconnected,Lost}; + + void state(State state); + State state() const {return state_;} + const std::string& describe() const; + void errorMessage(const std::string&); + std::time_t lastConnectTime() const {return lastConnect_;} + std::time_t lastLostTime() const {return lastFailed_;} + std::time_t lastDisconnectTime() const {return lastDisconnect_;} + const std::string& errorMessage() const {return errMsg_;} + const std::string& shortErrorMessage() const {return shortErrMsg_;} + +protected: + static void init(); + void logConnect(); + void logFailed(); + void logDisconnect(); + + State state_; + std::time_t lastConnect_; + std::time_t lastFailed_; + std::time_t lastDisconnect_; + std::string errMsg_; + std::string shortErrMsg_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,20 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "CustomCommandDialog.hpp" + + +CustomCommandDialog::CustomCommandDialog(QWidget *parent) +{ + setupUi(this); + + // when the user clicks the 'Run' button, we close the dialog with ACCEPT + connect(commandDesigner_->runButton(), SIGNAL(clicked()), this, SLOT(accept())); + +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,36 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + + +#ifndef CUSTOMCOMMANDDIALOG_HPP_ +#define CUSTOMCOMMANDDIALOG_HPP_ + +#include + +#include "ui_CustomCommandDialog.h" + +class CustomCommandDialog : public QDialog, private Ui::CustomCommandDialog +{ + Q_OBJECT + +public: + explicit CustomCommandDialog(QWidget *parent = 0); + ~CustomCommandDialog() {}; + + MenuItem &menuItem() {return commandDesigner_->menuItem();}; + void setNodes(std::vector &nodes) {commandDesigner_->setNodes(nodes);}; + std::vector &selectedNodes() {return commandDesigner_->selectedNodes();}; + + + +//public Q_SLOTS: +// void insertCurrentText(); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,80 @@ + + + CustomCommandDialog + + + + 0 + 0 + 985 + 512 + + + + + 369 + 0 + + + + User Command Editor + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + CommandDesignerWidget + QLineEdit +
      CommandDesignerWidget.hpp
      +
      +
      + + + + buttonBox_ + rejected() + CustomCommandDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + buttonBox_ + accepted() + CustomCommandDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,320 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include + +#include "CustomCommandHandler.hpp" +#include "SessionHandler.hpp" + +#include "DirectoryHandler.hpp" +#include "File.hpp" +#include "VSettings.hpp" + + +CustomCommand::CustomCommand(const std::string &name, const std::string &command, bool context) : + name_(name), command_(command), inContextMenu_(context) +{ +} + + +void CustomCommand::set(const std::string &name, const std::string &command, bool context) +{ + name_ = name; + command_ = command; + inContextMenu_ = context; +} + + +void CustomCommand::save(VSettings *vs) const +{ + vs->put("name", name()); + vs->put("command", command()); + vs->put("context", contextString()); +} + +CustomCommandHandler::CustomCommandHandler() +{ +} + +void CustomCommandHandler::init() +{ + readSettings(); +} + +CustomCommand* CustomCommandHandler::replace(int index, const std::string& name, const std::string& command, bool context) +{ + assert(index >= 0); + assert(index < static_cast(items_.size())); + + CustomCommand *item; + + // already in the list - just update it + item = items_[index]; + item->set(name, command, context); + + writeSettings(); + + return item; +} + +CustomCommand* CustomCommandHandler::replace(int index, const CustomCommand &cmd) +{ + return replace(index, cmd.name(), cmd.command(), cmd.inContextMenu()); +} + + + +void CustomCommandHandler::remove(int index) +{ + assert(index >= 0); + assert(index < static_cast(items_.size())); + + items_.erase(items_.begin()+index); + + writeSettings(); +} + +CustomCommand* CustomCommandHandler::duplicate(int index) +{ + assert(index >= 0); + assert(index < static_cast(items_.size())); + + CustomCommand *item = items_[index]; + std::string postfix("_1"); + std::string newName = item->name() + postfix; + + // ensure we are creating a unique new name - if we find an existing item with the same name, add another postfix + while(find(newName) != NULL) + newName += postfix; + + CustomCommand*newCmd = add(newName, item->command(), item->inContextMenu(), false); + + writeSettings(); + + return newCmd; +} + +void CustomCommandHandler::swapCommandsByIndex(int i1, int i2) +{ + assert(i1 >= 0); + assert(i1 < static_cast(items_.size())); + assert(i2 >= 0); + assert(i2 < static_cast(items_.size())); + + CustomCommand *temp = items_[i2]; + items_[i2] = items_[i1]; + items_[i1] = temp; +} + + +CustomCommand* CustomCommandHandler::find(const std::string& name) const +{ + for(std::deque::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name) + return *it; + } + return NULL; +} + + +// find the index of the command which has the given name; -1 if not found +int CustomCommandHandler::findIndexFromName(const std::string& name) const +{ + int i = 0; + for(std::deque::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name) + return i; + i++; + } + return -1; // it was not found +} + + +void CustomCommandHandler::writeSettings() +{ + std::vector vsItems; + std::string dummyFileName="dummy"; + std::string key="commands"; + + std::string settingsFilePath = settingsFile(); + VSettings vs(settingsFilePath); + + for(int i = 0; i < numCommands(); i++) + { + VSettings vsThisItem(dummyFileName); + CustomCommand *cmd = commandFromIndex(i); + cmd->save(&vsThisItem); + vsItems.push_back(vsThisItem); + } + vs.put(key,vsItems); + vs.write(); +} + +void CustomCommandHandler::readSettings() +{ + std::string settingsFilePath = settingsFile(); + VSettings vs(settingsFilePath); + + bool ok = vs.read(false); // false means we don't abort if the file is not there + + if(ok) + { + std::vector commands; + vs.get("commands", commands); + + for (std::size_t i = 0; i < commands.size(); i++) + { + VSettings *vsCommand = &commands[i]; + std::string emptyDefault=""; + std::string name = vsCommand->get("name", emptyDefault); + std::string command = vsCommand->get("command", emptyDefault); + std::string context = vsCommand->get("context", emptyDefault); + add(name, command, stringToBool(context), false); // add it to our in-memory list + } + } +} + +bool CustomCommandHandler::stringToBool(std::string &str) +{ + bool result = (!str.empty() && str == "yes"); + return result; +} + + +// ------------------------- +// CustomSavedCommandHandler +// ------------------------- + +CustomSavedCommandHandler* CustomSavedCommandHandler::instance_=0; + + +CustomSavedCommandHandler* CustomSavedCommandHandler::instance() +{ + if(!instance_) + { + instance_=new CustomSavedCommandHandler(); + } + + return instance_; +} + +CustomCommand* CustomSavedCommandHandler::add(const std::string& name, const std::string& command, bool context, bool saveSettings) +{ + CustomCommand *item=new CustomCommand(name, command, context); + items_.push_back(item); + + if (saveSettings) + writeSettings(); + + return item; +} + +std::string CustomSavedCommandHandler::settingsFile() +{ + SessionItem* cs=SessionHandler::instance()->current(); + return cs->savedCustomCommandsFile(); +} + + +// --------------------------- +// CustomCommandHistoryHandler +// --------------------------- + + +CustomCommandHistoryHandler* CustomCommandHistoryHandler::instance_=0; + +CustomCommandHistoryHandler::CustomCommandHistoryHandler() +{ + maxCommands_ = 10; +} + + +CustomCommandHistoryHandler* CustomCommandHistoryHandler::instance() +{ + if(!instance_) + { + instance_=new CustomCommandHistoryHandler(); + } + + return instance_; +} + + +CustomCommand* CustomCommandHistoryHandler::add(const std::string& name, const std::string& command, bool context, bool saveSettings) +{ + int index = findIndexFromName(name); + + if (index == -1) // not already in the list + { + CustomCommand *item=new CustomCommand(name, command, context); + items_.push_front(item); // add it to the front + + if(static_cast(items_.size()) > maxCommands_) // too many commands? + { + items_.pop_back(); // remove the last item + } + + if (saveSettings) + writeSettings(); + + return item; + } + else + { + return commandFromIndex(index); + } + +} + +std::string CustomCommandHistoryHandler::settingsFile() +{ + SessionItem* cs=SessionHandler::instance()->current(); + return cs->recentCustomCommandsFile(); +} + + +/* +void NodeQueryHandler::add(NodeQuery* item,bool saveToFile) +{ + items_.push_back(item); + if(saveToFile) + save(item); +} + + +void NodeQueryHandler::remove(const std::string&) +{ +} + +void NodeQueryHandler::remove(NodeQuery*) +{ + +} + + +void NodeQueryHandler::save() +{ +} + +void NodeQueryHandler::save(NodeQuery *item) +{ + std::string f=DirectoryHandler::concatenate(dirPath_,item->name() + "." + suffix_); + VSettings vs(f); + item->save(&vs); + vs.write(); + +} + +*/ + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomCommandHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomCommandHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ +#define VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ + +#include +#include + + +class VSettings; + +class CustomCommand +{ +public: + CustomCommand(const std::string &name, const std::string &command, bool context); + const std::string& name() const {return name_;} + const std::string& command() const {return command_;} + bool inContextMenu() const {return inContextMenu_;} + std::string contextString() const {return (inContextMenu_ ? "yes" : "no");} + void set(const std::string &name, const std::string &command, bool context); + void save(VSettings *vs) const; + + +private: + std::string name_; + std::string command_; + bool inContextMenu_; +}; + + +class CustomCommandHandler +{ +public: + CustomCommandHandler(); + + virtual CustomCommand* add(const std::string& name, const std::string& command, bool context, bool WriteSettings) = 0; + CustomCommand* replace(int index, const std::string& name, const std::string& command, bool context); + CustomCommand* replace(int index, const CustomCommand &cmd); + CustomCommand* duplicate(int index); + void remove(int index); + //void remove(const std::string& name); + //void remove(CustomCommand*); + //CustomCommand* find(const std::string& name) const; + + //void save(); + //void save(CustomCommand*); + void init(); + //const std::vector& items() const {return items_;} + CustomCommand* find(const std::string& name) const; + int findIndexFromName(const std::string& name) const; + int numCommands() const {return items_.size();} + CustomCommand *commandFromIndex(int i) {return items_[i];}; + bool stringToBool(std::string &str); + void swapCommandsByIndex(int i1, int i2); + void writeSettings(); + +protected: + void readSettings(); + virtual std::string settingsFile() = 0; + + const std::string suffix_; + std::deque items_; +}; + + + +// ---------------------------------------------------------------------------------------------- +// specialisation of CustomCommandHandler to handle the commands that the user has manually saved +// ---------------------------------------------------------------------------------------------- + +class CustomSavedCommandHandler : public CustomCommandHandler +{ +public: + CustomSavedCommandHandler() {}; + CustomCommand* add(const std::string& name, const std::string& command, bool context, bool saveSettings); + + static CustomSavedCommandHandler* instance(); + +protected: + static CustomSavedCommandHandler* instance_; + std::string settingsFile(); +}; + + + +// -------------------------------------------------------------------------------------------- +// specialisation of CustomCommandHandler to handle the commands that the user has recently run +// -------------------------------------------------------------------------------------------- + +class CustomCommandHistoryHandler : public CustomCommandHandler +{ +public: + CustomCommandHistoryHandler(); + CustomCommand* add(const std::string& name, const std::string& command, bool context, bool saveSettings); + static CustomCommandHistoryHandler* instance(); + +protected: + static CustomCommandHistoryHandler* instance_; + std::string settingsFile(); + int maxCommands_; +}; + + + + +#endif /* VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomListWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomListWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomListWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomListWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,100 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CustomListWidget.hpp" + +#include +#include + +CustomListWidget::CustomListWidget(QWidget* parent) : QListWidget(parent) +{ + setAlternatingRowColors(true); + + connect(this,SIGNAL(itemChanged(QListWidgetItem*)), + this,SLOT(slotItemChanged(QListWidgetItem*))); +} + +void CustomListWidget::addItems(QStringList lst,bool checkState) +{ + QListWidget::addItems(lst); + for(int i=0; i < count(); i++) + { + item(i)->setCheckState((checkState)?Qt::Checked:Qt::Unchecked); + } +} + +void CustomListWidget::addItems(QStringList lst,bool checkState,QList colLst) +{ + addItems(lst,checkState); + + for(int i=0; i < count() && i < colLst.count(); i++) + { + QColor col=colLst[i]; + if(col.isValid()) + { + QPixmap pix(10,10); + QPainter painter(&pix); + pix.fill(col); + painter.setPen(Qt::black); + painter.drawRect(0,0,9,9); + item(i)->setIcon(pix); + } + } +} + +void CustomListWidget::slotItemChanged(QListWidgetItem*) +{ + Q_EMIT selectionChanged(); +} + +bool CustomListWidget::hasSelection() const +{ + for(int i=0; i < count(); i++) + { + if(item(i)->checkState() == Qt::Checked) + return true; + } + + return false; +} + + +QStringList CustomListWidget::selection() const +{ + QStringList lst; + for(int i=0; i < count(); i++) + { + if(item(i)->checkState() == Qt::Checked) + lst << item(i)->text(); + } + + return lst; +} + + +void CustomListWidget::clearSelection() +{ + for(int i=0; i < count(); i++) + { + item(i)->setCheckState(Qt::Unchecked); + } + Q_EMIT selectionChanged(); +} + +void CustomListWidget::setSelection(QStringList sel) +{ + for(int i=0; i < count(); i++) + { + item(i)->setCheckState(Qt::Unchecked); + if(sel.contains(item(i)->text())) + item(i)->setCheckState(Qt::Checked); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomListWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomListWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomListWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomListWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ +#define VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ + +#include + +class CustomListWidget : public QListWidget +{ +Q_OBJECT + +public: + explicit CustomListWidget(QWidget* parent=0); + + void addItems(QStringList lst,bool checkState); + void addItems(QStringList lst,bool checkState,QList); + QStringList selection() const; + bool hasSelection() const; + void setSelection(QStringList sel); + +public Q_SLOTS: + void clearSelection(); + +protected Q_SLOTS: + void slotItemChanged(QListWidgetItem*); + +Q_SIGNALS: + void selectionChanged(); + + +}; + + +#endif /* VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomTabWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomTabWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomTabWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomTabWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,57 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "CustomTabWidget.hpp" + +#include + + +CustomTabWidget::CustomTabWidget(QWidget* parent) : QTabWidget(parent) +{ + setProperty("change","1"); +} + +void CustomTabWidget::setCustomIcon(int index, QPixmap pix) +{ + if (index >= 0 && index < count()) + { + QSize maxSize=maxIconSize(); + + if(maxSize.width() < pix.width()) + maxSize.setWidth(pix.width()); + + if(maxSize.height() < pix.height()) + maxSize.setHeight(pix.height()); + + if(maxSize != iconSize()) + setIconSize(maxSize); + + setTabIcon(index, QIcon(pix)); + } +} + +QSize CustomTabWidget::maxIconSize() const +{ + QSize maxSize(0,0); + for(int i=0; i < count(); i++) + { + if(tabIcon(i).availableSizes().count() > 0) + { + QSize avs=tabIcon(i).availableSizes().front(); + if(maxSize.width() < avs.width()) + maxSize.setWidth(avs.width()); + + if(maxSize.height() < avs.height()) + maxSize.setHeight(avs.height()); + } + } + return maxSize; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/CustomTabWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/CustomTabWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/CustomTabWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/CustomTabWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,28 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_CUSTOMTABWIDGET_HPP_ +#define VIEWER_SRC_CUSTOMTABWIDGET_HPP_ + +#include + +class CustomTabWidget : public QTabWidget +{ +public: + explicit CustomTabWidget(QWidget* parent=0); + + void setCustomIcon(int index, QPixmap pix); + +protected: + QSize maxIconSize() const; +}; + + +#endif /* VIEWER_SRC_CUSTOMTABWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Dashboard.cpp ecflow-4.11.1/Viewer/ecflowUI/src/Dashboard.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Dashboard.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Dashboard.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,696 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "Dashboard.hpp" + +#include "DashboardDialog.hpp" +#include "DashboardDock.hpp" +#include "DashboardTitle.hpp" +#include "InfoPanel.hpp" +#include "NodeWidget.hpp" +#include "ServerHandler.hpp" +#include "ServerFilter.hpp" +#include "TableNodeWidget.hpp" +#include "TreeNodeWidget.hpp" +#include "UiLog.hpp" +#include "VFilter.hpp" +#include "VConfig.hpp" +#include "VSettings.hpp" +#include "WidgetNameProvider.hpp" + +#include +#include +#include +#include +#include +#include +#include "NodeSearchDialog.hpp" +#include "NodeSearchWidget.hpp" + +int Dashboard::maxWidgetNum_=20; + +Dashboard::Dashboard(QString rootNode,QWidget *parent) : + QMainWindow(parent), + settingsAreRead_(false) +{ + //We use the mainwindow as a widget. Its task is + //to dock all the component widgets! + setWindowFlags(Qt::Widget); + + setObjectName("db"); + + //The serverfilter. It holds the list of servers displayed by this dashboard. + serverFilter_=new ServerFilter(); + serverFilter_->addObserver(this); + + titleHandler_=new DashboardTitle(serverFilter_,this); + + //Central widget - we need to create it but we do not + //use it. So we can hide it! + QWidget *w=new QLabel("centre",this); + w->setObjectName("dbc"); + setCentralWidget(w); + w->hide(); + + layout()->setContentsMargins(0,0,0,0); + + setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AllowNestedDocks); +} + +Dashboard::~Dashboard() +{ + widgets_.clear(); + popupWidgets_.clear(); + + Q_EMIT aboutToDelete(); + + serverFilter_->removeObserver(this); + delete serverFilter_; +} + + +DashboardWidget* Dashboard::addWidgetCore(const std::string& type,bool userAddedView) +{ + DashboardWidget *w=0; + + //Create a dashboard widget + if(type == "tree") + { + NodeWidget* ctl=new TreeNodeWidget(serverFilter_,this); + + connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), + this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); + + connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SLOT(slotCommand(VInfo_ptr,QString))); + + w=ctl; + } + else if(type == "table") + { + NodeWidget* ctl=new TableNodeWidget(serverFilter_,userAddedView,this); + + connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), + this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); + + connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SLOT(slotCommand(VInfo_ptr,QString))); + + w=ctl; + } + else if(type == "info") + { + InfoPanel* ctl=new InfoPanel(this); + + connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), + this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); + + connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SLOT(slotCommand(VInfo_ptr,QString))); + + w=ctl; + } + + if(w) + { + connect(w,SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChanged(VInfo_ptr))); + + connect(w,SIGNAL(maximisedChanged(DashboardWidget*)), + this,SLOT(slotMaximisedChanged(DashboardWidget*))); + } + + return w; +} + + +void Dashboard::slotSelectionChanged(VInfo_ptr info) +{ + DashboardWidget* s=static_cast(sender()); + + Q_FOREACH(DashboardWidget* dw,widgets_) + { + if(dw != s) + dw->setCurrentSelection(info); + } + Q_FOREACH(DashboardWidget* dw,popupWidgets_) + { + if(dw != s) + dw->setCurrentSelection(info); + } + + Q_EMIT selectionChanged(info); +} + + +DashboardWidget* Dashboard::addWidget(const std::string& type) +{ + //Get a unique dockId stored as objectName + QString dockId=uniqueDockId(); + + if(hasMaximised()) + { + resetMaximised(); + } + + bool popupTableFilter=false; + if(VProperty* prop=VConfig::instance()->find("view.table.popupFilter")) + { + popupTableFilter=prop->value().toBool(); + } + + DashboardWidget* w=addWidget(type,dockId.toStdString(),popupTableFilter); + + //At this point the widgets can be inactive. Reload will make them active!!! + w->reload(); + + if(type == "info") + { + VInfo_ptr info=currentSelectionInView(); + if(info && info.get()) + { + if(InfoPanel* ip=static_cast(w)) + { + ip->slotReload(info); + } + } + } + + return w; +} + +DashboardWidget* Dashboard::addWidget(const std::string& type,const std::string& dockId,bool userAddedView) +{ + DashboardWidget *w=Dashboard::addWidgetCore(type,userAddedView); + + //If the db-widget creation fails we should do something!!! + if(!w) + return 0; + + //Store dockId in the db-widget + w->id(dockId); + + widgets_ << w; + + //Create a dockwidget + DashboardDock *dw = new DashboardDock(w, this); + + dw->setAllowedAreas(Qt::RightDockWidgetArea); + + //Store the dockId in the dockwidget (as objectName) + dw->setObjectName(QString::fromStdString(dockId)); + + //Add the dockwidget to the dashboard + addDockWidget(Qt::RightDockWidgetArea, dw); + + connect(dw,SIGNAL(closeRequested()), + this,SLOT(slotDockClose())); + + checkMaximisedState(); + + return w; +} + +DashboardWidget* Dashboard::addDialog(const std::string& type) +{ + DashboardWidget *w=Dashboard::addWidgetCore(type,false); + + //If the db-widget creation fails we should do something!!! + if(!w) + return 0; + + //The DashBoard or any of its children cannot be the parent of the + //dialog because in this case it would be always on top its parent. This is + //the behaviour when the dialog's parent is QMainWindow. + DashboardDialog* dia=new DashboardDialog(0); + + //So the parent is 0 and we will emit a signal from the Dashboard + //destructor to notify the dialog about the deletion. Then we can be + //sure that the dialog deletes itself when the Dashboard gets deleted. + connect(this,SIGNAL(aboutToDelete()), + dia,SLOT(slotOwnerDelete())); + + connect(dia,SIGNAL(finished(int)), + this,SLOT(slotDialogFinished(int))); + + connect(dia,SIGNAL(aboutToClose()), + this,SLOT(slotDialogClosed())); + + //The dialog will reparent the widget + dia->add(w); + dia->show(); + + return w; +} + +void Dashboard::addSearchDialog() +{ + //It will delete itself on close!! + //The parent is 0, for the reason see the comment in addDialog() + NodeSearchDialog* d=new NodeSearchDialog(0); + d->queryWidget()->setServerFilter(serverFilter_); + + connect(d->queryWidget(),SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChanged(VInfo_ptr))); + + connect(d->queryWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); + + //The dashboard signals the dialog on deletion + connect(this,SIGNAL(aboutToDelete()), + d,SLOT(slotOwnerDelete())); + + d->show(); +} + +void Dashboard::addSearchDialog(VInfo_ptr info) +{ + //It will delete itself on close!! + //The parent is 0, for the reason see the comment in addDialog() + NodeSearchDialog* d=new NodeSearchDialog(0); + d->queryWidget()->setServerFilter(serverFilter_); + d->queryWidget()->setRootNode(info); + + connect(d->queryWidget(),SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChanged(VInfo_ptr))); + + connect(d->queryWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); + + //The dashboard signals the dialog on deletion + connect(this,SIGNAL(aboutToDelete()), + d,SLOT(slotOwnerDelete())); + + d->show(); +} + +void Dashboard::slotDockClose() +{ + if(DashboardDock *dock=static_cast(sender())) + { + if(DashboardWidget* dw=static_cast(dock->widget())) + { + widgets_.removeOne(dw); + disconnect(this,0,dw,0); + + if(dw->isMaximised()) + resetMaximised(); + + checkMaximisedState(); + + dw->deleteLater(); + } + dock->deleteLater(); + } +} + +VInfo_ptr Dashboard::currentSelection() +{ + return currentSelectionInView(); +} + +void Dashboard::currentSelection(VInfo_ptr n) +{ + //if(NodeWidget *ctl=handler_->currentControl()) + // ctl->currentSelection(n); +} + +void Dashboard::slotPopInfoPanel(QString name) +{ + if(DashboardWidget *dw=addDialog("info")) + { + if(InfoPanel* ip=static_cast(dw)) + { + ip->setDetached(true); + ip->setCurrent(name.toStdString()); + } + + popupWidgets_ << dw; + } +} + +void Dashboard::slotPopInfoPanel(VInfo_ptr info,QString name) +{ + if(DashboardWidget *dw=addDialog("info")) + { + if(InfoPanel* ip=static_cast(dw)) + { + //ip->setDetached(true); + ip->slotReload(info); + ip->setCurrent(name.toStdString()); + } + + popupWidgets_ << dw; + } +} + +void Dashboard::slotCommand(VInfo_ptr info,QString cmd) +{ + if(!info || !info.get() ) + return; + + if(cmd == "search") + { + addSearchDialog(info); + } +} + +//------------------------------- +// Maximise panel +//------------------------------- + +void Dashboard::slotMaximisedChanged(DashboardWidget* w) +{ + QList dLst=findChildren(QString()); + + //maximise the given panel + if(w->isMaximised()) + { + bool hasMaxApp=hasMaximisedApplied(); + //nothing can be maximised in practice at this point + Q_ASSERT(hasMaxApp == false); + savedDockState_=saveState(); + + Q_FOREACH(DashboardDock* d,dLst) + { + if(d->widget() == w) + d->setVisible(true); + else + { + DashboardWidget* dw=static_cast(d->widget()); + Q_ASSERT(dw); + dw->resetMaximised(); + d->setVisible(false); + } + } + } + //restore the previous state + else + { + resetMaximised(); + } +} + +bool Dashboard::hasMaximised() const +{ + for(int i=0; i < widgets_.count(); i++) + { + if(widgets_[i]->isMaximised()) + return true; + } + return false; +} + +bool Dashboard::hasMaximisedApplied() const +{ + QList dLst=findChildren(QString()); + Q_FOREACH(DashboardDock* d,dLst) + { + if(d->isVisible() == false) + return true; + } + return false; +} + +void Dashboard::resetMaximised() +{ + QList dLst=findChildren(QString()); + Q_FOREACH(DashboardDock* d,dLst) + { + DashboardWidget* dw=static_cast(d->widget()); + Q_ASSERT(dw); + dw->resetMaximised(); + d->setVisible(true); + } + + if(!savedDockState_.isEmpty()) + { + restoreState(savedDockState_); + savedDockState_.clear(); + } +} + +void Dashboard::checkMaximisedState() +{ + bool st=(widgets_.count() > 1); + for(int i=0; i < widgets_.count(); i++) + widgets_.at(i)->setEnableMaximised(st); +} + +//------------------------ +// Dialogs +//------------------------ + +void Dashboard::slotDialogFinished(int) +{ + if(DashboardDialog* dia=static_cast(sender())) + { + disconnect(this,0,dia->dashboardWidget(),0); + popupWidgets_.removeOne(dia->dashboardWidget()); + } +} + +void Dashboard::slotDialogClosed() +{ + if(DashboardDialog* dia=static_cast(sender())) + { + disconnect(this,0,dia->dashboardWidget(),0); + popupWidgets_.removeOne(dia->dashboardWidget()); + } +} + +//------------------------ +// Rescan +//------------------------ + +void Dashboard::reload() +{ + for(int i=0; i < widgets_.count(); i++) + widgets_.at(i)->reload(); +} + +void Dashboard::rerender() +{ + for(int i=0; i < widgets_.count(); i++) + widgets_.at(i)->rerender(); + + titleHandler_->updateTitle(); +} + +//------------------------ +// ViewMode +//------------------------ + +Viewer::ViewMode Dashboard::viewMode() +{ + //return handler_->currentMode(); + return Viewer::TreeViewMode; +} + +void Dashboard::setViewMode(Viewer::ViewMode mode) +{ + //handler_->setCurrentMode(mode); +} + +//---------------------------------- +// Save and load settings!! +//---------------------------------- + +void Dashboard::writeSettings(VComboSettings* vs) +{ + serverFilter_->writeSettings(vs); + + //Qt settings + if(savedDockState_.isEmpty() == false) + { + vs->putQs("state",savedDockState_); + } + else + { + vs->putQs("state",saveState()); + } + + //Other setting + vs->put("widgetCount",findChildren().count()); + + for(int i=0; i < widgets_.count(); i++) + { + std::string id=Dashboard::widgetSettingsId(i); + vs->beginGroup(id); + widgets_.at(i)->writeSettings(vs); + vs->endGroup(); + } +} + +void Dashboard::readSettings(VComboSettings* vs) +{ + settingsAreRead_=true; + + //This will create the ServerHandler objects + serverFilter_->readSettings(vs); + + //At this point each ServerHandler is running its reset()! + + Q_FOREACH(QWidget* w,findChildren()) + { + UiLog().dbg() << "DashBoard::readSettings() dock: " << w->objectName(); + } + + //Read the information about the dashboard widgets. + int cnt=vs->get("widgetCount",0); + for(int i=0; i < cnt; i++) + { + std::string id=Dashboard::widgetSettingsId(i); + if(vs->contains(id)) + { + vs->beginGroup(id); + std::string type=vs->get("type",""); + std::string dockId=vs->get("dockId",""); + + //Create a dashboard widget + if(DashboardWidget *dw=addWidget(type,dockId,false)) + { + //This will make the widgets active!!! The ServerHandler reset() can + //be still running at this point! + dw->readSettings(vs); + } + vs->endGroup(); + } + } + + //Restore state of the dockwidgets. It will not create the dockwidgets + //themselves but set their states and geometry. This is based on + //the the dockwidgets's objectname, so that has to be unique. We need to call + //it when the dockwidgets have already been created. + if(vs->containsQs("state")) + { + restoreState(vs->getQs("state").toByteArray()); + } + + initialSelectionInView(); + + settingsAreRead_=false; +} + +#if 0 +void Dashboard::slotInfoPanelSelection(VInfo_ptr info) +{ + selectInTreeView(info); +} +#endif + + +void Dashboard::initialSelectionInView() +{ + Q_FOREACH(DashboardWidget* w,widgets_) + { + if(w->initialSelectionInView()) + { + return; + } + } +} + +bool Dashboard::selectInTreeView(VInfo_ptr info) +{ + if(!info) + return false; + + Q_FOREACH(DashboardWidget* w,widgets_) + { + if(w->type() == "tree") + { + w->setCurrentSelection(info); + return serverFilter_->isFiltered(info->server()); + } + } + + return false; +} + +VInfo_ptr Dashboard::currentSelectionInView() +{ + Q_FOREACH(DashboardWidget* w,widgets_) + { + VInfo_ptr info=w->currentSelection(); + if(info && info.get()) + return info; + } + + return VInfo_ptr(); +} + +//Find an unique id for a new dockwidget +QString Dashboard::uniqueDockId() +{ + QSet ids; + Q_FOREACH(QDockWidget* dw, findChildren()) + { + ids << dw->objectName(); + } + + for(unsigned i=0; i < 1000; i++) + { + QString uid="dock_" + QString::number(i); + if(!ids.contains(uid)) + { + return uid; + } + } + + //We should handle this situation!! + assert(0); + + return "dock_1000"; +} + +std::string Dashboard::widgetSettingsId(int i) +{ + return "widget_" + boost::lexical_cast(i); +} + +void Dashboard::notifyServerFilterAdded(ServerItem* item) +{ + if(!settingsAreRead_) + { + //If there are no views we automatically add a tree view + if(widgets_.count() == 0) + addWidget("tree"); + + Q_EMIT contentsChanged(); + } +} + +void Dashboard::notifyServerFilterRemoved(ServerItem* item) +{ + if(!settingsAreRead_) + Q_EMIT contentsChanged(); +} + +void Dashboard::notifyServerFilterChanged(ServerItem*) +{ + if(!settingsAreRead_) + Q_EMIT contentsChanged(); +} + +void Dashboard::notifyServerFilterDelete() +{ + //if(!settingsAreRead_) + // Q_EMIT contentsChanged(); +} + +//We need to do this in order to prevent the dock window context menu from popping up. +//See ECFLOW-894 +void Dashboard::contextMenuEvent(QContextMenuEvent * e) +{ + e->accept(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,125 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "DashboardDialog.hpp" + +#include "DashboardWidget.hpp" +#include "SessionHandler.hpp" +#include "WidgetNameProvider.hpp" + +#include +#include +#include +#include + +DashboardDialog::DashboardDialog(QWidget *parent) : + QDialog(parent), + dw_(0) +{ + setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose); + + //Disable the default button + Q_FOREACH(QAbstractButton* b,buttonBox_->buttons()) + { + if(QPushButton* pb=buttonBox_->button(buttonBox_->standardButton(b)) ) + pb->setAutoDefault(false); + } + + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +DashboardDialog::~DashboardDialog() +{ + //dw_->deleteLater(); +} + +void DashboardDialog::add(DashboardWidget* dw) +{ + dw_=dw; + + //The dialog takes ownership of the widget + dw_->setParent(this); + + layout_->insertWidget(0,dw_,1); + + connect(dw_,SIGNAL(titleUpdated(QString,QString)), + this,SLOT(slotUpdateTitle(QString,QString))); + + dw_->populateDialog(); + dw_->readSettingsForDialog(); +} + +void DashboardDialog::reject() +{ + if(dw_) + dw_->writeSettingsForDialog(); + + writeSettings(); + QDialog::reject(); +} + +void DashboardDialog::closeEvent(QCloseEvent * event) +{ + if(dw_) + dw_->writeSettingsForDialog(); + + Q_EMIT aboutToClose(); + event->accept(); + writeSettings(); +} + +void DashboardDialog::slotUpdateTitle(QString txt,QString /*type*/) +{ + setWindowTitle(txt.remove("").remove("")); +} + +void DashboardDialog::slotOwnerDelete() +{ + this->deleteLater(); +} + +void DashboardDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("DashboardDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void DashboardDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("DashboardDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(520,500)); + } + + settings.endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,47 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef DASHBOARDDIALOG_HPP_ +#define DASHBOARDDIALOG_HPP_ + +#include + +#include "ui_DashboardDialog.h" + +class DashboardWidget; + +class DashboardDialog : public QDialog, protected Ui::DashboardDialog +{ +Q_OBJECT + +public: + explicit DashboardDialog(QWidget *parent=0); + ~DashboardDialog(); + + void add(DashboardWidget*); + DashboardWidget* dashboardWidget() const {return dw_;} + +public Q_SLOTS: + void reject(); + void slotUpdateTitle(QString,QString); + void slotOwnerDelete(); + +Q_SIGNALS: + void aboutToClose(); + +protected: + void closeEvent(QCloseEvent * event); + void readSettings(); + void writeSettings(); + + DashboardWidget* dw_; + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,85 @@ + + + DashboardDialog + + + + 0 + 0 + 538 + 251 + + + + + 0 + 0 + + + + About EcflowUI + + + false + + + + 4 + + + 2 + + + 4 + + + 4 + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox_ + accepted() + DashboardDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox_ + rejected() + DashboardDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDock.cpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDock.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDock.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDock.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,238 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "DashboardDock.hpp" + +#include +#include + +#include "DashboardWidget.hpp" +#include "IconProvider.hpp" + +DashboardDockTitleWidget::DashboardDockTitleWidget(QWidget *parent) : + QWidget(parent), + titleBc_(0) +{ + setupUi(this); + + setProperty("dockTitle","1"); + + //We define the style here because it did not work from the qss file + QPalette p=palette(); + QLinearGradient gr(0,0,0,1); + gr.setCoordinateMode(QGradient::ObjectBoundingMode); + gr.setColorAt(0,QColor(126,126,126)); + gr.setColorAt(0.4,QColor(105,105,105)); + gr.setColorAt(0.41,QColor(97,97,97)); + gr.setColorAt(1,QColor(70,70,70)); + p.setBrush(QPalette::Window,gr); + setPalette(p); + + //Add warning icon + IconProvider::add(":viewer/warning.svg","warning"); + + //Title icon + text + p=titleLabel_->palette(); + p.setColor(QPalette::WindowText,Qt::white); + titleLabel_->setPalette(p); + titleLabel_->hide(); + titleIconLabel_->hide(); + + detachedTb_->setProperty("docktitle","1"); + maxTb_->setProperty("docktitle","1"); + optionsTb_->setProperty("docktitle","1"); + closeTb_->setProperty("docktitle","1"); +} + +void DashboardDockTitleWidget::setBcWidget(QWidget *w) +{ + Q_ASSERT(mainLayout->count() > 1); + Q_ASSERT(w); + titleBc_=w; + mainLayout->insertWidget(1,titleBc_,1); +#if 0 + for(int i=0; i < mainLayout->count(); i++) + { + if(QLayoutItem* item=mainLayout->itemAt(i)) + { + if(item->widget() == titleIconLabel_) + { + titleBc_=w; + mainLayout->insertWidget(i,titleBc_,1); + return; + } + } + } +#endif + + Q_ASSERT(titleBc_); +} + +void DashboardDockTitleWidget::setDetachedAction(QAction *ac) +{ + detachedTb_->setDefaultAction(ac); +} + +void DashboardDockTitleWidget::setMaximisedAction(QAction *ac) +{ + maxTb_->setDefaultAction(ac); +} + +void DashboardDockTitleWidget::addActions(QList lst) +{ + Q_FOREACH(QAction* ac,lst) + { + QToolButton *tb=new QToolButton(this); + tb->setProperty("docktitle","1"); + tb->setDefaultAction(ac); + tb->setAutoRaise(true); + tb->setPopupMode(QToolButton::InstantPopup); + //tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + + QPalette p=tb->palette(); + p.setColor(QPalette::ButtonText,Qt::white); + tb->setPalette(p); + + actionLayout_->addWidget(tb); + + if(!ac->isEnabled()) + tb->hide(); + + actionTbList_ << tb; + + connect(ac,SIGNAL(changed()), + this,SLOT(slotActionChanged())); + } + + //Hide the separator line if no buttons were added + //if(actionTbList_.isEmpty()) + // line_->hide(); +} + +void DashboardDockTitleWidget::slotActionChanged() +{ + Q_FOREACH(QToolButton *tb, actionTbList_) + { + if(!tb->isEnabled()) + { + tb->hide(); + } + else + { + tb->show(); + } + } +} + + +QSize DashboardDockTitleWidget::sizeHint() const +{ + QSize s=QWidget::sizeHint(); + //s.setHeight(); + return s; +} + +QSize DashboardDockTitleWidget::minimumSizeHint() const +{ + QSize s=QWidget::minimumSizeHint(); + //s.setHeight(16); + return s; +} + +QToolButton* DashboardDockTitleWidget::optionsTb() const +{ + return optionsTb_; +} + +#if 0 +void DashboardDockTitleWidget::on_floatTb__clicked(bool) +{ + if(QDockWidget *dw = qobject_cast(parentWidget())) + { + if(dw->features().testFlag(QDockWidget::DockWidgetFloatable)) + { + bool current=dw->isFloating(); + dw->setFloating(!current); + } + } +} +#endif + +void DashboardDockTitleWidget::on_closeTb__clicked(bool) +{ + if(QDockWidget *dw = qobject_cast(parentWidget())) + { + dw->close(); + } +} + +void DashboardDockTitleWidget::slotUpdateTitle(QString txt,QString type) +{ + if(txt.isEmpty()) + { + titleLabel_->hide(); + titleIconLabel_->hide(); + } + else + { + if(type == "warning") + { + titleIconLabel_->setPixmap(IconProvider::pixmap("warning",16)); + titleIconLabel_->show(); + txt.prepend(" "); + txt.append(" "); + } + else + { + titleIconLabel_->show(); + } + + titleLabel_->show(); + titleLabel_->setText(txt); + } +} + +//============================================================ +// +// DashBoardDock +// +//============================================================ + +DashboardDock::DashboardDock(DashboardWidget *dw,QWidget * parent) : + QDockWidget(parent) +{ + setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable); + + DashboardDockTitleWidget *dt=new DashboardDockTitleWidget(this); + + setTitleBarWidget(dt); + + dt->setDetachedAction(dw->detachedAction()); + dt->setMaximisedAction(dw->maximisedAction()); + dt->addActions(dw->dockTitleActions()); + + connect(dw,SIGNAL(titleUpdated(QString,QString)), + dt,SLOT(slotUpdateTitle(QString,QString))); + + dw->populateDockTitleBar(dt); + + setWidget(dw); +} + +void DashboardDock::showEvent(QShowEvent* event) +{ + QWidget::showEvent(event); +} + +void DashboardDock::closeEvent (QCloseEvent *event) +{ + QWidget::closeEvent(event); + Q_EMIT closeRequested(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDock.hpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDock.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDock.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDock.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,70 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef DASHBOARDDOCK_HPP_ +#define DASHBOARDDOCK_HPP_ + +#include + +#include "ui_DashboardDockTitleWidget.h" + +class DashboardWidget; +class QToolButton; + +class DashboardDockTitleWidget : public QWidget, protected Ui::DashboardDockTitleWidget +{ +Q_OBJECT + +public: + explicit DashboardDockTitleWidget(QWidget *parent=0); + + void addInfoPanelActions(); + QSize sizeHint() const; + QSize minimumSizeHint() const; + QToolButton* optionsTb() const; + void setBcWidget(QWidget *w); + void addActions(QList lst); + void setDetachedAction(QAction *ac); + void setMaximisedAction(QAction *ac); + +public Q_SLOTS: + void slotUpdateTitle(QString txt,QString type); + +protected Q_SLOTS: +#if 0 + void on_floatTb__clicked(bool); +#endif + void on_closeTb__clicked(bool); + void slotActionChanged(); + +Q_SIGNALS: + void detachedChanged(bool); + +protected: + QList actionTbList_; + QWidget* titleBc_; + QPixmap warnPix_; +}; + +class DashboardDock : public QDockWidget +{ +Q_OBJECT + +public: + explicit DashboardDock(DashboardWidget* dw,QWidget * parent=0); + +Q_SIGNALS: + void closeRequested(); + +protected: + void showEvent(QShowEvent* event); + void closeEvent (QCloseEvent *event); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDockTitleWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDockTitleWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardDockTitleWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardDockTitleWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,221 @@ + + + DashboardDockTitleWidget + + + + 0 + 0 + 711 + 140 + + + + + 0 + 0 + + + + Form + + + + + + true + + + + 2 + + + QLayout::SetMinAndMaxSize + + + 0 + + + 2 + + + 0 + + + 2 + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 2 + 20 + + + + + + + + Qt::Horizontal + + + + 15 + 20 + + + + + + + + TextLabel + + + + + + + + 0 + 0 + + + + + 8 + + + + Title + + + Qt::AlignCenter + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + + + + + 0 + + + + + + + Detach from selection in other panels + + + + + + + :/viewer/images/dock_float.svg:/viewer/images/dock_float.svg + + + false + + + false + + + + + + + Options + + + + + + + :/viewer/dock_config.svg:/viewer/dock_config.svg + + + false + + + QToolButton::InstantPopup + + + true + + + + + + + ... + + + + :/viewer/dock_max.svg:/viewer/dock_max.svg + + + + 16 + 16 + + + + Qt::ToolButtonIconOnly + + + true + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + Close panel + + + + + + + :/viewer/images/dock_close.svg:/viewer/images/dock_close.svg + + + + 16 + 16 + + + + true + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Dashboard.hpp ecflow-4.11.1/Viewer/ecflowUI/src/Dashboard.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Dashboard.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Dashboard.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,100 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef DASHBOARD_HPP_ +#define DASHBOARD_HPP_ + +#include "Viewer.hpp" + +#include + +#include "ServerFilter.hpp" +#include "VInfo.hpp" +#include "VSettings.hpp" + +class DashboardTitle; +class DashboardWidget; +class ServerFilter; +class ServerItem; +class VComboSettings; + +class Dashboard : public QMainWindow, public ServerFilterObserver +{ + Q_OBJECT + +public: + Dashboard(QString,QWidget* parent=0); + ~Dashboard(); + + void reload(); + void rerender(); + + DashboardWidget* addWidget(const std::string& type); + DashboardWidget* addDialog(const std::string& type); + Viewer::ViewMode viewMode(); + void setViewMode(Viewer::ViewMode); + ServerFilter* serverFilter() const {return serverFilter_;} + VInfo_ptr currentSelection(); + void currentSelection(VInfo_ptr n); + bool selectInTreeView(VInfo_ptr); + void addSearchDialog(); + DashboardTitle* titleHandler() const {return titleHandler_;} + bool hasMaximised() const; + bool hasMaximisedApplied() const; + + void notifyServerFilterAdded(ServerItem* item); + void notifyServerFilterRemoved(ServerItem* item); + void notifyServerFilterChanged(ServerItem*); + void notifyServerFilterDelete(); + + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + void contentsChanged(); + void aboutToDelete(); + +public Q_SLOTS: + void slotPopInfoPanel(VInfo_ptr,QString); + void slotCommand(VInfo_ptr,QString); + +protected Q_SLOTS: + void slotDockClose(); + void slotDialogFinished(int); + void slotDialogClosed(); + void slotPopInfoPanel(QString); + void slotSelectionChanged(VInfo_ptr info); + void slotMaximisedChanged(DashboardWidget* w); + +protected: + void contextMenuEvent(QContextMenuEvent* e); + +private: + DashboardWidget* addWidgetCore(const std::string& type,bool userAddedView); + DashboardWidget* addWidget(const std::string& type,const std::string& dockId,bool userAddedView); + QString uniqueDockId(); + static std::string widgetSettingsId(int i); + void initialSelectionInView(); + VInfo_ptr currentSelectionInView(); + void addSearchDialog(VInfo_ptr); + void resetMaximised(); + void checkMaximisedState(); + + ServerFilter* serverFilter_; + DashboardTitle* titleHandler_; + QList widgets_; + QList popupWidgets_; + QByteArray savedDockState_; + bool settingsAreRead_; + static int maxWidgetNum_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardTitle.cpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardTitle.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardTitle.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardTitle.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,385 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "DashboardTitle.hpp" + +#include "Dashboard.hpp" +#include "ServerFilter.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" +#include "UiLog.hpp" + +#include +#include + +int DashboardTitle::lighter_=150; + +//#define _UI_DASHBOARDTITLE_DEBUG + +DashboardTitle::DashboardTitle(ServerFilter* filter,Dashboard *parent) : + QObject(parent), + dashboard_(parent), + filter_(filter), + maxPixWidth_(0), + current_(false) +{ + filter_->addObserver(this); +} + +DashboardTitle::~DashboardTitle() +{ + clear(); +} + +void DashboardTitle::clear() +{ + if(!filter_) + return; + + filter_->removeObserver(this); + for(std::vector::const_iterator it=filter_->items().begin(); it !=filter_->items().end(); ++it) + { + ServerHandler* s=(*it)->serverHandler(); + s->removeServerObserver(this); + } + + filter_=0; +} + + +void DashboardTitle::notifyServerFilterAdded(ServerItem* item) +{ + ServerHandler* s=item->serverHandler(); + s->addServerObserver(this); + updateTitle(); +} + +void DashboardTitle::notifyServerFilterRemoved(ServerItem* item) +{ + ServerHandler* s=item->serverHandler(); + s->removeServerObserver(this); + updateTitle(); +} + +void DashboardTitle::notifyServerFilterChanged(ServerItem*) +{ + updateTitle(); +} + +void DashboardTitle::notifyServerFilterDelete() +{ + clear(); +} + +void DashboardTitle::notifyDefsChanged(ServerHandler* server, const std::vector& a) +{ + updateTitle(); +} + +void DashboardTitle::notifyServerDelete(ServerHandler* server) +{ + //server->removeServerObserver(this); +} + +void DashboardTitle::notifyEndServerClear(ServerHandler* server) +{ + updateTitle(); +} + +void DashboardTitle::notifyEndServerScan(ServerHandler* server) +{ + updateTitle(); +} + +void DashboardTitle::notifyServerConnectState(ServerHandler* server) +{ + updateTitle(); +} + +void DashboardTitle::notifyServerActivityChanged(ServerHandler* server) +{ + updateTitle(); +} + +void DashboardTitle::setMaxPixWidth(int w) +{ + maxPixWidth_=w; + updateTitle(); +} + +void DashboardTitle::setCurrent(bool b) +{ + if(b != current_) + { + current_=b; + updateTitle(); + } +} + +int DashboardTitle::fullWidth() const +{ + QFont f; + QFontMetrics fm(f); + const int gap=1; + const int padding=10; + int w=0; + for(size_t i=0; i < filter_->items().size(); i++) + { + QString str=QString::fromStdString(filter_->items()[i]->name()); + int tw=fm.width(str); + if(tw > w) w=tw; + } + + return 10+filter_->items().size()*(w+padding)+(filter_->items().size()-1)*gap; +} + +void DashboardTitle::updateTitle() +{ + pix_=QPixmap(); + tooltip_.clear(); + title_.clear(); + desc_.clear(); + descPix_=QPixmap(); + + if(!filter_ || filter_->itemCount()==0) + { + tooltip_=tr("No server is loaded in this this tab"); + desc_=tr("No server loaded in tab"); + Q_EMIT changed(this); + return; + } + + if(maxPixWidth_ == 0) + return; + + QStringList texts; + QList fillColors; + QList textColors; + + const std::vector& items=filter_->items(); + for(std::vector::const_iterator it=items.begin(); it != items.end(); ++it) + { + //Get text + QString str=QString::fromStdString((*it)->name()); + QString host=QString::fromStdString((*it)->host()); + QString port=QString::fromStdString((*it)->port()); + + //Get server status + ServerHandler* server=(*it)->serverHandler(); + fillColors << server->vRoot()->stateColour(); + textColors << server->vRoot()->stateFontColour(); + + texts << str; + + //Description for tab list menu + if(!desc_.isEmpty()) + { + desc_+=", "; + } + desc_+=str; + + //Tooltip + if(!tooltip_.isEmpty()) + { + tooltip_+="
      -------------------
      "; + } + tooltip_+=server->vRoot()->toolTip(); + } + + int num=texts.count(); + Q_ASSERT(num>0); + + { + const int marginX=0; + const int gap=1; + + int maxBandH=2; + int bandH=(current_)?maxBandH:2; + + QFont f; + QFontMetrics fm(f); + + QList textRects; + QList fillRects; + + //Compute the pixmap size + int h=fm.height()+bandH+1; + //int w=fm.width("ABCD...")+(num-1); + int w=maxPixWidth_-10; + int dw=w/num; + + int xp=0; + int yp=0; + bool noText=false; + + for(int i=0; i < texts.count(); i++) + { +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg() << i << texts[i] << dw << fm.width(texts[0]); +#endif + QString txt=fm.elidedText(texts[i],Qt::ElideRight,dw); +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg() << " " << txt << fm.width(txt); +#endif + QString ellips(0x2026); //horizontal ellipsis + + if(txt.startsWith(ellips)) + { + txt=texts[i].left(2); + txt=fm.elidedText(txt,Qt::ElideRight,dw); + } +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg() << " " << txt << fm.width(txt); +#endif + if(txt.startsWith(ellips)) + { + txt=texts[i].left(1); + txt=fm.elidedText(txt,Qt::ElideRight,dw); + } +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg()<< " " << txt << fm.width(txt); +#endif + if(txt.isEmpty()) + { + txt=texts[i].left(1); + } +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg() << " " << txt << fm.width(txt); +#endif + if(fm.width(txt) > dw) + { + texts[i]=""; + noText=true; + } + else + { + texts[i]=txt; + } +#ifdef _UI_DASHBOARDTITLE_DEBUG + UiLog().dbg() << " " << texts[i] << fm.width(texts[i]); +#endif + textRects << QRect(xp,yp,dw,fm.height()); + fillRects << QRect(xp,yp+fm.height(),dw,bandH); + xp=fillRects.back().right()+gap; + } + + w=xp-gap+marginX+2; + + //Render the pixmap + pix_=QPixmap(w,h); + pix_.fill(Qt::transparent); + QPainter painter(&pix_); + + for(int i=0; i < texts.count(); i++) + { + QColor bg=fillColors[i]; + + if(noText) + { + QRect r=fillRects[i]; + r.setTop(0); + r.setBottom(h-2); + fillRects[i]=r; + + QColor bgLight; + if(bg.value() < 235) + bgLight=bg.lighter(130); + else + bgLight=bg.lighter(lighter_); + + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(0,1); + + grad.setColorAt(0,bgLight); + grad.setColorAt(1,bg); + QBrush bgBrush(grad); + + QColor borderCol=bg.darker(150); + painter.setPen(borderCol); + painter.setBrush(bgBrush); + painter.drawRect(fillRects[i]); + } + else + { + QColor fg=QColor(0,0,0); + painter.setPen(fg); + painter.drawText(textRects[i],Qt::AlignCenter,texts[i]); + + QColor borderCol=bg.darker(140); + painter.setPen(borderCol); + painter.setBrush(bg); + painter.drawRect(fillRects[i]); + + if( i >0) + { + painter.setPen(QColor(140,140,140)); + painter.drawLine(fillRects[i].left(),fillRects[i].bottom(), + fillRects[i].left(),textRects[i].center().y()); + } + } + } + } + + //Desc pix + { + QFont f; + QFontMetrics fm(f);//Compute the pixmap size + int h=fm.height()+2; + //int w=fm.width("ABCD...")+(num-1); + int w=h*5/4; + int dw=w/num; + + descPix_=QPixmap(w+1,h+1); + descPix_.fill(Qt::transparent); + QPainter painter(&descPix_); + + int xp=0; + for(int i=0; i < fillColors.count(); i++) + { + QRect r(xp,0,dw,h); + QColor bg=fillColors[i]; + QColor bgLight; + if(bg.value() < 235) + bgLight=bg.lighter(130); + else + bgLight=bg.lighter(lighter_); + + QColor borderCol=bg.darker(150); + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(0,1); + + grad.setColorAt(0,bgLight); + grad.setColorAt(1,bg); + QBrush bgBrush(grad); + + painter.setPen(borderCol); + painter.setBrush(bgBrush); + painter.drawRect(r); + xp+=dw; + } + + } + + if(tooltip_.isEmpty()) + tooltip_=tr("No server is loaded in this this tab"); + + if(desc_.isEmpty()) + desc_=tr("No server loaded in tab"); + + Q_EMIT changed(this); +} + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardTitle.hpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardTitle.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardTitle.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardTitle.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,76 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef DASHBOARDTITLE_HPP_ +#define DASHBOARDTITLE_HPP_ + +#include +#include + +#include "ServerFilter.hpp" +#include "ServerObserver.hpp" + +class Dashboard; +class ServerItem; + +class DashboardTitle : public QObject, public ServerFilterObserver, public ServerObserver +{ +Q_OBJECT + +friend class Dashboard; + +public: + DashboardTitle(ServerFilter*,Dashboard *parent); + ~DashboardTitle(); + + Dashboard* dashboard() const {return dashboard_;} + QString title() const {return title_;} + QString tooltip() const {return tooltip_;} + QString desc() const {return desc_;} + QPixmap pix() const {return pix_;} + QPixmap descPix() const {return descPix_;} + void setMaxPixWidth(int w); + void setCurrent(bool b); + int fullWidth() const; + + void notifyServerFilterAdded(ServerItem* item); + void notifyServerFilterRemoved(ServerItem* item); + void notifyServerFilterChanged(ServerItem*); + void notifyServerFilterDelete(); + + void notifyDefsChanged(ServerHandler* server, const std::vector& a); + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server) {} + void notifyEndServerClear(ServerHandler* server); + void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + void notifyEndServerScan(ServerHandler* server); + void notifyServerConnectState(ServerHandler* server); + void notifyServerActivityChanged(ServerHandler* server); + void notifyServerSuiteFilterChanged(ServerHandler* server) {} + +Q_SIGNALS: + void changed(DashboardTitle*); + +private: + void clear(); + void updateTitle(); + + Dashboard* dashboard_; + ServerFilter* filter_; + int maxPixWidth_; + QPixmap pix_; + QPixmap descPix_; + QString title_; + QString tooltip_; + QString desc_; + bool current_; + static int lighter_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,132 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#include "DashboardWidget.hpp" + +#include "VSettings.hpp" + +static QString tooltipUnchk("This panel is linked and will respond to selections made in other panels. Click to toggle.") ; +static QString tooltipChk("This panel is detached and will not respond to selections made in other panels. Click to toggle."); + +static QString tooltipMaxChk("This panel is maximised and hides other panels. Click to restore its original size.") ; +static QString tooltipMaxUnchk("Maximise panel"); + +DashboardWidget::DashboardWidget(const std::string& type, QWidget* parent) : + QWidget(parent), + type_(type), + acceptSetCurrent_(false), + ignoreMaximisedChange_(false), + bcWidget_(0), + inDialog_(false) +{ + //detach + detachedAction_=new QAction("Detached",this); + QIcon ic(QPixmap(":viewer/dock_chain_closed.svg")); + ic.addPixmap(QPixmap(":viewer/dock_chain_open.svg"),QIcon::Normal,QIcon::On); + detachedAction_->setIcon(ic); + detachedAction_->setCheckable(true); + detachedAction_->setChecked(false); + + connect(detachedAction_,SIGNAL(toggled(bool)), + this,SLOT(slotDetachedToggled(bool))); + + detachedAction_->setToolTip(tooltipUnchk); + + //maximise + maximisedAction_=new QAction("Maximise",this); + QIcon icM(QPixmap(":viewer/dock_max.svg")); + icM.addPixmap(QPixmap(":viewer/dock_restore.svg"),QIcon::Normal,QIcon::On); + icM.addPixmap(QPixmap(":viewer/dock_max_disabled.svg"),QIcon::Disabled,QIcon::Off); + maximisedAction_->setIcon(icM); + maximisedAction_->setCheckable(true); + maximisedAction_->setChecked(false); + maximisedAction_->setToolTip("max"); + connect(maximisedAction_,SIGNAL(toggled(bool)), + this,SLOT(slotMaximisedToggled(bool))); + + maximisedAction_->setToolTip(tooltipMaxUnchk); +} + +void DashboardWidget::slotDetachedToggled(bool b) +{ + detachedChanged(); + detachedAction_->setToolTip((detached())?tooltipChk:tooltipUnchk); +} + +void DashboardWidget::setDetached(bool b) +{ + if(detached() != b) + { + detachedAction_->setChecked(b); + } +} + +bool DashboardWidget::detached() const +{ + return detachedAction_->isChecked(); +} + +void DashboardWidget::slotMaximisedToggled(bool b) +{ + if(!ignoreMaximisedChange_) + Q_EMIT maximisedChanged(this); + + if(b) + Q_EMIT titleUpdated("Panel maximised!","warning"); + else + Q_EMIT titleUpdated(""); + + maximisedAction_->setToolTip((isMaximised())?tooltipMaxChk:tooltipMaxUnchk); +} + +bool DashboardWidget::isMaximised() const +{ + return maximisedAction_->isChecked(); +} + +//reset the state of the maximised actione without emitting a change signal +void DashboardWidget::resetMaximised() +{ + ignoreMaximisedChange_=true; + maximisedAction_->setChecked(false); + ignoreMaximisedChange_=false; +} + +void DashboardWidget::setEnableMaximised(bool st) +{ + resetMaximised(); + maximisedAction_->setEnabled(st); +} + +void DashboardWidget::setInDialog(bool b) +{ + if(inDialog_ != b) + { + inDialog_=b; + QIcon ic(QPixmap(":viewer/chain_closed.svg")); + ic.addPixmap(QPixmap(":viewer/chain_open.svg"),QIcon::Normal,QIcon::On); + detachedAction_->setIcon(ic); + } + + if(b) + { + maximisedAction_->setEnabled(false); + maximisedAction_->setVisible(false); + } +} + +void DashboardWidget::writeSettings(VComboSettings* vs) +{ + vs->putAsBool("detached",detached()); +} + +void DashboardWidget::readSettings(VComboSettings* vs) +{ + setDetached(vs->getAsBool("detached",detached())); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DashboardWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/DashboardWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DashboardWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DashboardWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,90 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#ifndef DASHBOARDWIDGET_HPP_ +#define DASHBOARDWIDGET_HPP_ + +#include +#include +#include +#include +#include + +#include "VInfo.hpp" + +class DashboardDockTitleWidget; +class NodePathWidget; +class ServerFilter; +class VComboSettings; + +class DashboardWidget : public QWidget +{ +Q_OBJECT + +public: + DashboardWidget(const std::string& type, QWidget* parent=0); + virtual ~DashboardWidget() {} + + virtual void populateDockTitleBar(DashboardDockTitleWidget*)=0; + virtual void populateDialog()=0; + virtual void reload()=0; + virtual void rerender()=0; + virtual bool initialSelectionInView() {return false;} + virtual VInfo_ptr currentSelection() {return VInfo_ptr(); } + QAction* detachedAction() const {return detachedAction_;} + QAction* maximisedAction() const {return maximisedAction_;} + virtual QList dockTitleActions() {return QList();} + + bool detached() const; + void setDetached(bool b); + bool isMaximised() const; + void resetMaximised(); + void setEnableMaximised(bool st); + bool isInDialog() const {return inDialog_;} + + virtual void writeSettings(VComboSettings*); + virtual void readSettings(VComboSettings*); + virtual void writeSettingsForDialog() {} + virtual void readSettingsForDialog() {} + + const std::string type() const {return type_;} + void id(const std::string& id) {id_=id;} + +public Q_SLOTS: + virtual void setCurrentSelection(VInfo_ptr)=0; + +Q_SIGNALS: + void titleUpdated(QString,QString type=QString()); + void selectionChanged(VInfo_ptr); + void maximisedChanged(DashboardWidget*); + void popInfoPanel(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + +protected Q_SLOTS: + void slotDetachedToggled(bool); + void slotMaximisedToggled(bool); + +protected: + virtual void detachedChanged()=0; + void setInDialog(bool); + + std::string id_; + std::string type_; + bool acceptSetCurrent_; + QAction *detachedAction_; + QAction *maximisedAction_; + bool ignoreMaximisedChange_; + NodePathWidget* bcWidget_; + +private: + bool inDialog_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DiagData.cpp ecflow-4.11.1/Viewer/ecflowUI/src/DiagData.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DiagData.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DiagData.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,247 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "DiagData.hpp" + +#include "File_r.hpp" +#include "File.hpp" +#include "ModelColumn.hpp" +#include "NodePath.hpp" +#include "ServerHandler.hpp" +#include "Str.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "VNode.hpp" +#include "VItemPathParser.hpp" + +#include +#include + +DiagData* DiagData::instance_=NULL; + +DiagDataServerItem::DiagDataServerItem(const std::string& host,const std::string& port, size_t colNum) : + host_(host), port_(port) +{ + for(size_t i=0; i < colNum; i++) + data_.push_back(std::vector()); +} + +const std::string& DiagDataServerItem::dataAt(int row,int column) const +{ + return data_[column][row]; + //static std::string emptyStr; + //return emptyStr; +} + +int DiagDataServerItem::findRowByPath(const std::string& path) const +{ + for(size_t i=0; i < pathData_.size(); i++) + if(pathData_[i] == path) + return i; + + return -1; +} + +bool DiagDataServerItem::checkSizes() const +{ + size_t num=pathData_.size(); + for(size_t i=0; i < data_.size(); i++) + { + if(data_[i].size() != num) + return false; + } + return true; +} + +DiagData::DiagData() +{ +} + +DiagData* DiagData::instance() +{ + if(!instance_) + instance_=new DiagData(); + + return instance_; +} + +const std::string& DiagData::columnName(int i) const +{ + if(i >=0 && i < static_cast(columnNames_.size())) + return columnNames_[i]; + + static std::string emptyStr; + return emptyStr; +} + +const std::string& DiagData::dataAt(VNode* vn,int column) const +{ + static std::string emptyStr; + if(!vn) + return emptyStr; + + if(ServerHandler* sh=vn->server()) + { + if(DiagDataServerItem *d=findServerData(sh->host(),sh->port())) + { + int row=d->findRowByPath(vn->absNodePath()); + if(row > -1) + return d->dataAt(row,column); + } + } + + return emptyStr; +} + +DiagDataServerItem* DiagData::findServerData(const std::string& host,const std::string& port) const +{ + if(host.empty() || port.empty()) + return NULL; + + for(size_t i=0; i < serverData_.size(); i++) + { + if(serverData_[i]->host_ == host && serverData_[i]->port_ == port) + return serverData_[i]; + } + + return NULL; +} + + +void DiagData::clear() +{ + fileName_.clear(); + columnNames_.clear(); + for(size_t i=0; i < serverData_.size(); i++) + { + delete serverData_[i]; + } + serverData_.clear(); +} + +void DiagData::load() +{ + if(const char *df=getenv("ECFLOWUI_DIAG_FILE")) + loadFile(std::string(df)); +} + +void DiagData::loadFile(const std::string& fileName) +{ + clear(); + + fileName_=fileName; + + /// The log file can be massive > 50Mb + ecf::File_r diag_file(fileName_); + if( !diag_file.ok() ) + { + UiLog().warn() << "DiagData::loadFile: Could not open diagnostics file " + fileName; + return; + } + + std::string line; + + //The first line is the header + diag_file.getline(line); // default delimiter is /n + + //No header, no diag data can be read + if(line.empty()) + return; + + QString headerLine=QString::fromStdString(line); + if(headerLine.startsWith("#")) + headerLine=headerLine.mid(1); + + QStringList headerLst=headerLine.split(","); + int pathColumnIndex=-1; + for(int i=0; i < headerLst.count(); i++) + { + if(headerLst[i].compare("path",Qt::CaseInsensitive) == 0) + pathColumnIndex=i; + else + columnNames_.push_back(headerLst[i].toStdString()); + } + + if(pathColumnIndex == -1) + { + UiLog().warn() << "DiagData::loadFile: could not find node path column in file. Diagnostics cannot be loaded!"; + return; + } + + while ( diag_file.good() ) + { + diag_file.getline(line); // default delimiter is /n + + if(line.size() <= 1) + continue; + + if(line[0] == '#') + line=line.substr(1); + + //split by comma + QStringList lst=QString::fromStdString(line).split(","); + + //When number of items does not match expected number we skip the line + if(lst.count() != static_cast(columnNames_.size()) + 1) + continue; + + std::string path=lst[pathColumnIndex].toStdString(); + std::string host, port; + DiagDataServerItem* data=NULL; + VItemPathParser parser(path,VItemPathParser::DiagFormat); + if(parser.itemType() != VItemPathParser::NoType) + { + data=findServerData(parser.host(),parser.port()); + if(!data) + { + data=new DiagDataServerItem(parser.host(),parser.port(),columnNames_.size()); + serverData_.push_back(data); + } + } + else + { + continue; + } + + data->pathData_.push_back(parser.node()); + assert(data->data_.size() == columnNames_.size()); + + for(int i=0; i < lst.count(); i++) + { + QString val=lst[i]; +#if 0 + QStringList vals=lst[i].split(":"); + QString val=lst[i]; + if(vals.count() == 2) + columnData_[i].push_back(vals[1].toStdString()); + else + columnData_[i].push_back(lst[i].toStdString()); +#endif + + int idx=i; + if(idx > pathColumnIndex) + idx--; + + + if(i != pathColumnIndex) + data->data_[idx].push_back(val.toStdString()); + } + + } + + updateTableModelColumn(); +} + +void DiagData::updateTableModelColumn() +{ + if(ModelColumn* mc=ModelColumn::def("table_columns")) + { + mc->setDiagData(this); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/DiagData.hpp ecflow-4.11.1/Viewer/ecflowUI/src/DiagData.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/DiagData.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/DiagData.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,59 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef DIAGDATA_HPP +#define DIAGDATA_HPP + +#include +#include + +class VNode; +class DiagData; + +class DiagDataServerItem +{ + friend class DiagData; + +public: + DiagDataServerItem(const std::string& host,const std::string& port,size_t); + const std::string& dataAt(int row,int column) const; + int findRowByPath(const std::string& path) const; + +protected: + bool checkSizes() const; + std::string host_; + std::string port_; + std::vector pathData_; + std::vector > data_; +}; + +class DiagData +{ +public: + static DiagData* instance(); + + void load(); + void loadFile(const std::string&); + int count() const {return static_cast(columnNames_.size());} + const std::string& columnName(int i) const; + const std::string& dataAt(VNode*,int column) const; + +protected: + DiagData(); + void clear(); + void updateTableModelColumn(); + DiagDataServerItem* findServerData(const std::string& host,const std::string& port) const; + + static DiagData* instance_; + std::string fileName_; + std::vector columnNames_; + std::vector serverData_; +}; + +#endif // DIAGDATA_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,157 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "EditItemWidget.hpp" + +#include "EditProvider.hpp" +#include "Highlighter.hpp" +#include "PropertyMapper.hpp" +#include "VConfig.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "MessageLabel.hpp" + +//======================================================== +// +// EditItemWidget +// +//======================================================== + +EditItemWidget::EditItemWidget(QWidget *parent) : + QWidget(parent) +{ + setupUi(this); + + infoProvider_=new EditProvider(this); + + //The document becomes the owner of the highlighter + new Highlighter(textEdit_->document(),"script"); + + searchLine_->setEditor(textEdit_); + + searchLine_->setVisible(false); + + externTb_->hide(); + + //connect(submitTb_,SIGNAL(clicked(bool)), + // this,SLOT(on_submitTb__clicked(bool))); + + textEdit_->setProperty("edit","1"); + textEdit_->setFontProperty(VConfig::instance()->find("panel.edit.font")); +} + +QWidget* EditItemWidget::realWidget() +{ + return this; +} + +void EditItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + info_=info; + + //Info must be a node + if(info_ && info_->isNode() && info_->node()) + { + //Get file contents + EditProvider* ep=static_cast(infoProvider_); + ep->preproc(preproc()); + infoProvider_->info(info_); + } +} + +void EditItemWidget::clearContents() +{ + InfoPanelItem::clear(); + textEdit_->clear(); + infoLabel_->clear(); + infoLabel_->hide(); +} + +void EditItemWidget::infoReady(VReply* reply) +{ + infoLabel_->hide(); + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); +} + +void EditItemWidget::infoFailed(VReply* reply) +{ + infoLabel_->showError(QString::fromStdString(reply->errorText())); + infoLabel_->show(); + //UserMessage::message(UserMessage::ERROR, true, reply->errorText()); +} + +void EditItemWidget::infoProgress(VReply*) +{ + +} + +void EditItemWidget::on_preprocTb__toggled(bool) +{ + reload(info_); +} + +void EditItemWidget::on_submitTb__clicked(bool) +{ + QStringList lst=textEdit_->toPlainText().split("\n"); + std::vector txt; + Q_FOREACH(QString s,lst) + { + txt.push_back(s.toStdString()); + } + + EditProvider* ep=static_cast(infoProvider_); + ep->submit(txt,alias()); +} + +void EditItemWidget::on_searchTb__clicked() +{ + searchLine_->setVisible(true); + searchLine_->setFocus(); + searchLine_->selectAll(); +} + +void EditItemWidget::on_gotoLineTb__clicked() +{ + textEdit_->gotoLine(); +} + +bool EditItemWidget::alias() const +{ + return aliasCb_->isChecked(); +} + +bool EditItemWidget::preproc() const +{ + return preprocTb_->isChecked(); +} + +//----------------------------------------- +// Fontsize management +//----------------------------------------- + +void EditItemWidget::on_fontSizeUpTb__clicked() +{ + //We need to call a custom slot here instead of "zoomIn"!!! + textEdit_->slotZoomIn(); +} + +void EditItemWidget::on_fontSizeDownTb__clicked() +{ + //We need to call a custom slot here instead of "zoomOut"!!! + textEdit_->slotZoomOut(); +} + +static InfoPanelItemMaker maker1("edit"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,55 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef EDITITEMWIDGET_HPP_ +#define EDITITEMWIDGET_HPP_ + +#include + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" + +#include "ui_EditItemWidget.h" + +class EditItemWidget : public QWidget, public InfoPanelItem, protected Ui::EditItemWidget +{ +Q_OBJECT + +public: + explicit EditItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected Q_SLOTS: + void on_preprocTb__toggled(bool); + void on_submitTb__clicked(bool); + void on_searchTb__clicked(); + void on_gotoLineTb__clicked(); + void on_fontSizeUpTb__clicked(); + void on_fontSizeDownTb__clicked(); + +protected: + bool preproc() const; + bool alias() const; + void updateState(const ChangeFlags&) {} +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/EditItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,223 @@ + + + EditItemWidget + + + + 0 + 0 + 494 + 484 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Increase font size in text editor <br><code>Ctrl++</code> + + + ... + + + + :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg + + + Ctrl++ + + + true + + + + + + + Decrease font size in text editor <br><code>Ctrl+-</code> + + + ... + + + + :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg + + + Ctrl+- + + + true + + + + + + + ... + + + + + + + Show search bar (CTRL-F) + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + true + + + true + + + + + + + Goto line number (CTRL-L) + + + ... + + + + :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg + + + Ctrl+L + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Submit as alias + + + + + + + Pre-process text + + + Pre-process + + + true + + + + + + + Submit + + + Submit + + + + :/viewer/submit.svg:/viewer/submit.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + + + + + + + 0 + 1 + + + + QPlainTextEdit::NoWrap + + + false + + + + + + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + PlainTextSearchLine + QWidget +
      PlainTextSearchLine.hpp
      + 1 +
      + + PlainTextEdit + QPlainTextEdit +
      PlainTextEdit.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditorInfoLabel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/EditorInfoLabel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditorInfoLabel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditorInfoLabel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,86 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "EditorInfoLabel.hpp" + +#include +#include + + +EditorInfoLabel::EditorInfoLabel(QWidget* parent) : QLabel(parent) +{ + //Define id for the css + setProperty("editorInfo","1"); + setWordWrap(true); + + //Set size policy + /*QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); + setSizePolicy(sizePolicy); + //setMinimumSize(QSize(0, 60)); + //setMaximumSize(QSize(16777215, 45));*/ + + setMargin(4); + setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + //Other settings + setAutoFillBackground(true); + + setFrameShape(QFrame::StyledPanel); + setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); +} + +void EditorInfoLabel::setInfo(QString parent,QString type) +{ + setText(formatKeyLabel("Node path: ") + formatNodePath(parent) + "
      " + + formatKeyLabel("Attibute type: ") + type); +} + +static QColor nodeNameColour(7,108,209); +static QColor serverNameColour(0,0,0); +static QColor labelColour(59,60,61); + +QString EditorInfoLabel::formatKeyLabel(QString n) +{ + return "" + n + ""; +} + +QString EditorInfoLabel::formatNodeName(QString n) +{ + return "" + n + ""; +} + +QString EditorInfoLabel::formatNodePath(QString p) +{ + if(p.endsWith("://")) + { + return p; + } + + QStringList lst=p.split("/"); + if(lst.count() > 0) + { + QString n=lst.back(); + lst.pop_back(); + QColor c(80,80,80); + QString s="" + lst.join("/") + "/" + + "" + n + ""; + return s; + } + + return p; +} + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditorInfoLabel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/EditorInfoLabel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditorInfoLabel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditorInfoLabel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,27 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef EDITORINFOLABEL_HPP +#define EDITORINFOLABEL_HPP + +#include + +class EditorInfoLabel : public QLabel +{ +public: + explicit EditorInfoLabel(QWidget* parent=0); + void setInfo(QString parent,QString type); + + static QString formatKeyLabel(QString n); + static QString formatNodeName(QString n); + static QString formatNodePath(QString p); +}; + +#endif // EDITORINFOLABEL_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/EditProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,126 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "EditProvider.hpp" + +#include "NodeFwd.hpp" + +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" + +#include + +//Node +void EditProvider::visit(VInfoNode* info) +{ + //Reset the reply + reply_->reset(); + + if(!info) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + VNode *n=info->node(); + + if(!n || !n->node()) + { + owner_->infoFailed(reply_); + return; + } + + if (preproc_) + { + //Define a task for getting the info from the server. + task_=VTask::create(VTask::ScriptPreprocTask,n,this); + } + else + { + task_=VTask::create(VTask::ScriptEditTask,n,this); + } + + //Run the task in the server. When it finishes taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + server->run(task_); +} + +void EditProvider::submit(const std::vector& txt,bool alias) +{ + if(!(info_ && info_.get() && info_->isNode() && info_->node()) && info_->server()) + return; + + VNode *node=info_->node(); + ServerHandler* server=info_->server(); + + bool run=true; + + //--------------------------- + // Extract user variables + //--------------------------- + + static std::string defMicro="%"; + static std::string comStartText = "comment - ecf user variables"; + static std::string comEndText = "end - ecf user variables"; + + NameValueVec vars; + + //Find out the micro + std::string microVar=node->findInheritedVariable("ECF_MICRO"); + std::string micro = (microVar.size() == 1) ? microVar.c_str() : defMicro; + + //Find out the full comment start and end texts + std::string comStart= micro + comStartText; + std::string comEnd= micro + comEndText; + + bool inVars = false; + for(std::vector::const_iterator it=txt.begin(); it != txt.end(); ++it) + { + const std::string& line=*it; + + //We are in the variable block + if(inVars) + { + std::size_t pos = (*it).find("="); + if(pos != std::string::npos && pos+1 != (*it).size()) + { + std::string name=(*it).substr(0,pos); + std::string val=(*it).substr(pos+1); + boost::trim(name); + boost::trim(val); + vars.push_back(std::make_pair(name,val)); + } + } + + if(line == comStart) + inVars = true; + + if(line == comEnd) + break; + } + + if(vars.empty()) + { + UiLog().dbg() << " No user variables!"; + } + + + task_=VTask::create(VTask::ScriptSubmitTask,node,this); + task_->contents(txt); + task_->vars(vars); + task_->param("alias",(alias)?"1":"0"); + task_->param("run",(run)?"1":"0"); + + //Run the task in the server. When it finishes taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + server->run(task_); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/EditProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/EditProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/EditProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/EditProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,34 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef EDITPROVIDER_HPP_ +#define EDITPROVIDER_HPP_ + +#include "VDir.hpp" +#include "VInfo.hpp" +#include "InfoProvider.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +class EditProvider : public InfoProvider +{ +public: + explicit EditProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::OutputTask), preproc_(false) {} + + void visit(VInfoNode*); + void submit(const std::vector& txt,bool alias); + + void preproc(bool b) {preproc_=b;} + +private: + bool preproc_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ExpandState.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ExpandState.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ExpandState.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ExpandState.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,395 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ExpandState.hpp" +#include "ExpandStateNode.hpp" +#include "AbstractNodeView.hpp" +#include "ServerHandler.hpp" +#include "TreeNodeModel.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "VNode.hpp" +#include "VTree.hpp" + +#include + +//#define _UI_EXPANDSTATE_DEBUG + +ExpandState::ExpandState(AbstractNodeView* view,TreeNodeModel* model) : + view_(view), model_(model), root_(0) +{ +} + +ExpandState::~ExpandState() +{ + clear(); +} + +void ExpandState::init(const VNode *vnode) +{ + clear(); + + ServerHandler* server=vnode->server(); + Q_ASSERT(server); + QModelIndex idx=model_->serverToIndex(server); + bool expanded=view_->isExpanded(idx); + + root_=new ExpandStateNode(server->vRoot(),expanded); + + save(server->vRoot()); +} + +bool ExpandState::isEmpty() const +{ + return !root_ || root_->children_.size() == 0; +} + +void ExpandState::clear() +{ + if(root_) + delete root_; + + root_=0; +} + + +//Save the expand state for a whole subtree (it can be the whole VNode tree as well) +void ExpandState::save(const VNode *vnode) +{ + UI_FUNCTION_LOG + UI_ASSERT(vnode,"node is null"); + UiLog().dbg() << " " << vnode->name(); + + if(!root_) + { + init(vnode); + Q_ASSERT(root_); + return; + } + + if(ExpandStateNode* es=find(vnode->absNodePath())) + { + Q_ASSERT(root_); + QModelIndex idx=model_->nodeToIndex(vnode); + + //It can happen that the VTree is empty but the server is already + //loaded. It is the situation when we load the same server in multiple + //tabs!!! We return here because there is nothing to save (and we would + //have a crash when trying to get index of the vnodes via the model!!) + if(VTreeNode* vtn=model_->indexToServerOrNode(idx)) + { + if(VTree* vt=vtn->root()) + { + if(vt->totalNum() == 0) + return; + } + } + + //save all the children recursively + save(vnode,es,idx); + } +} + +#if 0 +//Save the expand state for a whole subtree (it can be the whole VNode tree as well) +void ExpandState::save(const VNode *root) +{ + UI_FUNCTION_LOG + UI_ASSERT(root,"root is null"); + UiLog().dbg() << " " << root->name(); + + QModelIndex rootIdx=model_->nodeToIndex(root); + bool expanded=view_->isExpanded(rootIdx); + if(root_ == 0 || root_->name_ != root->strName()) + { + clear(); + root_=new ExpandStateNode(root,expanded); + } + else + { + root_->expanded_=expanded; + } + + //It can happen that the VTree is empty but the server is already + //loaded. It is the situation when we load the same server in multiple + //tabs!!! We return here because there is nothing to save (and we would + //have a crash when trying to get index of the vnodes via the model!!) + if(VTreeNode* vtn=model_->indexToServerOrNode(rootIdx)) + { + if(VTree* vt=vtn->root()) + { + if(vt->totalNum() == 0) + return; + } + } + + //save all the children recursively + save(root,root_,rootIdx); +} +#endif + +//Save the expand state for the given node. +// node: the vnode to save +// expandNode: the expand node representing the vnode +// idx: the modelindex of the vnode in the TreeNodeModel. It can be invalid +// since the model can use a filter and its tree (the VTree) might represent only +// the subset of all the vnodes of a given server +void ExpandState::save(const VNode *node,ExpandStateNode *expandNode,const QModelIndex& idx) +{ + std::size_t numExpandNode=expandNode->children_.size(); + std::size_t numNode=node->numOfChildren(); + + //the node and the expand node does not match. We clear the whole + //contents of the expand node + //CAN IT HAPPEN AT ALL?? + if(numExpandNode != numNode || expandNode->name_ != node->strName()) + { + UI_ASSERT(0,"numExpandNode=" << numExpandNode << " numNode=" << numNode << + " expandNode->name_=" << expandNode->name_ << " node->strName()=" << + node->strName()); + + expandNode->reset(node,view_->isExpanded(idx)); + //At the this point the expand node children vector is + //reserved, but contains null pointers + } + + for(std::size_t i=0; i < numNode; i++) + { + VNode* chNode=node->childAt(i); + QModelIndex chIdx=model_->nodeToIndex(chNode); + ExpandStateNode* chExpandNode=expandNode->children_[i]; + + //The expand node exists + if(chExpandNode) + { + //We only set the expand state when the child node is in the current VTree (i.e. is filtered). + //Otherwise we keep the original value + if(chIdx.isValid()) + chExpandNode->setExpanded(view_->isExpanded(chIdx)); + } + // ... create a new child expand node at the i-th place + else + { + chExpandNode=expandNode->setChildAt(i,chNode,view_->isExpanded(chIdx)); + } + + //save all the children recursively + save(chNode,chExpandNode,chIdx); + } +} + +//Collect the modelindexes of the expanded nodes in a VNode subtree. +//This is called after a server scan ended so the structure of the VNode tree and the +//expand tree might not match. In this case the expand tree has to be adjusted to the +//VNode tree. +void ExpandState::collectExpanded(const VNode* node,QSet& theSet) +{ +#ifdef _UI_EXPANDSTATE_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(node); + + if(!root_) + return; + + QModelIndex nodeIdx=model_->nodeToIndex(node); +#ifdef _UI_EXPANDSTATE_DEBUG + UiLog().dbg() << " root=" << root_->name_; +#endif + ExpandStateNode *expand=find(node->absNodePath()); + if(expand) + collectExpanded(expand,node,nodeIdx,theSet); + else + { +#ifdef _UI_EXPANDSTATE_DEBUG + UiLog().dbg() << " Node not found in expand tree"; +#endif + } +} + +void ExpandState::collectExpanded(ExpandStateNode *expand,const VNode* node,const QModelIndex& nodeIdx, + QSet& theSet) +{ +#ifdef _UI_EXPANDSTATE_DEBUG + UI_FUNCTION_LOG +#endif + //The contents of expand node and the vnode might differ. We try to + //adjust the expand node to the vnode with this call. + bool adjusted=expand->adjustContents(node); + + std::size_t numExpand=expand->children_.size(); + std::size_t numNode=node->numOfChildren(); + + UI_ASSERT(numExpand==numNode,"node=" << node->strName() << " numExpand=" << numExpand << + " numNode=" << numNode); + + //Lookup the node in the model + //QModelIndex nodeIdx=model_->nodeToIndex(node); + if(expand->expanded_ && nodeIdx.isValid()) + { + theSet.insert(nodeIdx); + +#ifdef _UI_EXPANDSTATE_DEBUG + UiLog().dbg() << " " << expand->name_; +#endif + } + + //We need to see what to do with newly added nodes. We either expand + //all its children or leave it unexpanded. It depends on the expandedAll_ + //flag set in any of the parents. + bool parentExpandedAll=false; + if(adjusted) + { + parentExpandedAll=needToExpandNewChild(expand,node->absNodePath()); + } + + for(std::size_t i=0; i < numExpand; i++) + { + VNode *chNode=node->childAt(i); + UI_ASSERT(chNode,"chNode is null"); + QModelIndex chIdx=model_->nodeToIndex(chNode); + ExpandStateNode *chExpand=expand->children_[i]; + + //An existing expand node + if(chExpand) + { + UI_ASSERT(chExpand->name_ == chNode->strName()," chExpand->name_=" << chExpand->name_ << " chNode->strName()=" << chNode->strName()); + } + + //A not-yet-allocated expand node. The corresponding vnode has just been + //added to the tree. We have to decide what to do with its expand state!!! + else + { + //create an expand node. Collapsed by default + chExpand=expand->setChildAt(i,chNode,0); + + //save all the children recursively + save(chNode,chExpand,chIdx); + + //expand recursively the new expand node if needed + if(parentExpandedAll) + chExpand->setExpandedRecursively(1); + else + { + UiLog().dbg() << " newly added node not expanded!!"; + } + + } + + collectExpanded(chExpand,chNode,chIdx,theSet); + } +} + + +void ExpandState::saveExpandAll(const VNode* node) +{ + UI_ASSERT(node,"node is null"); + if(ExpandStateNode* expand=find(node->absNodePath())) + { + expand->setExpandedAll(); + } +} + +void ExpandState::saveCollapseAll(const VNode* node) +{ + UI_ASSERT(node,"node is null"); + if(ExpandStateNode* expand=find(node->absNodePath())) + { + expand->setCollapsedAll(); + } +} + +//Find an expand node using its ful path +ExpandStateNode* ExpandState::find(const std::string& fullPath) +{ + if(!root_) + return NULL; + + if(fullPath.empty()) + return NULL; + + if(fullPath == "/") + return root_; + + std::vector pathVec; + boost::split(pathVec,fullPath,boost::is_any_of("/")); + + if(pathVec.size() > 0 && pathVec[0].empty()) + { + pathVec.erase(pathVec.begin()); + } + + return root_->find(pathVec); +} + +bool ExpandState::needToExpandNewChild(ExpandStateNode* expandNode,const std::string& expandNodePath) const +{ + + if(expandNode->expandedAll_ == 1) + return true; + + if(expandNode->collapsedAll_ == 1) + return false; + + //Checks if any of the parents have expandedAll set. In this case we + //expand recursively all the new expand node + std::vector parents; + collectParents(expandNodePath,parents); + for(std::vector::reverse_iterator it=parents.rbegin(); + it != parents.rend(); ++it) + { + if((*it)->expandedAll_ == 1) + { + return true; + } + else if((*it)->collapsedAll_ == 1) + { + return false; + } + } + return false; +} + +//We need to do it this way because we do not store the parents in the nodes for memory efficiency. +void ExpandState::collectParents(const std::string& fullPath,std::vector& parents) const +{ + if(!root_) + return; + + //how split works: + // str="/" -> "","" + // str="/a" -> "","a" + // str=" " -> " " + // str="" -> "" + + std::vector pathVec; + boost::split(pathVec,fullPath,boost::is_any_of("/")); + if(pathVec.size() > 0 && pathVec[0].empty()) + { + pathVec.erase(pathVec.begin()); + } + + ExpandStateNode *p=root_; + std::size_t num=pathVec.size(); + for(std::size_t i=0; i < num && p != 0; i++) + { + parents.push_back(p); + p=p->findChild(pathVec[i]); + } +} + + +void ExpandState::print() const +{ + if(!root_) + return; + + std::string indent=""; + root_->print(indent,true); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ExpandState.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ExpandState.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ExpandState.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ExpandState.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef EXPANDSTATE_HPP_ +#define EXPANDSTATE_HPP_ + +#include +#include + +#include +#include + +//Represents the expand state of a full/part VNode tree. This tree has the same structure +//as a VNode tree and each VNode object is represented by a ExpandStateNode object. + +class TreeNodeModel; +class AbstractNodeView; +class QModelIndex; +class VNode; +class ExpandStateNode; + +class ExpandState +{ + friend class TreeNodeView; + friend class CompactNodeView; + +public: + ExpandState(AbstractNodeView*,TreeNodeModel*); + ~ExpandState(); + + void save(const VNode*); + void collectExpanded(const VNode* node,QSet&); + void saveExpandAll(const VNode* node); + void saveCollapseAll(const VNode* node); + void print() const; + bool isEmpty() const; + +protected: + void init(const VNode *vnode); + void clear(); + ExpandStateNode* root() const {return root_;} + void save(const VNode *,ExpandStateNode*,const QModelIndex&); + void collectExpanded(ExpandStateNode *expand,const VNode* node, + const QModelIndex& nodeIdx,QSet& theSet); + + bool needToExpandNewChild(ExpandStateNode* expandNode,const std::string&) const; + void collectParents(const std::string& fullPath,std::vector& parents) const; + + ExpandStateNode* find(const std::string& fullPath); + + AbstractNodeView* view_; + TreeNodeModel* model_; + ExpandStateNode* root_; +}; + +#endif + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ExpandStateNode.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ExpandStateNode.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ExpandStateNode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ExpandStateNode.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,231 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ExpandStateNode.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "VNode.hpp" + +ExpandStateNode::ExpandStateNode(const VNode* node,bool expanded) : + expanded_(expanded), + expandedAll_(0), + collapsedAll_(0) +{ + name_=node->strName(); + reserveChildren(node->numOfChildren()); +} + +ExpandStateNode::~ExpandStateNode() +{ + clear(); +} + +//Clear the contents and create a new children vector +//with NULLs! +void ExpandStateNode::reset(const VNode* node,bool expanded) +{ + clear(); + name_=node->strName(); + expanded_=expanded; + expandedAll_=0; + collapsedAll_=0; + reserveChildren(node->numOfChildren()); +} + +void ExpandStateNode::clear() +{ + name_.clear(); + expanded_=0; + expandedAll_=0; + collapsedAll_=0; + for(unsigned int i=0; i < children_.size(); i++) + { + delete children_.at(i); + } + children_=std::vector(); +} + +//Create new children vector filled with NULLs +void ExpandStateNode::reserveChildren(std::size_t num) +{ + UI_ASSERT(children_.size() == 0,"children_.size()=" << children_.size()); + + ExpandStateNode *exn=0; + children_=std::vector(); + children_.resize(num,exn); +} + +//Allocate a new child and place it into then children vector at the specified position +ExpandStateNode* ExpandStateNode::setChildAt(std::size_t index,VNode* node,bool expanded) +{ + ExpandStateNode *exn=new ExpandStateNode(node,expanded); + children_[index]=exn; + return exn; +} + +//"Expand all children" was called on the node +void ExpandStateNode::setExpandedAll() +{ + expandedAll_=1; + collapsedAll_=0; + //Set the expand state on all the descendants + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + children_[i]->setExpandedRecursively(1); +} + +//"Collapse all children" was called on the node +void ExpandStateNode::setCollapsedAll() +{ + expandedAll_=0; + collapsedAll_=1; + //Set the expand state on all the descendants + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + children_[i]->setExpandedRecursively(0); +} + +void ExpandStateNode::setExpandedRecursively(unsigned int expanded) +{ + expanded_=expanded; + expandedAll_=0; + collapsedAll_=0; + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + { + children_[i]->setExpandedRecursively(expanded); + } +} + +//Find a descendant +ExpandStateNode* ExpandStateNode::find(const std::vector& pathVec) +{ + if(pathVec.size() == 0) + return this; + + if(pathVec.size() == 1) + { + return findChild(pathVec.at(0)); + } + + std::vector rest(pathVec.begin()+1,pathVec.end()); + ExpandStateNode*n = findChild(pathVec.at(0)); + + return n?n->find(rest):NULL; +} + +//Find a child with the given name +ExpandStateNode* ExpandStateNode::findChild(const std::string& theName) const +{ + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + { + //A child can be NULL temporarily + if(children_[i] && children_[i]->name_ == theName) + return children_[i]; + } + return 0; +} + +//Find a child with the given name. Returns its position as well. +ExpandStateNode* ExpandStateNode::findChild(const std::string& theName,std::size_t& pos) const +{ + pos=-1; + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + { + //A child can be NULL temporarily + if(children_[i] && children_[i]->name_ == theName) + { + pos=i; + return children_[i]; + } + } + return 0; +} + +void ExpandStateNode::print(std::string& indent,bool recursive) const +{ + UiLog().dbg() << indent << name_ << " " << expanded_; + if(recursive) + { + indent+=" "; + std::size_t num=children_.size(); + for(std::size_t i=0; i < num; i++) + children_[i]->print(indent,true); + + indent=indent.substr(0,indent.size()-2); + } +} + +//Adjust the contents of the given expand node to the contents of the vnode it +//represents. Return true if an adjustment was actually needed. +bool ExpandStateNode::adjustContents(const VNode* node) +{ + std::size_t numExpand=children_.size(); + std::size_t numNode=node->numOfChildren(); + + //Check if the children of the expand node and the vnode are the same. + //They migh differ when: + // -the order of the nodes changed + // -nodes were added/removed + bool same=false; + if(numExpand == numNode) + { + same=true; + for(std::size_t i=0; i < numNode; i++) + { + //items in children can be null pointers + if(!children_[i] || children_[i]->name_ != node->childAt(i)->strName()) + { + same=false; + break; + } + } + } + + //the children of the expand node and the vnode are not the same!!! + if(!same) + { + //create a new children vector for the expand node and + //copy all the expand children into it whose name appear in the + //vnode children + std::vector chVec; + for(std::size_t i=0; i < numNode; i++) + { + std::size_t chPos=-1; + if(ExpandStateNode* chExpand=findChild(node->childAt(i)->strName(),chPos)) + { + chVec.push_back(chExpand); + children_[chPos]=0; + } + else + chVec.push_back(0); + } + + //Delete the children vector of the expand node. It eaither contains NULLs or + //children objects which do not exist anymore! + for(std::size_t i=0; i < numExpand; i++) + { + if(children_[i] != 0) + delete children_[i]; + } + + children_.clear(); + + //reassign the children vector to the expand node + children_=chVec; + } + + return !same; +} + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ExpandStateNode.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ExpandStateNode.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ExpandStateNode.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ExpandStateNode.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,65 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef EXPANDSTATENODE_HPP +#define EXPANDSTATENODE_HPP + +#include +#include + +class VNode; + +//Store the expanded state of a node between major updates, server clears, +//status filter changes etc... Each VNode is represented by an ExpandStateNode! +//Note: for memory efficiency we do not store the parent!!! +class ExpandStateNode +{ + friend class ExpandState; + +public: + explicit ExpandStateNode(const VNode* node,bool); + ~ExpandStateNode(); + + void reset(const VNode* node,bool expanded); + void clear(); + ExpandStateNode* setChildAt(std::size_t index,VNode* node,bool + expanded); + void setExpanded(bool expanded) {expanded_=expanded;} + void setExpandedRecursively(unsigned int expanded); + void setExpandedAll(); + void setCollapsedAll(); + + ExpandStateNode* find(const std::vector& pathVec); + ExpandStateNode* findChild(const std::string& name) const; + ExpandStateNode* findChild(const std::string& name,std::size_t& pos) const; + bool adjustContents(const VNode* node); + void print(std::string& indent,bool recursive) const; + +protected: + void reserveChildren(std::size_t num); + + std::vector children_; + std::string name_; + + //Set if the node is expanded + unsigned int expanded_: 1; + + //Set if "Expand all children" was called on the node. + //Cleared when "collaps all children" is called on this node or + //on an ancestor + unsigned int expandedAll_ : 1; + + //Set if "Collaps all children" was called on the node + //Cleared when "expand all children" is called on this node or + //on an ancestor + unsigned int collapsedAll_ : 1; +}; + +#endif // EXPANDSTATENODE_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FileInfoLabel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/FileInfoLabel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FileInfoLabel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FileInfoLabel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,243 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "FileInfoLabel.hpp" + +#include +#include + +#include "TextFormat.hpp" +#include "VFileInfo.hpp" +#include "VReply.hpp" + +FileInfoLabel::FileInfoLabel(QWidget* parent) : QLabel(parent) +{ + //Define id for the css + setProperty("fileInfo","1"); + setWordWrap(true); + + setMargin(2); + setAlignment(Qt::AlignLeft|Qt::AlignVCenter); + + //Other settings + setAutoFillBackground(true); + + setFrameShape(QFrame::StyledPanel); + setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); +} + +void FileInfoLabel::update(VReply* reply,QString extraText) +{ + if(!reply) + { + clear(); + return; + } + + QString labelText; + QString ttText; + QString s; + + QColor col(39,49,101); + QColor colText(30,30,30); + QColor colErr(255,0,0); + + QString fileName=QString::fromStdString(reply->fileName()); + + if(fileName.isEmpty()) + { + s=Viewer::formatBoldText("File: ",col) + Viewer::formatText(" ??? ",colErr); + setText(s); + setToolTip(QString()); + return; + } + + //Name + labelText=Viewer::formatBoldText("File: ",col); + labelText+=fileName; + + s=""; + + //Local read + if(reply->fileReadMode() == VReply::LocalReadMode) + { + VFile_ptr f=reply->tmpFile(); + if(f) + { + VFileInfo fInfo(QString::fromStdString(f->path())); + if(fInfo.exists()) + { + labelText+=Viewer::formatBoldText(" Size: ",col) + + formatFileSize(fInfo.formatSize(),fInfo.size()); + s+=Viewer::formatBoldText(" Modified: ",col.name()) + fInfo.formatModDate(); + + } + } + else + { + VFileInfo f(fileName); + if(f.exists()) + { + labelText+=Viewer::formatBoldText(" Size: ",col); + labelText+=formatFileSize(f.formatSize(),f.size()); + s+=Viewer::formatBoldText(" Modified: ",col) + f.formatModDate(); + } + } + + s+="
      "; + s+=Viewer::formatBoldText("Source: ",col) + " read from disk"; + + if(f) + { + s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); + } + + } + else if(reply->fileReadMode() == VReply::ServerReadMode) + { + VFile_ptr f=reply->tmpFile(); + if(f) + { + if(f->storageMode() == VFile::MemoryStorage) + { + labelText+=Viewer::formatBoldText(" Size: ",col); + labelText+=formatFileSize(VFileInfo::formatSize(f->dataSize()),f->dataSize()); + } + else + { + VFileInfo fInfo(QString::fromStdString(f->path())); + if(fInfo.exists()) + { + labelText+=Viewer::formatBoldText(" Size: ",col); + labelText+=formatFileSize(fInfo.formatSize(),fInfo.size()); + } + } + + s+="
      "; + s+=Viewer::formatBoldText("Source: ",col) + QString::fromStdString(f->fetchModeStr()); + s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); + + int rowLimit=f->truncatedTo(); + if(rowLimit >= 0) + { + s+=" (text truncated to last " + QString::number(rowLimit) + " lines)"; + } + } + else if(reply->status() == VReply::TaskDone) + { + s+="
      "; + s+=Viewer::formatBoldText("Source: ",col) + " fetched from server " + + Viewer::formatBoldText(" at ",col) + formatDate(QDateTime::currentDateTime()); + + int rowLimit=reply->readTruncatedTo(); + if(rowLimit >= 0) + { + s+=" (text truncated to last " + QString::number(rowLimit) + " lines)"; + } + } + else + { + s+="
      Fetch attempted from server" + Viewer::formatBoldText(" at ",col) + + formatDate(QDateTime::currentDateTime()); + } + } + + else if(reply->fileReadMode() == VReply::LogServerReadMode) + { + VFile_ptr f=reply->tmpFile(); + if(f) + { + //Path + size + if(f->storageMode() == VFile::MemoryStorage) + { + labelText+=Viewer::formatBoldText(" Size: ",col); + labelText+=formatFileSize(VFileInfo::formatSize(f->dataSize()),f->dataSize()); + } + else + { + VFileInfo fInfo(QString::fromStdString(f->path())); + if(fInfo.exists()) + { + labelText+=Viewer::formatBoldText(" Size: ",col); + labelText+=formatFileSize(fInfo.formatSize(),fInfo.size()); + } + } + + s+="
      "; + + //Source + s+=Viewer::formatBoldText("Source: ",col); + + if(f->cached()) + { + s+="[from cache] "; + } + s+=QString::fromStdString(f->fetchModeStr()); + s+=" (took " + QString::number(static_cast(f->transferDuration())/1000.,'f',1) + " s)"; + s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); + } + } + + ttText=s; + labelText += ttText; + if(!extraText.isEmpty()) + { + labelText +=" " + extraText + ""; + } + + setText(labelText); +} + +QString FileInfoLabel::formatDate(QDateTime dt) const +{ + QColor col(34,107,138); + QString s=dt.toString("yyyy-MM-dd") + "  " +dt.toString("HH:mm:ss"); + return Viewer::formatBoldText(s,col); +} + +QString FileInfoLabel::formatFileSize(QString str,qint64 size) const +{ + if(size > 10*1024*1024) + return Viewer::formatText(str,QColor(Qt::red)); + return str; +} + +//============================================= +// +// DirInfoLabel +// +//============================================= + +void DirInfoLabel::update(VReply* reply) +{ + QDateTime dt; + if(reply) + { + std::vector dVec=reply->directories(); + if(dVec.empty()) + { + dt=QDateTime::currentDateTime(); + } + //take the last item + else + { + dt=dVec[dVec.size()-1]->fetchDate(); + } + } + else + { + dt=QDateTime::currentDateTime(); + } + + QColor col(39,49,101); + QString s="Directory listing updated at " + Viewer::formatBoldText(" at ",col) + + dt.toString("yyyy-MM-dd HH:mm:ss"); + setText(s); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FileInfoLabel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/FileInfoLabel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FileInfoLabel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FileInfoLabel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,40 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef FILEINFOLABEL_HPP_ +#define FILEINFOLABEL_HPP_ + +#include +#include + +#include "VDir.hpp" + +class VReply; + +class FileInfoLabel : public QLabel +{ +public: + explicit FileInfoLabel(QWidget* parent=0); + + void update(VReply*,QString str=QString()); + QString formatDate(QDateTime) const; + QString formatFileSize(QString,qint64 size) const; +}; + +class DirInfoLabel : public FileInfoLabel +{ +public: + explicit DirInfoLabel(QWidget* parent=0) : FileInfoLabel(parent) {} + + void update(VReply*); + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FileWatcher.cpp ecflow-4.11.1/Viewer/ecflowUI/src/FileWatcher.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FileWatcher.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FileWatcher.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,39 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "FileWatcher.hpp" + +FileWatcher::FileWatcher(const std::string& filePath,qint64 offset,QObject* parent) : + QFileSystemWatcher(parent), + offset_(offset) +{ + connect(this,SIGNAL(fileChanged(QString)), + this,SLOT(slotChanged(QString))); + + file_.setFileName(QString::fromStdString(filePath)); + if (!file_.open(QIODevice::ReadOnly | QIODevice::Text)) + return; + file_.seek(offset_); + + addPath(file_.fileName()); +} + +void FileWatcher::slotChanged(const QString& path) +{ + QStringList lst; + if(path == file_.fileName()) + { + while (!file_.atEnd()) + lst << file_.readLine(); + } + + Q_EMIT linesAppended(lst); + +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FileWatcher.hpp ecflow-4.11.1/Viewer/ecflowUI/src/FileWatcher.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FileWatcher.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FileWatcher.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_FILEWATCHER_HPP_ +#define VIEWER_SRC_FILEWATCHER_HPP_ + +#include +#include +#include + +class FileWatcher : public QFileSystemWatcher +{ +Q_OBJECT + +public: + FileWatcher(const std::string& filePath,qint64 offset,QObject* parent); + +protected Q_SLOTS: + void slotChanged(const QString& path); + +Q_SIGNALS: + void linesAppended(QStringList); + +protected: + QFile file_; + qint64 offset_; +}; + + +#endif /* VIEWER_SRC_FILEWATCHER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FilterWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/FilterWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FilterWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,523 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "FilterWidget.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VNState.hpp" +#include "VAttributeType.hpp" +#include "VConfig.hpp" +#include "VIcon.hpp" +#include "VFilter.hpp" + +#include "ServerItem.hpp" +#include "ServerFilter.hpp" + +//=========================================== +// +// VParamFilterMenu +// +//=========================================== + +VParamFilterMenu::VParamFilterMenu(QMenu * parent,VParamSet* filter,QString title,ItemMode itemMode,DecorMode decorMode) : + menu_(parent), + filter_(filter), + itemMode_(itemMode), + decorMode_(decorMode) +{ + buildTitle(title,parent); + + QAction* acSep = new QAction(this); + acSep->setSeparator(true); + menu_->addAction(acSep); + + //Param name must be unique + for(std::vector::const_iterator it=filter_->all().begin(); it != filter_->all().end(); ++it) + { + addAction((*it)->label(), + (*it)->name()); + } + + //For status + if(decorMode_ == ColourDecor) + { + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(0,1); + + int lighter=150; + bool useStateGrad=true; + if(VProperty* p=VConfig::instance()->find("view.common.node_gradient")) + { + useStateGrad=p->value().toBool(); + } + + QFont f; + QFontMetrics fm(f); + int pixSize=fm.height()-2; + + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator()) + { + if(VNState* vs=VNState::find(ac->data().toString().toStdString())) + { + //Fill rect + QColor bg=vs->colour(); + QColor bgLight=bg.lighter(lighter); + QColor border=bg.darker(125); + QBrush bgBrush; + if(useStateGrad) + { + grad.setColorAt(0,bgLight); + grad.setColorAt(1,bg); + bgBrush=QBrush(grad); + } + else + bgBrush=QBrush(bg); + + QPixmap pix(pixSize,pixSize); + QPainter painter(&pix); + + QRect fillRect(0,0,pixSize,pixSize); + painter.fillRect(fillRect,bgBrush); + painter.setPen(border); + painter.drawRect(fillRect.adjusted(0,0,-1,-1)); + ac->setIcon(pix); + } + } + } + } + + //For icons + else if(decorMode_ == PixmapDecor) + { + QFont f; + QFontMetrics fm(f); + int pixSize=fm.height()-2; + + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator()) + { + if(VIcon* vs=VIcon::find(ac->data().toString().toStdString())) + { + QPixmap pix(vs->pixmap(pixSize)); + ac->setIcon(pix); + } + } + } + } + + QAction *ac = new QAction(this); + ac->setSeparator(true); + menu_->addAction(ac); + + selectAllAc_ = new QAction(this); + if(itemMode_ == FilterMode) + selectAllAc_->setText(tr("Select all")); + else + selectAllAc_->setText(tr("Show all")); + menu_->addAction(selectAllAc_); + connect(selectAllAc_,SIGNAL(triggered(bool)), + this,SLOT(slotSelectAll(bool))); + + unselectAllAc_ = new QAction(this); + if(itemMode_ == FilterMode) + unselectAllAc_->setText(tr("Clear filter")); + else + unselectAllAc_->setText(tr("Hide all")); + menu_->addAction(unselectAllAc_); + connect(unselectAllAc_,SIGNAL(triggered(bool)), + this,SLOT(slotUnselectAll(bool))); + + reload(); +} + +void VParamFilterMenu::buildTitle(QString title,QMenu* parent) +{ + QLabel* titleLabel=new QLabel(title,menu_); + QFont f=menu_->font(); + f.setBold(true); + titleLabel->setFont(f); + titleLabel->setAlignment(Qt::AlignHCenter); + titleLabel->setAutoFillBackground(true); + QPalette pal=titleLabel->palette(); + pal.setColor(QPalette::Window,QColor(237,238,238)); + titleLabel->setPalette(pal); + + int titlePadding=3; + int topMargin=2; + if(parent && parent->isTearOffEnabled()) + { + titlePadding=1; + topMargin=0; + } + + QString titleQss="QLabel {padding: " + QString::number(titlePadding) + "px;}"; + titleLabel->setStyleSheet(titleQss); + + QWidget *w=new QWidget(menu_); + QVBoxLayout *vb=new QVBoxLayout(w); + vb->setContentsMargins(2,topMargin,2,2); + //vb->addSpacing(2); + vb->addWidget(titleLabel); + //vb->addSpacing(2); + + QWidgetAction *titleAc = new QWidgetAction(menu_); + //Qt doc says: the ownership of the widget is passed to the widgetaction. + //So when the action is deleted it will be deleted as well. + titleAc->setDefaultWidget(w); + //titleAc->setEnabled(false); + menu_->addAction(titleAc); +} + + +void VParamFilterMenu::addAction(QString name,QString id) +{ + QAction *ac = new QAction(this); + ac->setText(name); + ac->setData(id); + ac->setCheckable(true); + ac->setChecked(false); + + menu_->addAction(ac); + + //It will not be emitted when setChecked is called! + connect(ac,SIGNAL(triggered(bool)), + this,SLOT(slotChanged(bool))); +} + +void VParamFilterMenu::slotSelectAll(bool) +{ + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator() && + ac->isCheckable()) + { + ac->setChecked(true); + } + } + + slotChanged(true); +} + +void VParamFilterMenu::slotUnselectAll(bool) +{ + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator() && + ac->isCheckable()) + { + ac->setChecked(false); + } + } + + slotChanged(true); +} + +void VParamFilterMenu::slotChanged(bool) +{ + std::vector items; + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator() && + ac->isCheckable() && ac->isChecked()) + { + items.push_back(ac->data().toString().toStdString()); + } + } + + if(filter_) + filter_->setCurrent(items); + + checkActionState(); +} + +void VParamFilterMenu::reload() +{ + Q_FOREACH(QAction* ac,menu_->actions()) + { + if(!ac->isSeparator()) + { + ac->setChecked(filter_->isSet(ac->data().toString().toStdString())); + } + } + checkActionState(); +} + +void VParamFilterMenu::checkActionState() +{ + if(filter_) + { + selectAllAc_->setEnabled(!filter_->isComplete()); + unselectAllAc_->setEnabled(!filter_->isEmpty()); + } +} + +//=========================================== +// +// ServerFilterMenu +// +//=========================================== + +ServerFilterMenu::ServerFilterMenu(QMenu * parent) : + menu_(parent), + filter_(NULL) +{ + loadFont_.setBold(true); + + allMenu_=new QMenu("All servers",menu_); + menu_->addMenu(allMenu_); + + QAction* acFavSep = new QAction(this); + acFavSep->setSeparator(true); + menu_->addAction(acFavSep); + + QAction* acFavTitle = new QAction(this); + acFavTitle->setText(tr("Favourite or loaded servers")); + QFont f=acFavTitle->font(); + f.setBold(true); + acFavTitle->setFont(f); + + menu_->addAction(acFavTitle); + + init(); + + ServerList::instance()->addObserver(this); +} + +ServerFilterMenu::~ServerFilterMenu() +{ + ServerList::instance()->removeObserver(this); + if(filter_) + filter_->removeObserver(this); +} + +void ServerFilterMenu::aboutToDestroy() +{ + ServerList::instance()->removeObserver(this); + if(filter_) + filter_->removeObserver(this); + + clear(); +} + +void ServerFilterMenu::clear() +{ + Q_FOREACH(QAction* ac,acAllMap_) + { + delete ac; + } + acAllMap_.clear(); + + clearFavourite(); +} + +void ServerFilterMenu::clearFavourite() +{ + Q_FOREACH(QAction* ac,acFavMap_) + { + delete ac; + } + acFavMap_.clear(); +} + +void ServerFilterMenu::init() +{ + clear(); + + for(int i=0; i < ServerList::instance()->count(); i++) + { + ServerItem* item=ServerList::instance()->itemAt(i); + QString name=QString::fromStdString(item->name()); + QAction *ac=createAction(name,i); + acAllMap_[name]=ac; + } + + Q_FOREACH(QAction *ac,acAllMap_) + { + allMenu_->addAction(ac); + } + + buildFavourite(); +} + +void ServerFilterMenu::buildFavourite() +{ + clearFavourite(); + + for(int i=0; i < ServerList::instance()->count(); i++) + { + ServerItem* item=ServerList::instance()->itemAt(i); + if(item->isFavourite() || (filter_ && filter_->isFiltered(item))) + { + QString name=QString::fromStdString(item->name()); + acFavMap_[name]=createAction(name,i); + } + } + + Q_FOREACH(QAction *ac,acFavMap_) + { + menu_->addAction(ac); + } + +} + +QAction* ServerFilterMenu::createAction(QString name,int id) +{ + QAction *ac = new QAction(this); + ac->setText(name); + ac->setData(id); + ac->setCheckable(true); + ac->setChecked(false); + + //It will not be emitted when setChecked is called!! + connect(ac,SIGNAL(triggered(bool)), + this,SLOT(slotChanged(bool))); + + return ac; +} + + +void ServerFilterMenu::slotChanged(bool) +{ + if(!filter_) + return; + + if(QAction *ac=static_cast(sender())) + { + if(ac->isSeparator()) return; + + if(ServerItem *item=ServerList::instance()->itemAt(ac->data().toInt())) + { + QString name=ac->text(); + bool checked=ac->isChecked(); + if(checked) + filter_->addServer(item); + else + filter_->removeServer(item); + + //At this point the action (ac) might be deleted so + //we need to use the name and check state for syncing + syncActionState(name,checked); + } + } +} + +void ServerFilterMenu::syncActionState(QString name,bool checked) +{ + QMap::const_iterator it = acAllMap_.find(name); + if(it != acAllMap_.end() && it.value()->isChecked() != checked) + { + //Triggered() will not be called!! + it.value()->setChecked(checked); + } + + it = acFavMap_.find(name); + if(it != acFavMap_.end() && it.value()->isChecked() != checked) + { + //Triggered() will not be called!! + it.value()->setChecked(checked); + } +} + +//Reset actions state when a new filter is loaded +void ServerFilterMenu::reload(ServerFilter *filter) +{ + if(filter_) + filter_->removeObserver(this); + + filter_=filter; + + if(filter_) + filter_->addObserver(this); + + reload(); +} + +//Reset actions state when a new filter is loaded +void ServerFilterMenu::reload() +{ + buildFavourite(); + + QMap::const_iterator it=acAllMap_.constBegin(); + while(it != acAllMap_.constEnd()) + { + if(ServerItem *item=ServerList::instance()->find(it.value()->text().toStdString())) + { + bool current=it.value()->isChecked(); + if(current != filter_->isFiltered(item)) + { + //Triggered() will not be called!! + it.value()->setChecked(!current); + it.value()->setFont(current?font_:loadFont_); + } + } + ++it; + } + + it=acFavMap_.constBegin(); + while(it != acFavMap_.constEnd()) + { + if(ServerItem *item=ServerList::instance()->find(it.value()->text().toStdString())) + { + bool current=it.value()->isChecked(); + if(current != filter_->isFiltered(item)) + { + //Triggered() will not be called!! + it.value()->setChecked(!current); + it.value()->setFont(current?font_:loadFont_); + } + } + ++it; + } +} + +void ServerFilterMenu::notifyServerListChanged() +{ + init(); + reload(); +} + +void ServerFilterMenu::notifyServerListFavouriteChanged(ServerItem* item) +{ + reload(); +} + +void ServerFilterMenu::notifyServerFilterAdded(ServerItem*) +{ + reload(); +} + +void ServerFilterMenu::notifyServerFilterRemoved(ServerItem*) +{ + reload(); +} + +void ServerFilterMenu::notifyServerFilterChanged(ServerItem*) +{ + reload(); +} + +void ServerFilterMenu::notifyServerFilterDelete() +{ + reload(0); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FilterWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/FilterWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FilterWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,100 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef FILTERWIDGET_HPP_ +#define FILTERWIDGET_HPP_ + +#include +#include +#include +#include + +#include "DState.hpp" +#include "ServerFilter.hpp" +#include "ServerList.hpp" + +class QToolButton; +class VParam; +class VParamSet; +class ServerFilter; + + +class VParamFilterMenu : public QObject +{ +Q_OBJECT + +public: + enum DecorMode {NoDecor,ColourDecor,PixmapDecor}; + enum ItemMode {FilterMode,ShowMode}; + + VParamFilterMenu(QMenu* parent,VParamSet* filter,QString title,ItemMode,DecorMode decorMode=NoDecor); + void reload(); + +protected Q_SLOTS: + void slotChanged(bool); + void slotSelectAll(bool); + void slotUnselectAll(bool); + +protected: + void buildTitle(QString,QMenu*); + void addAction(QString name,QString id); + void checkActionState(); + + QMenu* menu_; + VParamSet* filter_; + ItemMode itemMode_; + DecorMode decorMode_; + QAction *selectAllAc_; + QAction *unselectAllAc_; +}; + + +class ServerFilterMenu : public QObject, public ServerListObserver, public ServerFilterObserver +{ +Q_OBJECT + +public: + explicit ServerFilterMenu(QMenu* parent); + ~ServerFilterMenu(); + + void reload(ServerFilter*); + void aboutToDestroy(); //Called when the parent mainwindow is being destroyed + + //From ServerListObserver + void notifyServerListChanged(); + void notifyServerListFavouriteChanged(ServerItem*); + + //From ConfigObserver + void notifyServerFilterAdded(ServerItem*); + void notifyServerFilterRemoved(ServerItem*); + void notifyServerFilterChanged(ServerItem*); + void notifyServerFilterDelete(); + +protected Q_SLOTS: + void slotChanged(bool); + +protected: + void init(); + void clear(); + QAction* createAction(QString name,int id); + void reload(); + void buildFavourite(); + void clearFavourite(); + void syncActionState(QString,bool); + + QMenu* menu_; + QMenu* allMenu_; + QMap acAllMap_; + QMap acFavMap_; + ServerFilter* filter_; + QFont font_; + QFont loadFont_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FlagSet.hpp ecflow-4.11.1/Viewer/ecflowUI/src/FlagSet.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FlagSet.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FlagSet.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef FLAGSET_HPP_ +#define FLAGSET_HPP_ + +template +class FlagSet +{ +public: + FlagSet() : flags_(0) {} + FlagSet(T t) : flags_(0) {set(t);} + + void clear() {flags_=0;} + void set(T flag ) { flags_ |= (1 << flag); } + void unset(T flag ) { flags_ &= ~ (1 << flag); } + bool isSet(T flag) const { return (flags_ >> flag) & 1; } + bool isEmpty() const {return flags_==0;} + bool sameAs(T flag) const {return flags_ == flag;} + +private: + int flags_; + +}; + +#endif // FLAGSET_HPP_ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FontMetrics.cpp ecflow-4.11.1/Viewer/ecflowUI/src/FontMetrics.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FontMetrics.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FontMetrics.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,58 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "FontMetrics.hpp" + +#include +#include + +FontMetrics::FontMetrics(const QFont &font) : + QFontMetrics(font), + realHeight_(height()), + topPadding_(0), + bottomPadding_(0) +{ + computeRealHeight(font); +} + +void FontMetrics::computeRealHeight(QFont f) +{ + QFontMetrics fm(f); + QString txt="Ayfgl"; + QImage img(fm.width(txt)+6,fm.height(),QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::white); + QPainter p(&img); + p.setPen(Qt::black); + f.setBold(true); + p.setFont(f); + p.drawText(QRect(0,0,img.width(),img.height()),Qt::AlignCenter,txt); + + int minRow=img.height()+100; + int maxRow=-1; + for(int i=0; i < img.height(); i++) + for(int j=0; j < img.width(); j++) + { + QRgb c=img.pixel(j,i); + if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) + { + if(i > maxRow) + maxRow=i; + if(i < minRow) + minRow=i; + } + } + + if(minRow >=0 && maxRow < img.height()) + { + realHeight_=maxRow-minRow+1; + topPadding_=minRow; + bottomPadding_=img.height()-1-maxRow; + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/FontMetrics.hpp ecflow-4.11.1/Viewer/ecflowUI/src/FontMetrics.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/FontMetrics.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/FontMetrics.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,34 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef FONTMETRICS_HPP +#define FONTMETRICS_HPP + +#include + +class FontMetrics : public QFontMetrics +{ +public: + FontMetrics(const QFont &font); + int realHeight() const {return realHeight_;} + int topPaddingForCentre() const {return topPadding_;} + int bottomPaddingForCentre() const {return bottomPadding_;} + +protected: + int realHeight_; + int topPadding_; + int bottomPadding_; + +private: + void computeRealHeight(QFont f); +}; + + +#endif // FONTMETRICS_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "GotoLineDialog.hpp" + +#include + +GotoLineDialog::GotoLineDialog(QWidget *parent) : QDialog(parent) +{ + setupUi(this); // this sets up GUI// setupFileMenu(); + + + connect (buttonBox, SIGNAL(accepted()), this, SLOT(done())); + connect (buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + connect (lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(setButtonStatus())); +} + + +GotoLineDialog::~GotoLineDialog() +{ + +} + + +// --------------------------------------------------------------------------- +// GotoLineDialog::setButtonStatus +// if there is text in the input box, then we can activate the 'OK' button, +// otherwise we should disable it. This function is called each time the text +// in the box is changed. +// --------------------------------------------------------------------------- + +void GotoLineDialog::setButtonStatus() +{ + QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); + + if (lineEdit->text().isEmpty()) + { + okButton->setEnabled(false); + } + else + { + okButton->setEnabled(true); + } +} + + +// --------------------------------------------------------------------------- +// GotoLineDialog::setupUIBeforeShow +// sets up UI elements before the dialog is displayed. +// --------------------------------------------------------------------------- + +void GotoLineDialog::setupUIBeforeShow() +{ + lineEdit->setFocus(Qt::OtherFocusReason); + buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); + + setButtonStatus(); +} + + +// --------------------------------------------------------------------------- +// GotoLineDialog::accept +// called when the user clicks the 'OK' button - emits a signal to tell the +// text editor to go to the chosen line +// --------------------------------------------------------------------------- + +void GotoLineDialog::done() +{ + int line = lineEdit->text().toInt(); + Q_EMIT gotoLine(line); + close(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef GotoLineDialog_H +#define GotoLineDialog_H + +#include "ui_GotoLineDialog.h" + +using namespace std; + + +class GotoLineDialog : public QDialog, private Ui::GotoLineDialogQ +{ + Q_OBJECT + +public: + explicit GotoLineDialog(QWidget *parent = 0); + ~GotoLineDialog(); + void setupUIBeforeShow(); + +Q_SIGNALS: + void gotoLine(int line); // emitted when the user says 'ok' + + +public Q_SLOTS: + void done(); + void setButtonStatus(); + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/GotoLineDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/GotoLineDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,92 @@ + + + GotoLineDialogQ + + + + 0 + 0 + 186 + 113 + + + + Dialog + + + + + 10 + 20 + 162 + 79 + + + + + + + + + Goto Line: + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + buttonBox + lineEdit + label + label + + + + + buttonBox + accepted() + Dialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + Dialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Highlighter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/Highlighter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Highlighter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Highlighter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,171 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "Highlighter.hpp" +#include "UserMessage.hpp" +#include "VParam.hpp" + +#include +#include +#include +#include + +#include +#include + +std::string Highlighter::parFile_; + +#include "VProperty.hpp" + + +Highlighter::Highlighter(QTextDocument *parent,QString id) + : QSyntaxHighlighter(parent) +{ + load(id); +} + +void Highlighter::addRule(QString pattern,QTextCharFormat format) +{ + HighlightingRule rule; + rule.pattern = QRegExp(pattern,Qt::CaseSensitive); + rule.format = format; + rules_.append(rule); +} + +void Highlighter::highlightBlock(const QString &text) +{ + Q_FOREACH(HighlightingRule rule, rules_) + { + QRegExp expression(rule.pattern); + int index = text.indexOf(expression); + while (index >= 0) + { + int length = expression.matchedLength(); + setFormat(index, length, rule.format); + index = text.indexOf(expression, index + length); + } + } + setCurrentBlockState(0); +} + +void Highlighter::init(const std::string& parFile) +{ + parFile_=parFile; +} + +void Highlighter::load(QString id) +{ + //Parse param file using the boost JSON property tree parser + using boost::property_tree::ptree; + ptree pt; + + try + { + read_json(parFile_,pt); + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + std::string errorMessage = e.what(); + UserMessage::message(UserMessage::ERROR, true, + std::string("Error! Highlighter::load() unable to parse definition file: " + parFile_ + " Message: " +errorMessage)); + return; + } + + ptree::const_assoc_iterator itTop=pt.find(id.toStdString()); + if(itTop == pt.not_found()) + { + return; + } + + //For each parameter + for(ptree::const_iterator itRule = itTop->second.begin(); itRule != itTop->second.end(); ++itRule) + { + QString pattern; + QTextCharFormat format; + + ptree::const_assoc_iterator itPar; + ptree ptPar=itRule->second; + + if((itPar=ptPar.find("pattern")) !=ptPar.not_found()) + { + pattern=QString::fromStdString(itPar->second.get_value()); + } + if((itPar=ptPar.find("colour")) !=ptPar.not_found()) + { + format.setForeground(VProperty::toColour(itPar->second.get_value())); + } + if((itPar=ptPar.find("bold")) !=ptPar.not_found()) + { + if(itPar->second.get_value() == "true") + format.setFontWeight(QFont::Bold); + } + if((itPar=ptPar.find("italic")) !=ptPar.not_found()) + { + if(itPar->second.get_value() == "true") + format.setFontItalic(true); + } + addRule(pattern,format); + } +} + +void Highlighter::toHtml(QString& html) +{ + // Create a new document from all the selected text document. + QTextCursor cursor(document()); + cursor.select(QTextCursor::Document); + + QTextDocument* tmpDoc(new QTextDocument()); + Q_ASSERT(tmpDoc); + QTextCursor tmpCursor(tmpDoc); + tmpCursor.insertFragment(cursor.selection()); + tmpCursor.select(QTextCursor::Document); + + // Set the default foreground for the inserted characters. + //QTextCharFormat textfmt = tmpCursor.charFormat(); + //textfmt.setForeground(Qt::black); + //tmpCursor.setCharFormat(textfmt); + + // Apply the additional formats set by the syntax highlighter + QTextBlock start = document()->findBlock(cursor.selectionStart()); + QTextBlock end = document()->findBlock(cursor.selectionEnd()); + end = end.next(); + + const int selectionStart = cursor.selectionStart(); + const int endOfDocument = tmpDoc->characterCount() - 1; + for(QTextBlock current = start; current.isValid() and current not_eq end; current = current.next()) + { + const QTextLayout* layout(current.layout()); + + Q_FOREACH(const QTextLayout::FormatRange &range, layout->additionalFormats()) + { + const int start = current.position() + range.start - selectionStart; + const int end = start + range.length; + if(end <= 0 or start >= endOfDocument) + continue; + tmpCursor.setPosition(qMax(start, 0)); + tmpCursor.setPosition(qMin(end, endOfDocument), QTextCursor::KeepAnchor); + tmpCursor.setCharFormat(range.format); + } + } + + // Reset the user states since they are not interesting + for(QTextBlock block = tmpDoc->begin(); block.isValid(); block = block.next()) + block.setUserState(-1); + + // Make sure the text appears pre-formatted, and set the background we want. + tmpCursor.select(QTextCursor::Document); + QTextBlockFormat blockFormat = tmpCursor.blockFormat(); + blockFormat.setNonBreakableLines(true); + blockFormat.setBackground(Qt::black); + tmpCursor.setBlockFormat(blockFormat); + + // Finally retreive the syntax higlighted and formatted html. + html = tmpCursor.selection().toHtml(); + delete tmpDoc; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Highlighter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/Highlighter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Highlighter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Highlighter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef HIGHLIGHTER_HPP_ +#define HIGHLIGHTER_HPP_ + +#include +#include + +class Highlighter : public QSyntaxHighlighter +{ +public: + Highlighter(QTextDocument *parent,QString id); + static void init(const std::string& parFile); + void toHtml(QString& html); + +protected: + void highlightBlock(const QString &text); + void addRule(QString,QTextCharFormat); + +private: + void load(QString); + + struct HighlightingRule + { + QRegExp pattern; + QTextCharFormat format; + }; + + QList rules_; + static std::string parFile_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,198 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "HistoryItemWidget.hpp" + +#include +#include +#include +#include + +#include "LogProvider.hpp" +#include "LogModel.hpp" +#include "VReply.hpp" + +static bool firstRun=true; + +HistoryItemWidget::HistoryItemWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + + searchTb_->setEnabled(false); + searchTb_->setVisible(false); + searchLine_->setVisible(false); + + infoProvider_=new LogProvider(this); + infoProvider_->setAutoUpdate(true); + + model_=new LogModel(this); + + treeView_->setProperty("log","1"); + treeView_->setModel(model_); + treeView_->setItemDelegate(new LogDelegate(this)); + treeView_->setContextMenuPolicy(Qt::ActionsContextMenu); + + //make the horizontal scrollbar work + treeView_->header()->setStretchLastSection(false); + + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + treeView_->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#endif + + checkActionState(); + + //Define context menu + treeView_->addAction(actionCopyEntry_); + treeView_->addAction(actionCopyRow_); +} + +QWidget* HistoryItemWidget::realWidget() +{ + return this; +} + +void HistoryItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + { + return; + } + + clearContents(); + info_=info; + + if(info_) + { + infoProvider_->info(info_); + } +} + +void HistoryItemWidget::clearContents() +{ + InfoPanelItem::clear(); + model_->clearData(); +} + +void HistoryItemWidget::infoReady(VReply* reply) +{ + model_->setData(reply->text()); + adjustColumnSize(); + fileLabel_->update(reply,"(Last 100 lines)"); + treeView_->scrollTo(model_->lastIndex()); + checkActionState(); +} + +void HistoryItemWidget::infoProgress(VReply* reply) +{ + QString s=QString::fromStdString(reply->text()); +} + +void HistoryItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); + + checkActionState(); +} + +void HistoryItemWidget::infoAppended(VReply* reply) +{ + model_->appendData(reply->textVec()); + adjustColumnSize(); + treeView_->scrollTo(model_->lastIndex()); + + checkActionState(); +} + +void HistoryItemWidget::updateState(const ChangeFlags& flags) +{ + if(flags.isSet(SelectedChanged)) + { + if(!selected_) + { + infoProvider_->setAutoUpdate(false); + reloadTb_->setEnabled(false); + return; + } + } + + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + infoProvider_->setAutoUpdate(false); + reloadTb_->setEnabled(false); + return; + } + + } + + checkActionState(); +} + +void HistoryItemWidget::checkActionState() +{ + if(suspended_) + return; + + if(infoProvider_->autoUpdate() == frozen_) + { + infoProvider_->setAutoUpdate(!frozen_); + } + reloadTb_->setEnabled(!infoProvider_->autoUpdate() || !infoProvider_->inAutoUpdate()); +} + +//Adjust column size if it is the first run +void HistoryItemWidget::adjustColumnSize() +{ + if(firstRun && model_->hasData()) + { + firstRun=false; + for(int i=0; i < model_->columnCount()-1; i++) + { + treeView_->resizeColumnToContents(i); + } + } +} + +void HistoryItemWidget::on_reloadTb__clicked(bool) +{ + if(info_ && info_.get()) + { + infoProvider_->info(info_); + } +} + +void HistoryItemWidget::on_actionCopyEntry__triggered() +{ + toClipboard(model_->entryText(treeView_->currentIndex())); +} + +void HistoryItemWidget::on_actionCopyRow__triggered() +{ + toClipboard(model_->fullText(treeView_->currentIndex())); +} + +void HistoryItemWidget::toClipboard(QString txt) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif +} + +static InfoPanelItemMaker maker1("history"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,56 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef HISTORYITEMWIDGET_HPP_ +#define HISTORYITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" + +#include "ServerHandler.hpp" + +#include "ui_HistoryItemWidget.h" + +class LogModel; + +class HistoryItemWidget : public QWidget, public InfoPanelItem, protected Ui::HistoryItemWidget +{ +Q_OBJECT + +public: + explicit HistoryItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + void infoAppended(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected Q_SLOTS: + void on_reloadTb__clicked(bool); + void on_actionCopyEntry__triggered(); + void on_actionCopyRow__triggered(); + +protected: + void updateState(const ChangeFlags&); + void adjustColumnSize(); + void checkActionState(); + void toClipboard(QString txt) const; + + LogModel* model_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/HistoryItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HistoryItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,170 @@ + + + HistoryItemWidget + + + + 0 + 0 + 510 + 465 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show search bar + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + Refresh log + + + ... + + + + :/viewer/sync.svg:/viewer/sync.svg + + + true + + + + + + + + + + + + QAbstractItemView::SingleSelection + + + false + + + true + + + true + + + + + + + + + + Copy text of &Entry + + + Copy text of the log entry + + + Ctrl+C + + + + + Copy text of full &row + + + Copy text of full row + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + PlainTextSearchLine + QWidget +
      PlainTextSearchLine.hpp
      + 1 +
      + + TreeView + QTreeView +
      TreeView.hpp
      +
      + + FileInfoLabel + QLabel +
      FileInfoLabel.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HtmlEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/HtmlEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HtmlEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HtmlEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,80 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "HtmlEdit.hpp" + +#include +#include + +#include "UiLog.hpp" + +HtmlEdit::HtmlEdit(QWidget * parent) : + QTextBrowser(parent), + fontProp_(0) +{ +#if 0 + QFont f("Courier"); + //QFont f("Monospace"); + //f.setStyleHint(QFont::TypeWriter); + f.setFixedPitch(true); + f.setPointSize(10); + //f.setStyleStrategy(QFont::PreferAntialias); + setFont(f); +#endif +} + +HtmlEdit::~HtmlEdit() +{ + if(fontProp_) + fontProp_->removeObserver(this); +} + +//--------------------------------------------- +// Fontsize management +//--------------------------------------------- + +void HtmlEdit::setFontProperty(VProperty* p) +{ + fontProp_=p; + fontProp_->addObserver(this); + updateFont(); +} + +void HtmlEdit::wheelEvent(QWheelEvent *event) +{ + int fps=font().pointSize(); + + QTextBrowser::wheelEvent(event); + if(font().pointSize() != fps) + fontSizeChangedByZoom(); +} + +void HtmlEdit::fontSizeChangedByZoom() +{ + if(fontProp_) + fontProp_->setValue(font()); +} + +void HtmlEdit::updateFont() +{ + if(fontProp_) + { + QFont f=fontProp_->value().value(); + if(font() != f) + setFont(f); + } +} + +void HtmlEdit::notifyChange(VProperty* p) +{ + if(fontProp_ ==p) + { + setFont(p->value().value()); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HtmlEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/HtmlEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HtmlEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HtmlEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,38 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef HTMLEDIT_HPP +#define HTMLEDIT_HPP + +#include + +#include "VProperty.hpp" + +class HtmlEdit : public QTextBrowser, public VPropertyObserver +{ +public: + explicit HtmlEdit(QWidget* parent = 0); + ~HtmlEdit(); + + void setFontProperty(VProperty* p); + void updateFont(); + void notifyChange(VProperty* p); + +protected: + void wheelEvent(QWheelEvent *event); + +private: + void fontSizeChangedByZoom(); + + VProperty *fontProp_; +}; + +#endif // HTMLEDIT_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,67 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "HtmlItemWidget.hpp" + +#include +#include + +HtmlItemWidget::HtmlItemWidget(QWidget *parent) : + QWidget(parent) +{ + setupUi(this); + + externalTb_->hide(); + + fileLabel_->setProperty("fileInfo","1"); + + searchLine_->setEditor(textEdit_); + searchLine_->setVisible(false); + + textEdit_->setOpenExternalLinks(false); + textEdit_->setOpenLinks(false); + textEdit_->setReadOnly(true); +} + +HtmlItemWidget::~HtmlItemWidget() +{ +} + +void HtmlItemWidget::removeSpacer() +{ + //Remove the first spacer item!! + for(int i=0; horizontalLayout->count(); i++) + { + if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) + { + horizontalLayout->takeAt(i); + delete sp; + break; + } + } +} + +void HtmlItemWidget::on_searchTb__clicked() +{ + searchLine_->setVisible(true); + searchLine_->setFocus(); + searchLine_->selectAll(); +} + + +void HtmlItemWidget::on_fontSizeUpTb__clicked() +{ + textEdit_->slotZoomIn(); +} + +void HtmlItemWidget::on_fontSizeDownTb__clicked() +{ + textEdit_->slotZoomOut(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,38 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef HTMLITEMWIDGET_HPP +#define HTMLITEMWIDGET_HPP + +#include + +#include "ui_HtmlItemWidget.h" + +class HtmlItemWidget : public QWidget, protected Ui::HtmlItemWidget +{ +Q_OBJECT + +public: + explicit HtmlItemWidget(QWidget *parent=0); + ~HtmlItemWidget(); + +protected Q_SLOTS: + void on_searchTb__clicked(); + void on_fontSizeUpTb__clicked(); + void on_fontSizeDownTb__clicked(); + +Q_SIGNALS: + void editorFontSizeChanged(); + +protected: + void removeSpacer(); +}; + +#endif // HTMLITEMWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/HtmlItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/HtmlItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,180 @@ + + + HtmlItemWidget + + + + 0 + 0 + 510 + 465 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + false + + + QFrame::StyledPanel + + + + + + 2 + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg + + + Ctrl++ + + + true + + + + + + + Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg + + + Ctrl+- + + + true + + + + + + + ... + + + + + + + Show search bar (CTRL-F) + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + + + + + + + + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + FileInfoLabel + QLabel +
      FileInfoLabel.hpp
      +
      + + RichTextSearchLine + QWidget +
      RichTextSearchLine.hpp
      + 1 +
      + + RichTextEdit + QTextBrowser +
      RichTextEdit.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,833 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "InfoPanel.hpp" + +#include +#include +#include +#include + +#include "DashboardDock.hpp" +#include "InfoPanelItem.hpp" +#include "InfoPanelHandler.hpp" +#include "NodePathWidget.hpp" +#include "ServerHandler.hpp" +#include "SessionHandler.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "VSettings.hpp" +#include "WidgetNameProvider.hpp" + +//#define _UI_INFOPANEL_DEBUG + +//============================================== +// +// InfoPanelItemHandler +// +//============================================== + +QWidget* InfoPanelItemHandler::widget() +{ + return item_->realWidget(); +} + +bool InfoPanelItemHandler::match(const std::vector& ids) const +{ + return (std::find(ids.begin(),ids.end(),def_) != ids.end()); +} + +void InfoPanelItemHandler::addToTab(QTabWidget *tab) +{ + int idx=tab->addTab(item_->realWidget(),QString::fromStdString(def_->label())); + tab->setTabIcon(idx,QPixmap(":/viewer/" + QString::fromStdString(def_->icon()))); +} + +//============================================== +// +// InfoPanel +// +//============================================== + +InfoPanel::InfoPanel(QWidget* parent) : + DashboardWidget("info",parent), + tabBeingCleared_(false), + tabBeingAdjusted_(false) +{ + setupUi(this); + + bcWidget_=new NodePathWidget(this); + + connect(tab_,SIGNAL(currentChanged(int)), + this,SLOT(slotCurrentWidgetChanged(int))); + + connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), + this,SLOT(slotReloadFromBc(VInfo_ptr))); + + tab_->setIconSize(QSize(16,16)); + + messageLabel_->hide(); + + //Initialise action state + actionBreadcrumbs_->setChecked(bcWidget_->isGuiMode()); + actionFrozen_->setChecked(false); + + WidgetNameProvider::nameChildren(this); + + //Create the handler for all the possible panels! + for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); + it != InfoPanelHandler::instance()->panels().end(); ++it) + { + createHandler(*it); + } +} + +InfoPanel::~InfoPanel() +{ + localClear(); + + Q_FOREACH(InfoPanelItemHandler *d,items_) + delete d; +} + +QMenu* InfoPanel::buildOptionsMenu() +{ + QMenu *menu=new QMenu(this); + menu->setTearOffEnabled(true); + + menu->addAction(actionBreadcrumbs_); + menu->addAction(actionFrozen_); + + return menu; +} + +//When the infopanel is in a dockwidget we add the option actions +//to the dockwidget title bar widget +void InfoPanel::populateDockTitleBar(DashboardDockTitleWidget* tw) +{ + QMenu *menu=buildOptionsMenu(); + + //Sets the menu on the toolbutton + tw->optionsTb()->setMenu(menu); + + //Add the bc to the titlebar. This will reparent the bcWidget!!! So we must not + //access it in the destructor!!! + tw->setBcWidget(bcWidget_); +} + +//When the infopanel is in a dialog we need to add the optionsTb to the dialog. +void InfoPanel::populateDialog() +{ + setInDialog(true); + + //Add the bcWidget_ to the top of the dialogue + bcWidget_->useTransparentBg(false); + verticalLayout_->insertWidget(0,bcWidget_); + + QMenu *menu=buildOptionsMenu(); + + QWidget *cornerW=new QWidget(this); + QHBoxLayout *hb=new QHBoxLayout(cornerW); + hb->setContentsMargins(0,0,0,0); + hb->setSpacing(1); + + QToolButton *detachedTb=new QToolButton(this); + detachedTb->setAutoRaise(true); + detachedTb->setDefaultAction(detachedAction_); + hb->addWidget(detachedTb); + setDetached(true); //by default a dialog is detached! + + QToolButton* optionsTb=new QToolButton(this); + optionsTb->setAutoRaise(true); + optionsTb->setIcon(QPixmap(":/viewer/cogwheel.svg")); + optionsTb->setPopupMode(QToolButton::InstantPopup); + optionsTb->setToolTip(tr("Options")); + optionsTb->setMenu(menu); + hb->addWidget(optionsTb); + + tab_->setCornerWidget(cornerW); + + //This will set the dialog title + updateTitle(); +} + +void InfoPanel::setCurrent(const std::string& name) +{ + for(int i=0; i < tab_->count(); i++) + { + if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) + { + //Clear the contents + if(d->def() && d->def()->name() == name) + { + tab_->setCurrentIndex(i); + } + } + } +} + +void InfoPanel::clear() +{ + localClear(); + + //Clear the breadcrumbs + bcWidget_->clear(); +} + +//This is safe to call from the destructor +void InfoPanel::localClear() +{ + messageLabel_->hide(); + messageLabel_->clear(); + + //Unregister from observer lists + if(info_ && info_.get()) + { + if(info_->server()) + { + info_->server()->removeServerObserver(this); + } + + info_->removeObserver(this); + } + + //release info + info_.reset(); + + //Clear the tab contents + for(int i=0; i < tab_->count(); i++) + { + if(InfoPanelItem* item=findItem(tab_->widget(i))) + { + //Diable and clear the contents + item->setActive(false); + } + } + //Clear the tabs + clearTab(); +} + +//TODO: It should be the slot +void InfoPanel::reset(VInfo_ptr info) +{ + if(info_ && info) + { + //UiLog().dbg() << "path: " << info_->path() << " " << info->path(); + + if(*(info_.get()) == *(info.get())) + return; + + //it can happen that the stored info was not yet updated after a + //server reload. If there is chance for it we try to regain its data and + //comapare it again to the incoming node + else if(info_->server() == info->server() && + info_->storedNodePath() == info->storedNodePath() && + !info_->node() && info->node()) + { + info_->regainData(); + if(info_->node() == info->node()) + return; + } + } + + messageLabel_->hide(); + messageLabel_->clear(); + + //Set info + adjustInfo(info); + + //Set tabs + adjustTabs(info); + + //Set breadcrumbs + bcWidget_->setPath(info); + + updateTitle(); +} + +//This slot is called when the info object is selected in another panel +void InfoPanel::slotReload(VInfo_ptr info) +{ + //When the mode is detached it cannot receive + //the reload request + if(info_ && detached()) + return; + + if(info && info->isAttribute()) + { + reset(VInfo::createParent(info)); + } + else + { + reset(info); + } +} + +void InfoPanel::slotReloadFromBc(VInfo_ptr info) +{ + reset(info); + if(info_) + Q_EMIT selectionChanged(info_); +} + +void InfoPanel::linkSelected(VInfo_ptr info) +{ + //Here info can be an attribute! + slotReload(info); + if(info_ && info) + Q_EMIT selectionChanged(info); +} + +//Set the new VInfo object. +//We also we need to manage the node observers. The InfoItem +//will be the observer of the server of the object stored in +//the new VInfo +void InfoPanel::adjustInfo(VInfo_ptr info) +{ + //Check if there is data in info + if(info) + { + ServerHandler *server=info->server(); + + bool sameServer=(info_)?(info_->server() == server):false; + + //Handle observers + if(!sameServer) + { + if(info_ && info_->server()) + { + info_->server()->removeServerObserver(this); + //info_->server()->removeNodeObserver(this); + } + + info->server()->addServerObserver(this); + //info->server()->addNodeObserver(this); + } + } + //If the there is no data we clean everything and return + else + { + if(info_ && info_->server()) + { + info_->server()->removeServerObserver(this); + //info_->server()->removeNodeObserver(this); + } + } + + //Set the info + if(info_) + { + info_->removeObserver(this); + } + + info_=info; + + if(info_) + { + info_->addObserver(this); + } + +} + +void InfoPanel::adjustTabs(VInfo_ptr info) +{ + //Set tabs according to the current set of roles + std::vector ids; + InfoPanelHandler::instance()->visible(info,ids); + +#ifdef _UI_INFOPANEL_DEBUG + for(int i=0; i < ids.size(); i++) + { + UiLog().dbg() << "InfoPanel --> tab: " << ids[i]->name(); + } +#endif + + int match=0; + for(int i=0; i < tab_->count(); i++) + { + if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) + { + //We only keep the tab as it is when all these match: + //1. it is a server tab that must always be visible whatever + //node is selected + //2. the server data in the tab has to be kept (i.e. static) + //3. the server in the new info object is the same as in the tab + + //Sanity check + if(d->item()->keepServerDataOnLoad()) + { + UI_ASSERT(d->match(ids),"d=" << d->def()->name()) ; + } + + //Disable and force to clear the contents when it is not the case + //described above + if(!(d->item()->keepServerDataOnLoad() && + d->item()->hasSameContents(info))) + { + d->item()->setActive(false); + } + + if(d->match(ids)) + match++; + } + } + + //Remember the current widget + QWidget *current=tab_->currentWidget(); + InfoPanelItem* currentItem=findItem(current); + + //A new set of tabs is needed! + if(tab_->count() != static_cast(ids.size()) || match != static_cast(ids.size())) + { + //We set this flag true so that the change of the current tab should not + //trigger a reload! We want to reload the current tab only after the + //tab adjustment. + tabBeingAdjusted_=true; + + //Remove the pages but does not delete them. + clearTab(); + + for(std::vector::iterator it=ids.begin(); it != ids.end(); ++it) + { + if(InfoPanelItemHandler* d=findHandler(*it)) + { + d->addToTab(tab_); + d->item()->setActive(true); + } + } + + //Try to set the previous current widget as current again + currentItem=0; + bool hasCurrent=false; + for(int i=0 ; i < tab_->count(); i++) + { + if(tab_->widget(i) == current) + { + tab_->setCurrentIndex(i); + currentItem=findItem(current); + hasCurrent=true; + break; + } + } + //If the current widget is not present select the first + if(!hasCurrent && tab_->count() >0) + { + tab_->setCurrentIndex(0); + currentItem=findItem(tab_->widget(0)); + } + + tabBeingAdjusted_=false; + } + + //We use the same set of tabs + else + { + for(int i=0; i < tab_->count(); i++) + { + if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) + { + d->item()->setActive(true); + } + } + } + + //We reload the current tab + if(currentItem) + { + currentItem->setSelected(true,info); + //currentItem->reload(info); + } +} + +InfoPanelItem* InfoPanel::findItem(QWidget* w) +{ + if(!w) + return 0; + + Q_FOREACH(InfoPanelItemHandler *d,items_) + { + if(d->widget() == w) + return d->item(); + } + + return 0; +} + +InfoPanelItemHandler* InfoPanel::findHandler(QWidget* w) +{ + if(!w) + return 0; + + Q_FOREACH(InfoPanelItemHandler *d,items_) + { + if(d->widget() == w) + return d; + } + + return 0; +} + +InfoPanelItemHandler* InfoPanel::findHandler(InfoPanelDef* def) +{ + Q_FOREACH(InfoPanelItemHandler *d,items_) + { + if(d->def() == def) + return d; + } + + return createHandler(def); +} + +InfoPanelItemHandler* InfoPanel::createHandler(InfoPanelDef* def) +{ + if(InfoPanelItem *iw=InfoPanelItemFactory::create(def->name())) + { + WidgetNameProvider::nameChildren(iw->realWidget()); + iw->setOwner(this); + iw->setFrozen(frozen()); + iw->setDetached(detached()); + + //iw will be added to the tab so the tab will be its parent. Moreover + //the tab will stay its parent even if iw got removed from the tab! + //So when the tab is deleted all the iw-s will be correctly deleted as well. + + InfoPanelItemHandler* h=new InfoPanelItemHandler(def,iw); + items_ << h; + return h; + } + return 0; +} + +//We clicked on another tab +void InfoPanel::slotCurrentWidgetChanged(int idx) +{ + if(tabBeingCleared_ || tabBeingAdjusted_) + return; + + if(!info_.get()) + return; + + if(InfoPanelItem* current=findItem(tab_->widget(idx))) + { + current->setSelected(true,info_); + + //Reload the item if it is needed + /*if(!current->isSuspended() && !current->info()) + current->reload(info_);*/ + + //Deselect the others + for(int i=0; i < tab_->count(); i++) + { + if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) + { + if(d->item() != current) + d->item()->setSelected(false,info_); + } + } + } +} + +void InfoPanel::clearTab() +{ + tabBeingCleared_=true; + tab_->clear(); + tabBeingCleared_=false; +} + +void InfoPanel::detachedChanged() +{ + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->setDetached(detached()); + } + updateTitle(); +} + +void InfoPanel::on_actionBreadcrumbs__toggled(bool b) +{ + if(isInDialog()) + { + bcWidget_->setVisible(b); + } + else + { + if(b) + { + bcWidget_->setMode(NodePathWidget::GuiMode); + } + else + { + bcWidget_->setMode(NodePathWidget::TextMode); + } + } +} + +void InfoPanel::on_actionFrozen__toggled(bool b) +{ + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->setFrozen(b); + } + updateTitle(); +} + +bool InfoPanel::frozen() const +{ + return actionFrozen_->isChecked(); +} + +void InfoPanel::updateTitle() +{ + if(isInDialog()) + { + QString txt; + if(frozen()) + txt+="(frozen) "; + + if(info_) + { + txt+=QString::fromStdString(info_->path()); + } + + Q_EMIT titleUpdated(txt); + } +} + +void InfoPanel::relayInfoPanelCommand(VInfo_ptr info,QString cmd) +{ + Q_EMIT popInfoPanel(info,cmd); +} + +void InfoPanel::relayDashboardCommand(VInfo_ptr info,QString cmd) +{ + Q_EMIT dashboardCommand(info,cmd); +} + +void InfoPanel::notifyDataLost(VInfo* info) +{ + if(info_ && info_.get() == info) + { + clear(); + } +} + +//------------------------------------------------- +// ServerObserver methods +//------------------------------------------------- + +void InfoPanel::notifyDefsChanged(ServerHandler *server, const std::vector& aspect) +{ + if(frozen()) + return; + + if(info_) + { + if(info_->server() && info_->server() == server) + { + //Dispatch the change + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->defsChanged(aspect); + } + } + } +} + +void InfoPanel::notifyServerDelete(ServerHandler* server) +{ + if(info_ && info_->server() == server) + { + clear(); + } +} + +//This must be called at the beginning of a reset +void InfoPanel::notifyBeginServerClear(ServerHandler* server) +{ + if(info_) + { + if(info_->server() && info_->server() == server) + { + messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + " is being reloaded. \ + Until it is finished only limited functionalty is avaliable in the Info Panel!"); + + messageLabel_->startLoadLabel(); + + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->setSuspended(true,info_); + } + } + } +} + +//This must be called at the end of a reset +void InfoPanel::notifyEndServerScan(ServerHandler* server) +{ + if(info_) + { + if(info_->server() && info_->server() == server) + { + messageLabel_->hide(); + messageLabel_->clear(); + + //We try to ressurect the info. We have to do it explicitly because it is not guaranteed + //that notifyEndServerScan() will be first called on the VInfo then on the InfoPanel. So it + //is possible that the node exists but is still set to NULL in VInfo. + info_->regainData(); + + //If the info is not available dataLost() might have already been called and + //the panel was reset! + if(!info_) + return; + + Q_ASSERT(info_->server() && info_->node()); + + //Otherwise we resume all the tabs + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->setSuspended(false,info_); + } + } + } +} + +void InfoPanel::notifyServerConnectState(ServerHandler* server) +{ + if(frozen()) + return; + + if(info_) + { + if(info_->server() && info_->server() == server) + { + //Dispatch the change + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->connectStateChanged(); + } + } + } +} + +void InfoPanel::notifyServerSuiteFilterChanged(ServerHandler* server) +{ + //TODO: does frozen make sense in this case? + if(frozen()) + return; + + if(info_) + { + if(info_->server() && info_->server() == server) + { + //Dispatch the change + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->suiteFilterChanged(); + } + } + } +} + +void InfoPanel::notifyEndServerSync(ServerHandler* server) +{ + //TODO: does frozen make sense in this case? + if(frozen()) + return; + + if(info_) + { + if(info_->server() && info_->server() == server) + { + //Dispatch the change + Q_FOREACH(InfoPanelItemHandler *item,items_) + { + item->item()->serverSyncFinished(); + } + } + } +} + +void InfoPanel::rerender() +{ + bcWidget_->rerender(); +} + +void InfoPanel::writeSettings(VComboSettings* vs) +{ + vs->put("type",type_); + vs->put("dockId",id_); + + bcWidget_->writeSettings(vs); + + vs->putAsBool("frozen",frozen()); + + DashboardWidget::writeSettings(vs); + + Q_FOREACH(InfoPanelItemHandler *d,items_) + { + if(d->item()) + d->item()->writeSettings(vs); + } +} + +void InfoPanel::readSettings(VComboSettings* vs) +{ + std::string type=vs->get("type",""); + if(type != type_) + { + return; + } + + //-------------------------- + //Breadcrumbs + //-------------------------- + + bcWidget_->readSettings(vs); + + //Synchronise the action and the breadcrumbs state + //This will not emit the trigered signal of the action!! + actionBreadcrumbs_->setChecked(bcWidget_->isGuiMode()); + + actionFrozen_->setChecked(vs->getAsBool("frozen",frozen())); + + DashboardWidget::readSettings(vs); + + Q_FOREACH(InfoPanelItemHandler *d,items_) + { + if(d->item()) + d->item()->readSettings(vs); + } +} + +void InfoPanel::writeSettingsForDialog() +{ + SessionItem* cs=SessionHandler::instance()->current(); + assert(cs); + VSettings vs(cs->infoPanelDialogFile()); + + vs.putAsBool("breadcrumbs",bcWidget_->isVisible()); + vs.putAsBool("frozen",frozen()); + vs.putAsBool("detached",detached()); + vs.write(); +} + +void InfoPanel::readSettingsForDialog() +{ + SessionItem* cs=SessionHandler::instance()->current(); + assert(cs); + VSettings vs(cs->infoPanelDialogFile()); + vs.read(false); + + actionBreadcrumbs_->setChecked(vs.getAsBool("breadcrumbs",true)); + bcWidget_->setVisible(actionBreadcrumbs_->isChecked()); + + actionFrozen_->setChecked(vs.getAsBool("frozen",frozen())); + detachedAction_->setChecked(vs.getAsBool("detached",detached())); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,133 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "InfoPanelHandler.hpp" + + +#include +#include +#include + +#include "NodeExpression.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" + +InfoPanelHandler* InfoPanelHandler::instance_=0; + + +InfoPanelDef::InfoPanelDef(const std::string& name) : + name_(name), + hidden_(false), + visibleCondition_(0), + enabledCondition_(0) +{ +} + +InfoPanelHandler::InfoPanelHandler() +{ +} + +InfoPanelHandler* InfoPanelHandler::instance() +{ + if(!instance_) + instance_=new InfoPanelHandler(); + + return instance_; +} + +void InfoPanelHandler::init(const std::string &configFile) +{ + // parse the response using the boost JSON property tree parser + + using boost::property_tree::ptree; + ptree pt; + + try + { + read_json(configFile, pt); + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + std::string errorMessage = e.what(); + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse JSON menu file : " + errorMessage)); + return; + } + + + // iterate over the top level of the tree + for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) + { + if (itTopLevel->first == "info_panel") + { + UiLog().dbg() << "Panels:"; + + ptree const &panelsPt = itTopLevel->second; + + // iterate through all the panels + for (ptree::const_iterator itPanel = panelsPt.begin(); itPanel != panelsPt.end(); ++itPanel) + { + ptree const &panelPt = itPanel->second; + + std::string cname = panelPt.get("name", ""); + + UiLog().dbg() << " " << cname; + + InfoPanelDef* def= new InfoPanelDef(cname); + + def->setLabel(panelPt.get("label","")); + def->setIcon(panelPt.get("icon","")); + def->setDockIcon(panelPt.get("dock_icon","")); + def->setShow(panelPt.get("show","")); + def->setTooltip(panelPt.get("tooltip","")); + def->setButtonTooltip(panelPt.get("button_tooltip","")); + + std::string enabled = panelPt.get("enabled_for", ""); + std::string visible = panelPt.get("visible_for", ""); + + if(panelPt.get("hidden", "") == "1") + { + def->setHidden(true); + } + + BaseNodeCondition *enabledCond = NodeExpressionParser::instance()->parseWholeExpression(enabled); + if (enabledCond == NULL) + { + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse enabled condition: " + enabled)); + enabledCond = new FalseNodeCondition(); + } + def->setEnabledCondition(enabledCond); + + + BaseNodeCondition *visibleCond = NodeExpressionParser::instance()->parseWholeExpression(visible); + if (visibleCond == NULL) + { + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse visible condition: " + visible)); + visibleCond = new FalseNodeCondition(); + } + def->setVisibleCondition(visibleCond); + + panels_.push_back(def); + + } + } + } +} + +void InfoPanelHandler::visible(VInfo_ptr info,std::vector& lst) +{ + if(!info || !info.get()) + return; + + for(std::vector::const_iterator it=panels_.begin(); it != panels_.end(); ++it) + { + if(!(*it)->hidden() && (*it)->visibleCondition()->execute(info)) + lst.push_back((*it)); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,77 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INFOPANELHANDLER_HPP_ +#define INFOPANELHANDLER_HPP_ + +#include +#include + +#include "VInfo.hpp" + +class BaseNodeCondition; + +class InfoPanelDef +{ +public: + explicit InfoPanelDef(const std::string&); + + std::string name() const {return name_;} + std::string label() const {return label_;} + std::string icon() const {return icon_;} + std::string dockIcon() const {return dockIcon_;} + std::string show() const {return show_;} + std::string tooltip() const {return tooltip_;} + std::string buttonTooltip() const {return buttonTooltip_;} + bool hidden() const {return hidden_;} + BaseNodeCondition* visibleCondition() const {return visibleCondition_;} + + void setLabel(const std::string& s) {label_=s;} + void setIcon(const std::string& s) {icon_=s;} + void setDockIcon(const std::string& s) {dockIcon_=s;} + void setShow(const std::string& s) {show_=s;} + void setTooltip(const std::string& tooltip) {tooltip_=tooltip;} + void setButtonTooltip(const std::string& tooltip) {buttonTooltip_=tooltip;} + void setVisibleCondition(BaseNodeCondition *visibleCond) {visibleCondition_=visibleCond;} + void setEnabledCondition(BaseNodeCondition *enabledCond) {enabledCondition_=enabledCond;} + void setHidden(bool b) {hidden_=b;} + +protected: + std::string name_; + std::string label_; + std::string icon_; + std::string dockIcon_; + std::string show_; + std::string tooltip_; + std::string buttonTooltip_; + bool hidden_; + + BaseNodeCondition *visibleCondition_; + BaseNodeCondition *enabledCondition_; +}; + +class InfoPanelHandler +{ +public: + InfoPanelHandler(); + + void init(const std::string& file); + void visible(VInfo_ptr info,std::vector& lst); + const std::vector& panels() const {return panels_;} + + static InfoPanelHandler* instance(); + +protected: + static InfoPanelHandler* instance_; + + std::vector panels_; + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,126 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INFOPANEL_HPP_ +#define INFOPANEL_HPP_ + +#include + +#include "DashboardWidget.hpp" +#include "ServerObserver.hpp" +#include "VInfo.hpp" + +#include "ui_InfoPanel.h" + +class QMenu; +class QTabWidget; + +class DashboardDockTitleWidget; +class InfoPanel; +class InfoPanelDef; +class InfoPanelItem; + +class InfoPanelItemHandler +{ +friend class InfoPanel; + +public: + InfoPanelItemHandler(InfoPanelDef* def,InfoPanelItem* item) : + def_(def), item_(item) {} + + bool match(const std::vector& defs) const; + InfoPanelItem* item() const {return item_;} + QWidget* widget(); + InfoPanelDef* def() const {return def_;} + +protected: + void addToTab(QTabWidget *); + +private: + InfoPanelDef* def_; + InfoPanelItem* item_; +}; + + +class InfoPanel : public DashboardWidget, public ServerObserver, public VInfoObserver, private Ui::InfoPanel +{ + Q_OBJECT + +public: + explicit InfoPanel(QWidget* parent=0); + virtual ~InfoPanel(); + bool frozen() const; + void clear(); + void setCurrent(const std::string& name); + void linkSelected(VInfo_ptr); + void relayInfoPanelCommand(VInfo_ptr info,QString cmd); + void relayDashboardCommand(VInfo_ptr info,QString cmd); + + void populateDialog(); + + //From DashboardWidget + void populateDockTitleBar(DashboardDockTitleWidget*); + void reload() {} + void rerender(); + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + void writeSettingsForDialog(); + void readSettingsForDialog(); + + //From VInfoObserver + void notifyDelete(VInfo*) {} + void notifyDataLost(VInfo*); + + //From ServerObserver + void notifyDefsChanged(ServerHandler* server, const std::vector& a); + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + void notifyEndServerClear(ServerHandler* server) {} + void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + void notifyEndServerScan(ServerHandler* server); + void notifyServerConnectState(ServerHandler* server); + void notifyServerSuiteFilterChanged(ServerHandler* server); + void notifyEndServerSync(ServerHandler* server); + +public Q_SLOTS: + void slotReload(VInfo_ptr node); + void setCurrentSelection(VInfo_ptr node) {slotReload(node);} + +protected Q_SLOTS: + void slotReloadFromBc(VInfo_ptr node); + void slotCurrentWidgetChanged(int); + void on_actionBreadcrumbs__toggled(bool b); + void on_actionFrozen__toggled(bool b); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + +protected: + void detachedChanged(); + +private: + void localClear(); + void reset(VInfo_ptr node); + void adjustInfo(VInfo_ptr node); + void adjustTabs(VInfo_ptr node); + InfoPanelItemHandler* findHandler(QWidget* w); + InfoPanelItemHandler* findHandler(InfoPanelDef*); + InfoPanelItem* findItem(QWidget* w); + InfoPanelItemHandler* createHandler(InfoPanelDef*); + void clearTab(); + void updateTitle(); + QMenu* buildOptionsMenu(); + + QList items_; + VInfo_ptr info_; + bool tabBeingCleared_; + bool tabBeingAdjusted_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelItem.cpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelItem.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelItem.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelItem.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,325 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "InfoPanelItem.hpp" + +#include "InfoPanel.hpp" +#include "InfoProvider.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" + +#include + +static std::map* makers = 0; + +InfoPanelItemFactory::InfoPanelItemFactory(const std::string& name) +{ + if(makers == 0) + makers = new std::map; + + // Put in reverse order... + (*makers)[name] = this; +} + +InfoPanelItemFactory::~InfoPanelItemFactory() +{ + // Not called +} + +InfoPanelItem* InfoPanelItemFactory::create(const std::string& name) +{ + std::map::iterator j = makers->find(name); + if(j != makers->end()) + return (*j).second->make(); + + return 0; +} + +//======================================================= +// +// InfoPanelItem +// +//======================================================= + + +InfoPanelItem::~InfoPanelItem() +{ + clear(); +} + +void InfoPanelItem::setOwner(InfoPanel* owner) +{ + assert(!owner_); + owner_=owner; +} + +//Set the new VInfo object. +//We also we need to manage the node observers. The InfoItem +//will be the observer of the server of the object stored in +//the new VInfo +void InfoPanelItem::adjust(VInfo_ptr info) +{ + //Check if there is data in info + if(info) + { + ServerHandler *server=info->server(); + + bool sameServer=(info_)?(info_->server() == server):false; + + //Handle observers + if(!sameServer) + { + if(info_ && info_->server()) + { + info_->server()->removeNodeObserver(this); + } + info->server()->addNodeObserver(this); + } + } + //If the there is no data we clean everything and return + else + { + if(info_ && info_->server()) + { + info_->server()->removeNodeObserver(this); + } + } + + //Set the info + info_=info; +} + +void InfoPanelItem::clear() +{ + if(info_ && info_->server()) + { + //info_->server()->removeServerObserver(this); + info_->server()->removeNodeObserver(this); + } + + info_.reset(); + + for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) + { + (*it)->clear(); + } +} + +//This function is called when the infopanel +// is being reset. The info_ might be unset. +void InfoPanelItem::setActive(bool active) +{ + active_=active; + + if(active_) + { + //Enable the infoProviders + for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) + { + (*it)->setActive(true); + } + } + else + { + clearContents(); + + selected_=false; + suspended_=false; + + //Disable the info provider + for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) + { + //This will clear the providers again + (*it)->setActive(false); + } + } + + //updateWidgetState(); +} + +void InfoPanelItem::setSelected(bool selected,VInfo_ptr info) +{ + if(selected_ == selected) + return; + + ChangeFlags flags(SelectedChanged); + selected_=selected; + + assert(active_); + + if(selected_) + { + //Suspend + if(suspended_) {} + //Resume + else + { + if(unselectedFlags_.isSet(KeepContents)) + { + if(!info_) + { + reload(info); + return; + } + } + else + { + reload(info); + return; + } + } + } + + //if the item becomes unselected we do not do anything if it is frozen + //or the contents must be kept (e.g. for output) + else + { + if(!frozen_) + { + if(!unselectedFlags_.isSet(KeepContents)) + { + //This will also clear the providers + clearContents(); + } + } + + return; + } + + //We update the derived class + updateState(flags); +} + +void InfoPanelItem::setSuspended(bool suspended,VInfo_ptr info) +{ + if(suspended_ == suspended) + return; + + suspended_=suspended; + ChangeFlags flags(SuspendedChanged); + + if(!active_) + return; + + //Suspend + if(suspended_) {} + //Resume + else + { + if(selected_ && !info_) + { + reload(info); + return; + } + } + + //We update the derived class + updateState(flags); +} + +void InfoPanelItem::setFrozen(bool b) +{ + frozen_=b; + if(!active_) + return; + + //We update the derived class + updateState(FrozenChanged); + +} + +void InfoPanelItem::setDetached(bool b) +{ + detached_=b; + + if(!active_) + return; + + //We update the derived class + updateState(DetachedChanged); +} + +bool InfoPanelItem::hasSameContents(VInfo_ptr) +{ + return false; +} + +void InfoPanelItem::linkSelected(const std::string& path) +{ + if(!suspended_) + { + VInfo_ptr info=VInfo::createFromPath(info_->server(),path); + + if(info) + { + assert(owner_); + owner_->linkSelected(info); + } + } +} + +void InfoPanelItem::linkSelected(VInfo_ptr info) +{ + if(!suspended_) + { + if(info) + { + assert(owner_); + owner_->linkSelected(info); + } + } +} + +void InfoPanelItem::relayInfoPanelCommand(VInfo_ptr info,QString cmd) +{ + if(info) + { + assert(owner_); + owner_->relayInfoPanelCommand(info,cmd); + } +} + +void InfoPanelItem::relayDashboardCommand(VInfo_ptr info,QString cmd) +{ + if(info) + { + assert(owner_); + owner_->relayDashboardCommand(info,cmd); + } +} + +//From NodeObserver +void InfoPanelItem::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange&) +{ + if(!node || frozen_ || !active_ || suspended_) + return; + + //Check if there is data in info + if(info_) + { + if(info_->isNode()) + { + //Check if updates are handled when unselected + if(!selected_ && !unselectedFlags_.isSet(KeepContents)) + { + return; + } + + //Check if the updated node is handled by the item + if(handleAnyChange_ || info_->node() == node || + (useAncestors_ && info_->node()->isAncestor(node))) + { + //We call the method implemented in the concrete class + //to handle the changes + nodeChanged(node,aspect); + return; + } + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelItem.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelItem.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanelItem.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanelItem.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,140 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INFOPANELITEM_HPP_ +#define INFOPANELITEM_HPP_ + +#include "FlagSet.hpp" +#include "NodeObserver.hpp" +#include "VInfo.hpp" +#include "InfoPresenter.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +#include +#include + +class QWidget; +class InfoPanel; +class InfoProvider; +class VComboSettings; + +//This is the (abstract) base class to represent one tab in the info panel. +//It cannot be inheried from QObject beacuse we would end up with double inheritance since +//all the derived calsses are inherited from QObject!! + +class InfoPanelItem : public VTaskObserver, public InfoPresenter, public NodeObserver +{ +friend class InfoPanel; + +public: + InfoPanelItem() : owner_(0), active_(false), selected_(false), suspended_(false), + frozen_(false), detached_(false), unselectedFlags_(KeepContents), + useAncestors_(false),handleAnyChange_(false), + keepServerDataOnLoad_(false) {} + virtual ~InfoPanelItem(); + + enum ChangeFlag {ActiveChanged=1,SelectedChanged=2,SuspendedChanged=4,FrozenChanged=8,DetachedChanged=16}; + typedef FlagSet ChangeFlags; + + //What to do when the item is unselected + enum UnselectedFlag {KeepContents=1,KeepActivity=2}; + typedef FlagSet UnselectedFlags; + + virtual void reload(VInfo_ptr info)=0; + virtual QWidget* realWidget()=0; + virtual void clearContents()=0; + virtual bool hasSameContents(VInfo_ptr info); + void setOwner(InfoPanel*); + + virtual void setActive(bool); + void setSelected(bool,VInfo_ptr); + void setSuspended(bool,VInfo_ptr); + void setFrozen(bool); + void setDetached(bool); + + bool isSuspended() const {return suspended_;} + bool keepServerDataOnLoad() const {return keepServerDataOnLoad_;} + + //From VTaskObserver + void taskChanged(VTask_ptr) {} + + //From VInfoPresenter + void infoReady(VReply*) {} + void infoFailed(VReply*) {} + void infoProgress(VReply*) {} + void infoProgressStart(int min,int max,const std::string& text) {} + void infoProgress(int value,const std::string& text) {} + + //From NodeObserver + void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); + void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) {} + + virtual void writeSettings(VComboSettings* vs) {} + virtual void readSettings(VComboSettings* vs) {} + +protected: + void adjust(VInfo_ptr); + void linkSelected(const std::string& path); + void linkSelected(VInfo_ptr); + void relayInfoPanelCommand(VInfo_ptr info,QString cmd); + void relayDashboardCommand(VInfo_ptr info,QString cmd); + + virtual void clear(); + virtual void updateState(const ChangeFlags&)=0; + + //Notifications about the server changes + virtual void defsChanged(const std::vector&)=0; + virtual void connectStateChanged() {} + virtual void suiteFilterChanged() {} + virtual void serverSyncFinished() {} + + //Notifications about the node changes + virtual void nodeChanged(const VNode*, const std::vector&)=0; + + InfoPanel* owner_; + bool active_; + bool selected_; + bool suspended_; + bool frozen_; + bool detached_; + UnselectedFlags unselectedFlags_; + bool useAncestors_; + bool handleAnyChange_; + + //do not reload the server data when a new info object is loaded. This + //only makes sense if the this sever-related item(tab) is alawys visible + //whatever node is selected!!! + bool keepServerDataOnLoad_; +}; + +class InfoPanelItemFactory +{ +public: + explicit InfoPanelItemFactory(const std::string&); + virtual ~InfoPanelItemFactory(); + + virtual InfoPanelItem* make() = 0; + static InfoPanelItem* create(const std::string& name); + +private: + explicit InfoPanelItemFactory(const InfoPanelItemFactory&); + InfoPanelItemFactory& operator=(const InfoPanelItemFactory&); + +}; + +template +class InfoPanelItemMaker : public InfoPanelItemFactory +{ + InfoPanelItem* make() { return new T(); } +public: + explicit InfoPanelItemMaker(const std::string& name) : InfoPanelItemFactory(name) {} +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.ui ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPanel.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPanel.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ + + + InfoPanel + + + + 0 + 0 + 683 + 468 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + + + + Tab 1 + + + + + + + + true + + + Frozen + + + Do not update panel when node is updated + + + + + true + + + Breadcrumbs + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      +
      + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoPresenter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoPresenter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoPresenter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoPresenter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,43 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INFOPRESENTER_HPP_ +#define INFOPRESENTER_HPP_ + +#include "VInfo.hpp" +#include + +class InfoProvider; +class VReply; + +//This is a base class for presenting a VInfo object. The InfoPanelItems +//are derived from this class. It can contain an InfoProvider member that +//is able to generate the information we want to display about the VInfo. + +class InfoPresenter +{ +public: + InfoPresenter() : infoProvider_(0) {} + virtual ~InfoPresenter() {} + virtual void infoReady(VReply*) {} + virtual void infoFailed(VReply*) {} + virtual void infoProgress(VReply*) {} + virtual void infoProgressStart(const std::string& text,int max) {} + virtual void infoProgress(const std::string& text,int value) {} + virtual void infoAppended(VReply*) {} + VInfo_ptr info() const {return info_;} + void registerInfoProvider(InfoProvider* ip) {infoProviders_.push_back(ip);} + +protected: + VInfo_ptr info_; + InfoProvider* infoProvider_; //the main info provider + std::vector infoProviders_; //the list of all the providers including the main one +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,291 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "InfoProvider.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" + +#include + +#include + +InfoProvider::InfoProvider(InfoPresenter* owner,VTask::Type taskType) : + owner_(owner), + taskType_(taskType), + active_(false), + autoUpdate_(false), + inAutoUpdate_(false) +{ + reply_=new VReply(this); + if(owner_) + owner_->registerInfoProvider(this); +} + +InfoProvider::~InfoProvider() +{ + delete reply_; + clear(); +} + +void InfoProvider::clear() +{ + if(task_) + task_->status(VTask::CANCELLED); + + reply_->reset(); + info_.reset(); +} + +void InfoProvider::setActive(bool b) +{ + active_=b; + if(!active_) + clear(); +} + +void InfoProvider::setAutoUpdate(bool b) +{ + autoUpdate_=b; +} + +void InfoProvider::info(VInfo_ptr info) +{ + //We keep it alive + info_=info; + + if(task_) + { + task_->status(VTask::CANCELLED); + task_.reset(); + } + + if(owner_ && info_) + info_->accept(this); +} + +//Server +void InfoProvider::visit(VInfoServer* info) +{ + reply_->reset(); + + if(!info->server()) + { + owner_->infoFailed(reply_); + } + + //Define a task for getting the info from the server. + task_=VTask::create(taskType_,this); + + //Run the task in the server. When it completes taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + info->server()->run(task_); +} + +//Node +void InfoProvider::visit(VInfoNode* info) +{ + reply_->reset(); + + if(!info->node() || !info->node()->node()) + { + owner_->infoFailed(reply_); + } + + //Check if we have a server + if(!info->server()) + { + owner_->infoFailed(reply_); + } + + VNode *n=info->node(); + + std::string fileName; + if(!fileVarName_.empty()) + { + //Get the fileName + fileName=n->genVariable(fileVarName_); + } + + //We try to read the file directly from the disk + if(info->server()->readFromDisk()) + { + //There is a variable defined for the filename + if(!fileName.empty()) + { + if(reply_->textFromFile(fileName)) + { + reply_->fileReadMode(VReply::LocalReadMode); + reply_->fileName(fileName); + owner_->infoReady(reply_); + return; + } + /*else if(handleFileMissing(fileName,reply_)) + { + return; + }*/ + } + } + + //We try to get the file contents from the server + //(this will go through the threaded communication) + + //Define a task for getting the info from the server. + task_=VTask::create(taskType_,n,this); + task_->reply()->fileName(fileName); + task_->reply()->fileReadMode(VReply::ServerReadMode); + + //Run the task in the server. When it finish taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + info->server()->run(task_); + +} + +void InfoProvider::handleFileNotDefined(VReply *reply) +{ + reply->setInfoText(fileNotDefinedText_); + owner_->infoReady(reply_); +} + +bool InfoProvider::handleFileMissing(const std::string& fileName,VReply *reply) +{ + return false; + + //reply->setWarningText(fileMissingText_); + //owner_->infoReady(reply_); +} + +void InfoProvider::taskChanged(VTask_ptr task) +{ + if(task_ != task) + return; + + //temporary hack! + task_->reply()->setSender(this); + + switch(task->status()) + { + case VTask::FINISHED: + { + task->reply()->addLog("TRY>fetch file from ecflow server: OK"); + + //The file should have a copy of the reply log + VFile_ptr f=task_->reply()->tmpFile(); + if(f) + { + f->setFetchDate(QDateTime::currentDateTime()); + f->setFetchMode(VFile::ServerFetchMode); + f->setLog(task_->reply()->log()); + } + task->reply()->status(VReply::TaskDone); + owner_->infoReady(task->reply()); + task_.reset(); + } + break; + case VTask::ABORTED: + case VTask::REJECTED: + task->reply()->addLog("TRY>fetch file from ecflow server: FAILED"); + task->reply()->status(VReply::TaskFailed); + owner_->infoFailed(task->reply()); + task_.reset(); + break; + case VTask::CANCELLED: + if(!task->reply()->errorText().empty()) + { + task->reply()->addLog("TRY>fetch file from ecflow server: FAILED"); + task->reply()->status(VReply::TaskCancelled); + owner_->infoFailed(task->reply()); + } + //We do not need the task anymore. + task_.reset(); + break; + default: + break; + } +} + +JobProvider::JobProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::JobTask) +{ + fileVarName_="ECF_JOB"; + + fileNotDefinedText_="Job is not defined"; + + fileMissingText_="Job not found!
      Check ECF_HOME directory \ + for read/write access. Check for file presence and read access below. \ + The file may have been deleted or this may be a 'dummy' task"; +} + +bool JobProvider::handleFileMissing(const std::string& fileName,VReply *reply) +{ + if(fileName.find(".job0") != std::string::npos) + { + reply->setInfoText("No job to be expected when TRYNO is 0!"); + owner_->infoReady(reply_); + return true; + } + + + /*else + { + reply->setWarningText(fileMissingText_); + }*/ + return false; +} + +ManualProvider::ManualProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::ManualTask) +{ + fileVarName_="ECF_MANUAL"; + fileNotDefinedText_="Manual is not available"; + fileMissingText_="Manual is not available"; +} + +MessageProvider::MessageProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::MessageTask) +{ + +} + +ScriptProvider::ScriptProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::ScriptTask) +{ + fileVarName_="ECF_SCRIPT"; + fileNotDefinedText_="Script is not defined"; + fileMissingText_="Script not/b> found!
      Check ECF_FILES or ECF_HOME directories, \ + for read access. Check for file presence and read access below files directory \ + or this may be a 'dummy' task"; +} + +HistoryProvider::HistoryProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::HistoryTask) +{ + +} + +SuiteProvider::SuiteProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::SuiteListTask) +{ + +} + + +ZombieProvider::ZombieProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::ZombieListTask) +{ + +} + +WhyProvider::WhyProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::WhyTask) +{ + +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InfoProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InfoProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InfoProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InfoProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,112 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INFOPROVIDER_HPP_ +#define INFOPROVIDER_HPP_ + +#include "FlagSet.hpp" +#include "VInfo.hpp" +#include "InfoPresenter.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +class InfoPanelItem; + +class InfoProvider : public VTaskObserver, public VInfoVisitor +{ +public: + InfoProvider(InfoPresenter* owner,VTask::Type); + virtual ~InfoProvider(); + + void info(VInfo_ptr); + void command(VTask::Type); + virtual void clear(); + + void setActive(bool); + virtual void setAutoUpdate(bool); + bool autoUpdate() const {return autoUpdate_;} + bool inAutoUpdate() const {return inAutoUpdate_;} + + //From VInfoVisitor + void visit(VInfoServer*); + void visit(VInfoNode*); + void visit(VInfoAttribute*) {} + + //From VTaskObserver + void taskChanged(VTask_ptr); + +protected: + virtual void handleFileNotDefined(VReply *reply); + virtual bool handleFileMissing(const std::string& fileName,VReply *reply); + virtual void optionsChanged() {} + + InfoPresenter* owner_; + VInfo_ptr info_; + VTask_ptr task_; + VReply* reply_; + VTask::Type taskType_; + std::string fileVarName_; + std::string fileNotDefinedText_; + std::string fileMissingText_; + bool active_; + bool autoUpdate_; + bool inAutoUpdate_; +}; + +class JobProvider : public InfoProvider +{ +public: + explicit JobProvider(InfoPresenter* owner); +protected: + bool handleFileMissing(const std::string& fileName,VReply *reply); +}; + +class ManualProvider : public InfoProvider +{ +public: + explicit ManualProvider(InfoPresenter* owner); +}; + +class MessageProvider : public InfoProvider +{ +public: + explicit MessageProvider(InfoPresenter* owner); +}; + +class ScriptProvider : public InfoProvider +{ +public: + explicit ScriptProvider(InfoPresenter* owner); +}; + +class HistoryProvider : public InfoProvider +{ +public: + explicit HistoryProvider(InfoPresenter* owner); +}; + +class SuiteProvider : public InfoProvider +{ +public: + explicit SuiteProvider(InfoPresenter* owner); +}; + +class ZombieProvider : public InfoProvider +{ +public: + explicit ZombieProvider(InfoPresenter* owner); +}; + +class WhyProvider : public InfoProvider +{ +public: + explicit WhyProvider(InfoPresenter* owner); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InputEventLog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/InputEventLog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InputEventLog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InputEventLog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,222 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "InputEventLog.hpp" + +#include "DirectoryHandler.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LogTruncator.hpp" +#include "TimeStamp.hpp" +#include "UiLog.hpp" + +InputEventLog* InputEventLog::instance_=0; +static bool firstStart=true; + +static QString objectPath(QObject *obj) +{ + QString res; + for(; obj; obj = obj->parent()) + { + if (!res.isEmpty()) + res.prepend("/"); + QString s=obj->objectName(); + //QString cn(obj->metaObject()->className()); + if(s.isEmpty()) s="?"; + //res.prepend("[" + cn + "]" +s); + res.prepend(s); + } + return res; +} + +InputEventLog::InputEventLog(QObject* parent) : QObject(parent), paused_(false) +{ + QString path=QString::fromStdString(DirectoryHandler::uiEventLogFileName()); + outFile_=new QFile(path); + + truncator_=new LogTruncator(path,86400*1000,5*1024*1024,2000,this); + connect(truncator_,SIGNAL(truncateBegin()),this,SLOT(truncateLogBegin())); + connect(truncator_,SIGNAL(truncateEnd()),this,SLOT(truncateLogEnd())); +} + +InputEventLog::~InputEventLog() +{ + outFile_->close(); + delete outFile_; +} + +InputEventLog* InputEventLog::instance() +{ + if(!instance_) + instance_=new InputEventLog(0); + return instance_; +} + +void InputEventLog::start() +{ + Q_ASSERT(!paused_); + + if(out_.device()) + return; + + QFile::OpenMode mode=(firstStart)?QFile::WriteOnly:QFile::Append; + if(outFile_->open(mode)) + { + firstStart=false; + out_.setDevice(outFile_); + qApp->removeEventFilter(this); + qApp->installEventFilter(this); + } + else + { + QFileInfo info(*outFile_); + UiLog().err() << "InputEventLog --> cannot open log file for writing: " << info.absoluteFilePath(); + } +} + +void InputEventLog::stop() +{ + qApp->removeEventFilter(this); + outFile_->close(); + out_.setDevice(0); +} + +void InputEventLog::truncateLogBegin() +{ + paused_=(out_.device() != 0); + stop(); +} + +void InputEventLog::truncateLogEnd() +{ + if(paused_) + { + paused_=false; + start(); + } +} + +bool InputEventLog::eventFilter(QObject *obj, QEvent *event) +{ + if(out_.device()) + { + //out_ << event->type() << " " << obj->objectName() << " " << obj->metaObject()->className() << "\n"; + //out_.flush(); + + if(event->type() == QEvent::MouseButtonPress) + { + logMousePress(obj,static_cast(event)); + } + else if(event->type() == QEvent::MouseButtonRelease) + { + logMouseRelease(obj,static_cast(event)); + } + else if(event->type() == QEvent::Close) + { + logClose(obj,static_cast(event)); + } + else if(event->type() == QEvent::ContextMenu) + { + logContextMenu(obj,static_cast(event)); + } + } + + return QObject::eventFilter(obj,event); +} + +void InputEventLog::logMousePress(QObject* obj,QMouseEvent *event) +{ + QString cn(obj->metaObject()->className()); + if(cn != "QWidgetWindow" && cn != "QMenuBar" && + cn != "QToolBar" && cn != "MainWindow" && cn != "QScrollBar" ) + { + std::string s; + ecf::TimeStamp::now_in_brief(s); + out_ << s.c_str() << "mp " << cn << " " << objectPath(obj); + + if(cn == "QTabBar") + { + if(QTabBar* t=static_cast(obj)) + { + int idx=t->tabAt(event->pos()); + if(idx >=0) + { + out_ << " tab=" << t->tabText(idx); + } + } + } + else if(cn == "QMenu") + { + if(QMenu* m=static_cast(obj)) + { + if(QAction* ac=m->actionAt(event->pos())) + { + out_ << " ac=" << ac->objectName(); + } + } + } + out_ << "\n"; + out_.flush(); + } +} + +void InputEventLog::logMouseRelease(QObject* obj,QMouseEvent *event) +{ + QString cn(obj->metaObject()->className()); + if(cn == "QMenu") + { + std::string s; + ecf::TimeStamp::now_in_brief(s); + out_ << s.c_str() << "mr " << cn << " " << objectPath(obj); + if(QMenu* m=static_cast(obj)) + { + if(QAction* ac=m->actionAt(event->pos())) + { + out_ << " ac=" << ac->objectName(); + } + } + out_ << "\n"; + out_.flush(); + } +} + +void InputEventLog::logClose(QObject* obj,QCloseEvent *event) +{ + QString cn(obj->metaObject()->className()); + if(cn != "QWidgetWindow" && cn != "QTipLabel") + { + std::string s; + ecf::TimeStamp::now_in_brief(s); + out_ << s.c_str() << "cl " << cn << " " << objectPath(obj) << "\n"; + out_.flush(); + } +} + +void InputEventLog::logContextMenu(QObject* obj,QContextMenuEvent *event) +{ + QString cn(obj->metaObject()->className()); + if(cn != "QWidgetWindow") + { + std::string s; + ecf::TimeStamp::now_in_brief(s); + out_ << s.c_str() << "cm " << cn << " " << objectPath(obj) << "\n"; + out_.flush(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/InputEventLog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/InputEventLog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/InputEventLog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/InputEventLog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,55 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef INPUTEVENTLOG_HPP +#define INPUTEVENTLOG_HPP + +#include +#include + +class QCloseEvent; +class QContextMenuEvent; +class QFile; +class QMouseEvent; +class LogTruncator; + +class InputEventLog : public QObject +{ + Q_OBJECT +public: + ~InputEventLog(); + + void start(); + void stop(); + + static InputEventLog* instance(); + +protected Q_SLOTS: + void truncateLogBegin(); + void truncateLogEnd(); + +protected: + InputEventLog(QObject* parent=0); + + bool eventFilter(QObject *obj, QEvent *event); + void logMousePress(QObject* obj,QMouseEvent *e); + void logMouseRelease(QObject* obj,QMouseEvent *e); + void logClose(QObject* obj,QCloseEvent *e); + void logContextMenu(QObject* obj,QContextMenuEvent *e); + + static InputEventLog* instance_; + bool paused_; + QFile *outFile_; + QTextStream out_; + LogTruncator* truncator_; +}; + + +#endif // INPUTEVENTLOG_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/JobItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/JobItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/JobItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/JobItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,135 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "JobItemWidget.hpp" + +#include "Highlighter.hpp" +#include "InfoProvider.hpp" +#include "VConfig.hpp" +#include "VReply.hpp" +#include "VNode.hpp" + +JobItemWidget::JobItemWidget(QWidget *parent) : CodeItemWidget(parent) +{ + messageLabel_->hide(); + messageLabel_->setShowTypeTitle(false); + + //Remove the first spacer item!! + removeSpacer(); + + //The document becomes the owner of the highlighter + new Highlighter(textEdit_->document(),"job"); + + infoProvider_=new JobProvider(this); + + //Editor font + textEdit_->setFontProperty(VConfig::instance()->find("panel.job.font")); +} + +JobItemWidget::~JobItemWidget() +{ +} + +QWidget* JobItemWidget::realWidget() +{ + return this; +} + +void JobItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + info_=info; + messageLabel_->hide(); + + //Info must be a node + if(info_ && info_->isNode() && info_->node()) + { + reloadTb_->setEnabled(false); + infoProvider_->info(info_); + } +} + +void JobItemWidget::clearContents() +{ + InfoPanelItem::clear(); + textEdit_->clear(); + messageLabel_->hide(); + reloadTb_->setEnabled(true); + clearCurrentFileName(); +} + +void JobItemWidget::infoReady(VReply* reply) +{ + Q_ASSERT(reply); + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); + + if(reply->hasWarning()) + { + messageLabel_->showWarning(QString::fromStdString(reply->warningText())); + } + else if(reply->hasInfo()) + { + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); + } + + fileLabel_->update(reply); + reloadTb_->setEnabled(true); + + setCurrentFileName(reply->fileName()); +} + +void JobItemWidget::infoProgress(VReply* reply) +{ + QString s=QString::fromStdString(reply->infoText()); + messageLabel_->showInfo(s); +} + +void JobItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); + messageLabel_->showError(s); + reloadTb_->setEnabled(true); +} + +void JobItemWidget::reloadRequested() +{ + reload(info_); +} + +void JobItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + reloadTb_->setEnabled(false); + } + //Resume + else + { + if(info_ && info_->node()) + { + reloadTb_->setEnabled(true); + } + else + { + clearContents(); + } + } + } +} + +static InfoPanelItemMaker maker1("job"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/JobItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/JobItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/JobItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/JobItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef JOBITEMWIDGET_HPP_ +#define JOBITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" +#include "CodeItemWidget.hpp" +#include "VInfo.hpp" + +#include "ServerHandler.hpp" + +class JobItemWidget : public CodeItemWidget, public InfoPanelItem +{ +public: + explicit JobItemWidget(QWidget *parent=0); + ~JobItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected: + void updateState(const ChangeFlags&); + void reloadRequested(); +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,132 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "LabelEditor.hpp" + +#include + +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "SessionHandler.hpp" + +LabelEditorWidget::LabelEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); + + QLayoutItem *item; + item=grid_->itemAtPosition(1,0); + Q_ASSERT(item); + item->setAlignment(Qt::AlignLeft|Qt::AlignTop); +} + +LabelEditor::LabelEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"label",parent) +{ + w_=new LabelEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "label"); + + if(a->data().count() < 2) + return; + + QString name=a->data().at(1); + QString val; + if(a->data().count() > 2) + val=a->data().at(2); + + oriVal_=val; + + w_->nameLabel_->setText(name); + w_->valueTe_->setPlainText(val); + w_->valueTe_->setFocus(); + + header_->setInfo(QString::fromStdString(info_->path()),"Label"); + + connect(w_->valueTe_,SIGNAL(textChanged()), + this,SLOT(slotValueChanged())); + + checkButtonStatus(); + + readSettings(); +} + +LabelEditor::~LabelEditor() +{ + writeSettings(); +} + +void LabelEditor::apply() +{ + std::string val=w_->valueTe_->toPlainText().toStdString(); + std::string name=w_->nameLabel_->text().toStdString(); + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","label",name,val); + CommandHandler::run(info_,cmd); +} + +void LabelEditor::resetValue() +{ + w_->valueTe_->setPlainText(oriVal_); + checkButtonStatus(); +} + +void LabelEditor::slotValueChanged() +{ + checkButtonStatus(); +} + +bool LabelEditor::isValueChanged() +{ + return (oriVal_ != w_->valueTe_->toPlainText()); +} + +void LabelEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("LabelEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void LabelEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("LabelEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(310,200)); + } + + settings.endGroup(); +} + +static AttributeEditorMaker makerStr("label"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef LABELEDITOR_HPP +#define LABELEDITOR_HPP + +#include "ui_LabelEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +class LabelEditor; + +class LabelEditorWidget : public QWidget, protected Ui::LabelEditorWidget +{ +friend class LabelEditor; +public: + LabelEditorWidget(QWidget *parent=0); +}; + +class LabelEditor : public AttributeEditor +{ +Q_OBJECT +public: + LabelEditor(VInfo_ptr,QWidget* parent=0); + ~LabelEditor(); + +protected Q_SLOTS: + void slotValueChanged(); + +protected: + void apply(); + void resetValue(); + bool isValueChanged(); + void readSettings(); + void writeSettings(); + + LabelEditorWidget* w_; + QString oriVal_; +}; + +#endif // LABELEDITOR_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/LabelEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LabelEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,64 @@ + + + LabelEditorWidget + + + + 0 + 0 + 329 + 227 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + TextLabel + + + + + + + Value: + + + + + + + + 0 + 1 + + + + + + + + Name: + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,330 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "LimitEditor.hpp" + +#include +#include +#include + +#include "Aspect.hpp" + +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "MainWindow.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VLimitAttr.hpp" +#include "SessionHandler.hpp" + +LimitEditorWidget::LimitEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); + removeTb_->setDefaultAction(actionRemove_); + removeAllTb_->setDefaultAction(actionRemoveAll_); + //pathView_->addAction(actionRemove_); + pathView_->addAction(actionLookUp_); + pathView_->setSelectionMode(QAbstractItemView::ExtendedSelection); + pathView_->setContextMenuPolicy(Qt::ActionsContextMenu); +} + +LimitEditor::LimitEditor(VInfo_ptr info,QWidget* parent) : + AttributeEditor(info,"limit",parent), + model_(0) +{ + w_=new LimitEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "limit"); + QStringList aData=a->data(); + + if(aData.count() < 4) + return; + + QString name=aData[1]; + oriVal_=aData[2].toInt(); + oriMax_=aData[3].toInt(); + + w_->nameLabel_->setText(name); + w_->valueLabel_->setText(QString::number(oriVal_)); + + w_->maxSpin_->setRange(0,10000000); + w_->maxSpin_->setValue(oriMax_); + w_->maxSpin_->setFocus(); + + if(aData[2].isEmpty() || aData[3].isEmpty()) + { + w_->actionRemove_->setEnabled(false); + w_->actionRemoveAll_->setEnabled(false); + return; + } + + buildList(a); + + connect(w_->maxSpin_,SIGNAL(valueChanged(int)), + this,SLOT(slotMaxChanged(int))); + + connect(w_->actionRemove_,SIGNAL(triggered()), + this,SLOT(slotRemove())); + + connect(w_->actionRemoveAll_,SIGNAL(triggered()), + this,SLOT(slotRemoveAll())); + + connect(w_->actionLookUp_,SIGNAL(triggered()), + this,SLOT(slotLookUp())); + + connect(w_->pathView_,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClicked(const QModelIndex&))); + + header_->setInfo(QString::fromStdString(info_->path()),"Limit"); + + checkButtonStatus(); + + readSettings(); + + //No reset button is allowed because we can perform irreversible changes! + doNotUseReset(); +} + +LimitEditor::~LimitEditor() +{ + writeSettings(); +} + +void LimitEditor::buildList(VAttribute *a) +{ + VLimitAttr* lim=static_cast(a); + Q_ASSERT(lim); + + model_=new QStringListModel(this); + w_->pathView_->setModel(model_); + + //Update the model(=node list) + setModelData(lim->paths()); +} + +void LimitEditor::apply() +{ + int intVal=w_->valueLabel_->text().toInt(); + int intMax=w_->maxSpin_->value(); + std::string val=QString::number(intVal).toStdString(); + std::string max=QString::number(intMax).toStdString(); + std::string name=w_->nameLabel_->text().toStdString(); + + std::vector valCmd; + VAttribute::buildAlterCommand(valCmd,"change","limit_value",name,val); + + std::vector maxCmd; + VAttribute::buildAlterCommand(maxCmd,"change","limit_max",name,max); + + if(oriVal_ != intVal && oriMax_ != intMax) + { + if(intVal < oriMax_) + { + CommandHandler::run(info_,valCmd); + CommandHandler::run(info_,maxCmd); + } + else + { + CommandHandler::run(info_,maxCmd); + CommandHandler::run(info_,valCmd); + } + } + else if(oriVal_ != intVal) + { + CommandHandler::run(info_,valCmd); + } + + else if(oriMax_ != intMax) + { + CommandHandler::run(info_,maxCmd); + } +} + +void LimitEditor::resetValue() +{ +} + +void LimitEditor::slotMaxChanged(int) +{ + checkButtonStatus(); +} + +bool LimitEditor::isValueChanged() +{ + return (oriMax_ != w_->maxSpin_->value()); +} + +void LimitEditor::slotRemove() +{ + remove(false); +} + +void LimitEditor::slotRemoveAll() +{ + remove(true); +} + +void LimitEditor::remove(bool all) +{ + if(!info_) + return; + + //We cannot cancel the setting after remove is callled + disableCancel(); + + Q_ASSERT(model_); + + VAttribute* a=info_->attribute(); + Q_ASSERT(a); + VLimitAttr* lim=static_cast(a); + Q_ASSERT(lim); + + if(all) + { + std::vector valCmd; + VAttribute::buildAlterCommand(valCmd,"change","limit_value",a->strName(),"0"); + CommandHandler::run(info_,valCmd); + } + else + { + std::vector paths; + Q_FOREACH(QModelIndex idx,w_->pathView_->selectionModel()->selectedRows()) + { + std::vector valCmd; + VAttribute::buildAlterCommand(valCmd,"delete","limit_path",a->strName(), + model_->data(idx,Qt::DisplayRole).toString().toStdString()); + CommandHandler::run(info_,valCmd); + } + } + + //Updating the gui with the new state will happen later + //because command() is asynchronous +} + +void LimitEditor::nodeChanged(const std::vector& aspect) +{ + bool limitCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::LIMIT) != aspect.end()); + if(limitCh && info_) + { + VAttribute* a=info_->attribute(); + Q_ASSERT(a); + VLimitAttr* lim=static_cast(a); + Q_ASSERT(lim); + + QStringList aData=a->data(); + if(aData.count() < 4) + return; + + oriVal_=aData[2].toInt(); + w_->valueLabel_->setText(QString::number(oriVal_)); + + oriMax_=aData[3].toInt(); + w_->maxSpin_->setValue(oriMax_); + + //Update the model (=node list) + setModelData(lim->paths()); + } +} + +void LimitEditor::setModelData(QStringList lst) +{ + Q_ASSERT(model_); + + bool hadData=(modelData_.isEmpty() == false); + modelData_=lst; + model_->setStringList(modelData_); + + if(!modelData_.isEmpty()) + { + if(!hadData) + { + w_->pathView_->setCurrentIndex(model_->index(0,0)); + w_->pathView_->setFocus(Qt::MouseFocusReason); + } + w_->actionRemove_->setEnabled(true); + w_->actionRemoveAll_->setEnabled(true); + } + else + { + w_->actionRemove_->setEnabled(false); + w_->actionRemoveAll_->setEnabled(false); + } +} +//Lookup in tree + +void LimitEditor::slotLookUp() +{ + QModelIndex idx=w_->pathView_->currentIndex(); + lookup(idx); +} + +void LimitEditor::slotDoubleClicked(const QModelIndex &index) +{ + lookup(index); +} + +void LimitEditor::lookup(const QModelIndex &idx) +{ + if(!info_) + return; + + Q_ASSERT(model_); + + std::string nodePath= + model_->data(idx,Qt::DisplayRole).toString().toStdString(); + + VInfo_ptr ni=VInfo::createFromPath(info_->server(),nodePath); + if(ni) + { + MainWindow::lookUpInTree(ni); + } +} + +void LimitEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("LimitEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void LimitEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("LimitEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(420,400)); + } + + settings.endGroup(); +} + +static AttributeEditorMaker makerStr("limit"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef LIMITEDITOR_HPP +#define LIMITEDITOR_HPP + +#include "ui_LimitEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +#include + +class LimitEditor; +class QStringListModel; + +class LimitEditorWidget : public QWidget, protected Ui::LimitEditorWidget +{ +friend class LimitEditor; +public: + LimitEditorWidget(QWidget *parent=0); +}; + +class LimitEditor : public AttributeEditor +{ +Q_OBJECT +public: + LimitEditor(VInfo_ptr,QWidget* parent=0); + ~LimitEditor(); + +protected Q_SLOTS: + void slotMaxChanged(int); + void slotRemove(); + void slotRemoveAll(); + void slotLookUp(); + void slotDoubleClicked(const QModelIndex &index); + +protected: + void resetValue(); + void apply(); + bool isValueChanged(); + void buildList(VAttribute *a); + void remove(bool all); + void nodeChanged(const std::vector& a); + void setModelData(QStringList lst); + void lookup(const QModelIndex &index); + void readSettings(); + void writeSettings(); + + LimitEditorWidget* w_; + int oriVal_; + int oriMax_; + QStringListModel* model_; + QStringList modelData_; +}; + +#endif // LIMITEDITOR_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/LimitEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LimitEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,169 @@ + + + LimitEditorWidget + + + + 0 + 0 + 329 + 227 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Name: + + + + + + + TextLabel + + + + + + + Value: + + + + + + + Maximum: + + + + + + + + + + TextLabel + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 5 + + + + + + + + + + Nodes consuming the limit: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Remove + + + Remove + + + Qt::ToolButtonTextBesideIcon + + + + + + + Reset + + + + + + + + + QAbstractItemView::ExtendedSelection + + + + + + + Remove + + + Remove selected node paths + + + + + Reset + + + Remove all node paths and reset the limit value to <b>zero</b> + + + + + &Look up node in tree + + + Look up node in tree + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LineEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LineEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LineEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LineEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,109 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "LineEdit.hpp" + +#include +#include +#include +#include + +LineEdit::LineEdit(QWidget *parent): + QLineEdit(parent), + iconLabel_(0) +{ + clearTb_=new QToolButton(this); + QPixmap pix(":/viewer/clear_left.svg"); + clearTb_->setIcon(pix); + clearTb_->setIconSize(QSize(12,12)); + clearTb_->setAutoRaise(true); + clearTb_->setCursor(Qt::ArrowCursor); + clearTb_->setToolTip(tr("Clear text")); + clearTb_->setObjectName("clearTextTb"); + + clearTb_->setStyleSheet("QToolButton{ border: node; padding: 0px;}"); + + connect(clearTb_,SIGNAL(clicked()), + this,SLOT(slotClear())); + + adjustSize(); + + /*QSize tbSize=clearTb_->sizeHint(); + + int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(tbSize.width()+frame+1)); + QSize minSize = minimumSizeHint(); + setMinimumSize(qMax(minSize.width(),tbSize.height()+frame*2+2), + qMax(minSize.height(),tbSize.height()+frame*2+2));*/ + +} + +void LineEdit::setDecoration(QPixmap pix) +{ + if(!iconLabel_) + iconLabel_=new QLabel(this); + + iconLabel_->setPixmap(pix); + iconLabel_->setProperty("lineEdit","true"); + + adjustSize(); + + /*QSize icSize=iconLabel_->sizeHint(); + QSize tbSize=clearTb_->sizeHint(); + + int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(icSize.width()+frame+1)); + QSize minSize = minimumSizeHint(); + setMinimumSize(qMax(minSize.width(),icSize.width()+tbSize.width()+frame*2+2), + qMax(minSize.height(),tbSize.height()+frame*2+2));*/ +} + + +void LineEdit::adjustSize() +{ + int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + QSize tbSize=clearTb_->sizeHint(); + setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(tbSize.width()+frame+1)); + + int leftWidth=0; + if(iconLabel_) + { + QSize icSize=iconLabel_->sizeHint(); + setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(icSize.width()+frame+1)); + leftWidth=+icSize.width(); + } + QSize minSize = minimumSizeHint(); + setMinimumSize(qMax(minSize.width(),leftWidth+tbSize.width()+frame*2+2), + qMax(minSize.height(),tbSize.height()+frame*2+2)); +} + + +void LineEdit::resizeEvent(QResizeEvent *) +{ + int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); + + QSize tbSize = clearTb_->sizeHint(); + clearTb_->move(rect().right()-frame-tbSize.width(), + (rect().bottom()+ 1-tbSize.height())/2); + + if(iconLabel_) + { + QSize icSize=iconLabel_->sizeHint(); + iconLabel_->move(rect().left()+frame+1, + (rect().bottom()+ 1-icSize.height())/2); + } +} + + +void LineEdit::slotClear() +{ + clear(); + Q_EMIT textCleared(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LineEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LineEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LineEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LineEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef LINEEDIT_INC_ +#define LINEEDIT_INC_ + +#include + +class QLabel; +class QToolButton; + +class LineEdit : public QLineEdit +{ +Q_OBJECT + +public: + explicit LineEdit (QWidget *parent=0); + void setDecoration(QPixmap); + +public Q_SLOTS: + void slotClear(); + +Q_SIGNALS: + void textCleared(); + +protected: + void adjustSize(); + void resizeEvent(QResizeEvent*); + + QToolButton *clearTb_; + QLabel* iconLabel_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogEvent.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LogEvent.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogEvent.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogEvent.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,315 @@ +// #define MAIN + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* + +shell = new QProcess(this); +QStringList argv() << myfile.fileName(); +shell.start("./src/unzip",argv); // or in win32 - src/unzip.exe +*/ + +#include +// foreach(QString x, strings) QTextStream(stdout) << x << endl; +#include "LogEvent.hpp" + +const int boxSize = 3; +// namespace status { +const QVector status (QVector() << "unknown" + << "suspended" + << "complete" + << "queued" + << "submitted" + << "active" + << "aborted" + << "shutdown" + << "halted" + << "event" + << "meter" + << "label" + ); +// } +static EventSorter *sorter = 0x0; + +counted::counted() + : count_(0) +{} + +counted::~counted() +{} + +void counted::attach() +{ ++count_; } + +void counted::detach() +{ if (--count_==0) delete this; } + +static int compare(const void*a, const void*b) { + LogEvent** pa = (LogEvent**)a; + LogEvent** pb = (LogEvent**)b; + return sorter->compare(*pa, *pb); +} + +bool lessThan( const LogEvent* le1, const LogEvent *le2 ) +{ + if (le1 && le2) return le1->time() < le2->time(); + return true; +} + +class LogCache // : public QArray +{ +public: + QVector add; + void reset() { int c = add.size(); while (c) { add[--c]->detach(); } add.clear(); } + void sort() { qSort(add.begin(), add.end(), lessThan); } + ~LogCache() {} +}; + +static LogCache cache; +static QString cached; + +LogEvent::LogEvent(const QString& path, const QDateTime& time) + : time_(time) + , path_(path) +{ + attach(); + cache.add.append(this); + // observe +} + +LogEvent::~LogEvent() +{} + +int LogEvent::load(bool reset) +{ + if (reset) { + cache.reset(); + cached = QString(); + } + return 0; +} + +int LogEvent::sort(EventSorter& s) +{ + sorter = &s; + cache.sort(); + sorter = 0x0; + return 0; +} + +int LogEvent::scan(const QString& path, EventLister&l) +{ + int i = cache.add.size(); + while (i--) { + if (cache.add[i]->path_.indexOf(path) != -1) + l.next(cache.add[i]); + } + return 0; +} + +int LogEvent::find(const QString&path) { return 0; } + +/********************************/ + +class StatusEvent: public LogEvent { + QString status_; + virtual bool start() { return status_ == "submitted"; } + virtual bool end() { return status_ == "complete"; } + virtual const QString& text(QString&) { return path_;} + virtual QString status() { return status_; } +public: + StatusEvent(const QString&path, const QDateTime& time, const QString& status) + : LogEvent(path, time) + , status_ (status) + {} +}; + +class EventEvent: public LogEvent { + bool set_; +public: + virtual QString status() { return "event"; } + EventEvent(const QString&path, const QDateTime& time, bool b) + : LogEvent(path, time) + , set_ (b) + {} +}; + +class MeterEvent: public LogEvent { + int step_; +public: + virtual QString status() { return "meter"; } + MeterEvent(const QString&path, const QDateTime& time, int step) + : LogEvent(path, time) + , step_ (step) + {} +}; + +/********************************/ +QString logs = "/media/map/boxster/map/ecm/201502/ect/tmp/map/work/p4/metapps/suites/o/def"; +QString logf1 = logs + "/timeline/ibis.900130.log"; +QString logf2 = logs + "/tkinter/logs/vsms1.ecf.3.log"; +typedef QMap > log_map_type; +log_map_type log_map; +QDateTime dt_min(QDateTime::fromString("00:00:00 1.1.2101", "hh:mm:ss d.M.yyyy")); +QDateTime dt_max(QDateTime::fromString("00:00:00 1.1.2001", "hh:mm:ss d.M.yyyy")); + +void reader(QString filename, + bool onlyTask=true, + int max=-1, + int debug=0 ) { + + QFile inputFile(filename); + log_map_type& rc = log_map; + + if (inputFile.open(QIODevice::ReadOnly)) { + QTextStream in(&inputFile); + QTextStream out (stdout); + int num = 0; + QString pkind (""), ppath(""); + while (!in.atEnd()) { + QString line = in.readLine(); + int pos (line.indexOf("LOG:")), + sql (line.indexOf("[")), + sqr (line.indexOf("]")); + if (pos == -1 || sqr == -1) continue; + QString time(line.left(sqr).mid(sql+1)); + QString log(line.mid(sqr+1).trimmed()); + QDateTime dt(QDateTime::fromString(time, "hh:mm:ss d.M.yyyy")); + if (dt < dt_min) dt_min = dt; + if (dt > dt_max) dt_max = dt; + + int ikd(log.indexOf(":")), + isp(log.indexOf(" ")); + QString kind (log.left(ikd)), + path(log.mid(ikd+1).trimmed()); + + if (onlyTask && ppath.indexOf(path) == 0) continue; + if (kind=="meter") + rc[path].append(new MeterEvent(path, dt, 1)); + else if (kind=="event") + rc[path].append(new EventEvent(path, dt, 1)); + else + rc[path].append(new StatusEvent(path, dt, kind)); + + pkind = kind; + ppath = path.split(" ")[0]; + if (debug) { + out << time << " " << dt.toString() + // << endl << log << endl << line << endl; + << " " << kind << " " << path.trimmed() << endl; + if (max > 0 && num > max) break; + ++num; + } + } + inputFile.close(); + } + // return rc; +} + +#ifdef MAIN_LOG +/* +make VERBOSE=1 +*/ +#include +#include "TimeItemWidget.hpp" + +int drawScene(TimeItemWidget* qwd) +{ + QGraphicsScene* scene = new QGraphicsScene(); + QMap colors; + colors.insert("submitted", Qt::blue); + colors["complete"] = Qt::yellow; + colors["aborted"] = Qt::red; + colors["active"] = Qt::green; + colors["meter"] = Qt::blue; + colors["event"] = Qt::black; + colors["unknown"] = Qt::gray; + // colors["unknown"] = Qt::grey; + + qwd->view()->setScene(scene); + if (1) { //Populate the scene + for(int x = 0; x < 8400; x = x + 25) { + for(int y = 0; y < 100; y = y + 25) { + if(x % 100 == 0 && y % 100 == 0) { + scene->addRect(x, y, 2, 2); + QString pointString; + QTextStream stream(&pointString); + if (0) stream << "(" << x << "," << y << ")"; + QGraphicsTextItem* item = scene->addText(pointString); + item->setPos(x, y); + } else { + scene->addRect(x, y, 1, 1); + } + } + } + } + /* ./work/sms/qt4/qt-book/chap08/diagram + /mnt/wdir/map/work/qt-5-4-1 */ + int num = 0, inc = 10; + log_map_type::const_iterator mit = log_map.begin(); + QVector::const_iterator lit; + while (mit != log_map.end()) { + num += inc; + QString line; + QTextStream stream(&line); + stream << mit.key(); + QGraphicsTextItem* item = scene->addText(line); + item->setPos(-100, num); + // scene->addText(mit.key()); + lit = mit.value().begin(); + while (lit != mit.value().end()) { + int yy = ((*lit)->time().toTime_t() - + // kSmallDate.toTime_t()) / 10; + dt_min.toTime_t()) / 10; + + /* QPixmap pm(5, 5); + pm.fill(); + QPainter p(&pm); + p.setRenderHint(QPainter::Antialiasing, true); + p.setPen(colors[(*lit)->status()]); + p.setBrush(QBrush(colors[(*lit)->status()])); */ + // QGraphicsEllipseItem *ell = p.drawEllipse(yy, num, 5, 5); + + stream << yy << "\n"; + QGraphicsEllipseItem *ell = scene->addEllipse(yy, num, 10, 10, + QPen(colors[(*lit)->status()]), + QBrush(colors[(*lit)->status()])); + ell->setPos(yy, num); + + ++lit; + } + ++mit; + } +} + +int main(int argc, char* argv[]) +{ + // Q_INIT_RESOURCE(images); + reader(logf1, 1, 500, 1); + reader(logf2, 1, 500, 1); + + QApplication app(argc, argv); + TimeItemWidget window(0x0); + window.setWindowTitle("TimeLine"); + window.show(); + log_map.clear(); + reader(logf2, 1); + drawScene(&window); + return app.exec(); +} +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogEvent.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LogEvent.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogEvent.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogEvent.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,61 @@ +#ifndef LogEvent_H +#define LogEvent_H + +class LogEvent; +class EventLister { +public: + virtual void next(LogEvent*) = 0; +}; + + +class EventSorter { +public: + virtual int compare(LogEvent*, LogEvent*) = 0; +}; + +// inline bool operator<(const DateTime& +const QDateTime kSmallDate(QDate(1900, 1, 1), QTime()); +const QDateTime kLargeDate(QDate(2100, 1, 1), QTime()); + +class counted +{ +public: + counted(); + void attach(); + void detach(); +protected: + ~counted(); +private: + counted(const counted&); + counted& operator=(const counted&); + int count_; +}; + +class LogEvent: public counted // , public observer +{ +public: + LogEvent(const QString&, const QDateTime&); + const QDateTime& time() const { return time_; } + virtual bool start() { return false; } + virtual bool end() { return false; } + virtual const QString& get_node() { return path_; } + virtual QString status() { return "none"; } + + static int load(bool reset); + static int scan(const QString&, EventLister&); + static int sort(EventSorter&); + static int find(const QString&); + static int compare(const LogEvent*, const LogEvent*); + virtual ~LogEvent(); + +protected: + QDateTime time_; + QString path_; +private: + LogEvent(const LogEvent&); + LogEvent& operator=(const LogEvent&); + // notification + // adoption + // gone +}; +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LogProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,197 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include + +#include +#include +#include +#include "LogProvider.hpp" + +#include "FileWatcher.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" +#include "File.hpp" + + +LogProvider::LogProvider(InfoPresenter* owner,QObject* parent) : + QObject(parent), + InfoProvider(owner,VTask::HistoryTask), + fileWatcher_(0) +{ + //NOTE: fileWatcher_'s parent (if it exists) will be "this", so + //fileWatcher_ will be automatically deleted in the destructor! +} + +void LogProvider::clear() +{ + stopWatchFile(); + InfoProvider::clear(); +} + +void LogProvider::setAutoUpdate(bool autoUpdate) +{ + InfoProvider::setAutoUpdate(autoUpdate); + + if(active_) + { + if(!autoUpdate_) + { + stopWatchFile(); + } + else + { + if(!inAutoUpdate_) + fetchFile(); + } + } + else + { + stopWatchFile(); + } +} + +void LogProvider::visit(VInfoServer* info) +{ + fetchFile(); +} + +void LogProvider::fetchFile() +{ + if(!active_) + return; + + stopWatchFile(); + + //Reset the reply + reply_->reset(); + + if(!info_) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + + //Get the filename + std::string fileName=server->vRoot()->genVariable("ECF_LOG"); + + fetchFile(server,fileName); +} + +void LogProvider::fetchFile(ServerHandler *server,const std::string& fileName) +{ + if(!server) + { + owner_->infoFailed(reply_); + return; + } + + //Set the filename in reply + reply_->fileName(fileName); + + //No filename is available + if(fileName.empty()) + { + reply_->setErrorText("Variable ECF_LOG is not defined!"); + owner_->infoFailed(reply_); + } + + //First we try to read the file directly from the disk + //if(server->readFromDisk()) + { + size_t file_size = 0; + std::string err_msg; + reply_->text( ecf::File::get_last_n_lines(fileName,100,file_size,err_msg)); + if(err_msg.empty()) + { + reply_->fileReadMode(VReply::LocalReadMode); + + if(autoUpdate_) + inAutoUpdate_=true; + + owner_->infoReady(reply_); + + //Try to track the changes in the log file + watchFile(fileName,file_size); + return; + } + } + + //Finally we try the server + reply_->fileReadMode(VReply::ServerReadMode); + + //Define a task for getting the info from the server. + task_=VTask::create(taskType_,server->vRoot(),this); + + //Run the task in the server. When it finish taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + server->run(task_); + +#if 0 + //If we are we could not get the file + //owner_->infoFailed(reply_); +#endif +} + +void LogProvider::watchFile(const std::string& fileName,size_t offset) +{ + if(autoUpdate_) + { + assert(fileWatcher_ == 0); + fileWatcher_=new FileWatcher(fileName,offset,this); + + connect(fileWatcher_,SIGNAL(linesAppended(QStringList)), + this,SLOT(slotLinesAppend(QStringList))); + + inAutoUpdate_=true; + } +} + +void LogProvider::stopWatchFile() +{ + if(fileWatcher_) + { + delete fileWatcher_; + fileWatcher_=0; + } + + inAutoUpdate_=false; +} + +void LogProvider::slotLinesAppend(QStringList lst) +{ + //Check if the task is already running + if(task_) + { + task_->status(VTask::CANCELLED); + task_.reset(); + } + + //Reset the reply + reply_->reset(); + + if(!info_) + { + owner_->infoFailed(reply_); + } + + std::vector vec; + Q_FOREACH(QString s,lst) + { + vec.push_back(s.toStdString()); + } + + reply_->setTextVec(vec); + owner_->infoAppended(reply_); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LogProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,49 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + + +#ifndef VIEWER_SRC_LOGPROVIDER_HPP_ +#define VIEWER_SRC_LOGPROVIDER_HPP_ + +#include +#include + +#include "VDir.hpp" +#include "VInfo.hpp" +#include "InfoProvider.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +class FileWatcher; + +class LogProvider : public QObject, public InfoProvider +{ + Q_OBJECT + +public: + LogProvider(InfoPresenter* owner,QObject* parent=0); + + void visit(VInfoServer*); + void clear(); + void setAutoUpdate(bool); + + public Q_SLOTS: + void slotLinesAppend(QStringList); + + private: + void fetchFile(); + void fetchFile(ServerHandler *server,const std::string& fileName); + void watchFile(const std::string&,size_t); + void stopWatchFile(); + + FileWatcher* fileWatcher_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogViewerCom.cpp ecflow-4.11.1/Viewer/ecflowUI/src/LogViewerCom.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogViewerCom.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogViewerCom.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,115 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifdef ECFLOW_LOGVIEW + +#include "LogViewerCom.hpp" + +#include +#include + +#include "LocalSocketServer.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" + +LogViewerCom::LogViewerCom() : + QObject(0) +{ + if(char* logexe=getenv("ECFLOWUI_LOG_VIEWER")) + { + program_=QString(logexe); + } +} + +void LogViewerCom::closeApp() +{ + if(!logViewerId_.isEmpty()) + { + QLocalSocket* socket = new QLocalSocket(this); + socket->setServerName(logViewerId_); + + socket->connectToServer(QIODevice::WriteOnly); + if(socket->waitForConnected(1000)) + { + socket->write("exit"); + socket->disconnectFromServer(); + socket->waitForDisconnected(1000); + } + } +} + +void LogViewerCom::addToApp(ServerHandler* sh) +{ + if(program_.isEmpty()) + return; + + if(sh) + { + QStringList args; + args << QString::fromStdString(sh->name()) << + QString::fromStdString(sh->host()) << + QString::fromStdString(sh->port()); + + QString logFile; + if(VServer* vs=sh->vRoot()) + { + logFile=QString::fromStdString(vs->findVariable("ECF_LOG",false)); + } + args << logFile; + + start(args); + } +} + +void LogViewerCom::start(QStringList args) +{ + if(program_.isEmpty()) + return; + + if(logViewerId_.isEmpty()) + { + qint64 pid; + if(QProcess::startDetached(program_,args,QString(),&pid)) + { + logViewerId_ = LocalSocketServer::generateServerName("log",pid); + } + } + else + { + //Send message over local socket + QLocalSocket* socket = new QLocalSocket(this); + socket->setServerName(logViewerId_); + socket->connectToServer(QIODevice::WriteOnly); + + if(socket->waitForConnected(1000)) + { + socket->write(args.join("::").toUtf8()); + socket->disconnectFromServer(); + socket->waitForDisconnected(1000); + } + //no connection: proc probably stopped + else + { + //start new process + qint64 pid; + if(QProcess::startDetached(program_,args,QString(),&pid)) + { + logViewerId_ = LocalSocketServer::generateServerName("log",pid); + } + else + { + logViewerId_.clear(); + } + } + + delete socket; + } +} + +#endif // ECFLOW_LOGVIEW diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/LogViewerCom.hpp ecflow-4.11.1/Viewer/ecflowUI/src/LogViewerCom.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/LogViewerCom.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/LogViewerCom.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef LOGVIEWERPROC_HPP +#define LOGVIEWERPROC_HPP + +#ifdef ECFLOW_LOGVIEW + +#include +#include +#include + +#include +#include + +class ServerHandler; +class QLocalSocket; + +class LogViewerCom : public QObject +{ +public: + LogViewerCom(); + + void addToApp(ServerHandler*); + void closeApp(); + +protected: + void start(QStringList); + + QString program_; + QString logViewerId_; +}; + +#endif // ECFLOW_LOGVIEW + +#endif // LOGVIEWERPROC_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.cpp ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,945 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "MainWindow.hpp" + +#include "AboutDialog.hpp" +#include "ChangeNotify.hpp" +#include "ChangeNotifyWidget.hpp" +#include "ClockWidget.hpp" +#include "FilterWidget.hpp" +#include "InfoPanel.hpp" +#include "InfoPanelHandler.hpp" +#include "LogViewerCom.hpp" +#include "MenuConfigDialog.hpp" +#include "NodePathWidget.hpp" +#include "NodePanel.hpp" +#include "PropertyDialog.hpp" +#include "ServerComInfoWidget.hpp" +#include "ServerHandler.hpp" +#include "ServerList.hpp" +#include "ServerListDialog.hpp" +#include "ServerListSyncWidget.hpp" +#include "SessionHandler.hpp" +#include "SaveSessionAsDialog.hpp" +#include "CommandOutputDialog.hpp" +#include "TextFormat.hpp" +#include "UiLog.hpp" +#include "VConfig.hpp" +#include "VIcon.hpp" +#include "VSettings.hpp" +#include "Version.hpp" +#include "WidgetNameProvider.hpp" + +#include +#include + +bool MainWindow::quitStarted_=false; +QList MainWindow::windows_; +int MainWindow::maxWindowNum_=25; + +#ifdef ECFLOW_LOGVIEW +LogViewerCom* MainWindow::logCom_=NULL; +#endif + +MainWindow::MainWindow(QStringList idLst,QWidget *parent) : + QMainWindow(parent), + serverSyncNotifyTb_(0) +{ + setupUi(this); + + //Assigns name to each object + setObjectName("win_" + QString::number(windows_.count())); + + setAttribute(Qt::WA_DeleteOnClose); + + //the window title + winTitle_=new MainWindowTitleHandler(this); + winTitle_->update(); + + //Create the main layout + QVBoxLayout* layout=new QVBoxLayout(); + layout->setContentsMargins(0,0,0,0); + QWidget *w=new QWidget(this); + w->setObjectName("c"); + w->setLayout(layout); + setCentralWidget(w); + + //Servers menu menu + serverFilterMenu_=new ServerFilterMenu(menuServer); + + //Create a node panel + nodePanel_=new NodePanel(this); + layout->addWidget(nodePanel_); + + connect(nodePanel_,SIGNAL(currentWidgetChanged()), + this,SLOT(slotCurrentChangedInPanel())); + + connect(nodePanel_,SIGNAL(selectionChangedInCurrent(VInfo_ptr)), + this,SLOT(slotSelectionChanged(VInfo_ptr))); + + connect(nodePanel_,SIGNAL(contentsChanged()), + this,SLOT(slotContentsChanged())); + + //-------------- + // Toolbar + //-------------- + + //Add server refresh widget to the front of the toolbar + serverComWidget_=new ServerRefreshInfoWidget(actionRefreshSelected,this); + Q_ASSERT(actionSearch); + viewToolBar->insertWidget(actionSearch,serverComWidget_); + //viewToolBar->addWidget(serverComWidget_); + + connect(serverComWidget_,SIGNAL(serverSettingsEditRequested(ServerHandler*)), + this,SLOT(slotEditServerSettings(ServerHandler*))); + + + //insert a spacer after the the server refresh widget + QWidget* spacer = new QWidget(); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + //viewToolBar->insertWidget(actionSearch,spacer); + viewToolBar->addWidget(spacer); + + //Add more actions + addInfoPanelActions(viewToolBar); + + //Add shortcuts to action tooltips + Viewer::addShortCutToToolTip(viewToolBar->actions()); + + //Initialise actions based on selection + actionRefreshSelected->setEnabled(false); + actionResetSelected->setEnabled(false); + +#ifdef ECFLOW_LOGVIEW + connect(actionServerLoadViewer,SIGNAL(triggered()), + this,SLOT(slotServerLoad())); +#else + actionServerLoadViewer->setEnabled(false); +#endif + + //-------------- + //Status bar + //-------------- + + //Add server list sync notification + if(ServerList::instance()->hasSyncChange()) + { + //Add server list sync notification + serverSyncNotifyTb_=new QToolButton(this); + serverSyncNotifyTb_->setAutoRaise(true); + serverSyncNotifyTb_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); + serverSyncNotifyTb_->setIcon(QPixmap(":/viewer/info.svg")); + serverSyncNotifyTb_->setText("Server list updated"); + serverSyncNotifyTb_->setToolTip("Your local copy of the system server list was updated. Click to see the changes."); + statusBar()->addWidget(serverSyncNotifyTb_); + + connect(serverSyncNotifyTb_,SIGNAL(clicked(bool)), + this,SLOT(slotServerSyncNotify(bool))); + } + + //Add notification widget + ChangeNotifyWidget* chw=new ChangeNotifyWidget(this); + statusBar()->addPermanentWidget(chw); + + //Add clock widget + clockWidget_=new ClockWidget(this); + statusBar()->addPermanentWidget(clockWidget_); + + //serverComWidget_=new ServerComLineDisplay(this); + //statusBar()->addPermanentWidget(serverComWidget_); + + //Assigns name to each object + WidgetNameProvider::nameChildren(this); + + //actionSearch->setVisible(false); +} + +MainWindow::~MainWindow() +{ + UiLog().dbg() << "MainWindow --> destructor"; + delete winTitle_; + serverFilterMenu_->aboutToDestroy(); +} + +void MainWindow::init(MainWindow *win) +{ + nodePanel_->init(); + + if(!win) + return; +} + +void MainWindow::addInfoPanelActions(QToolBar *toolbar) +{ + for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); + it != InfoPanelHandler::instance()->panels().end(); ++it) + { + if((*it)->show().find("toolbar") != std::string::npos) + { + QAction *ac=toolbar->addAction(QString::fromStdString((*it)->label())); + QPixmap pix(":/viewer/" + QString::fromStdString((*it)->icon())); + ac->setObjectName(QString::fromStdString((*it)->label())); + ac->setIcon(QIcon(pix)); + ac->setData(QString::fromStdString((*it)->name())); + ac->setToolTip(QString::fromStdString((*it)->buttonTooltip())); + + connect(ac,SIGNAL(triggered()), + this,SLOT(slotOpenInfoPanel())); + + infoPanelActions_ << ac; + } + } +} + +ServerHandler* MainWindow::selectedServer() const +{ + return(selection_)?(selection_->server()):0; +} + +//============================================================== +// +// File menu +// +//============================================================== + +void MainWindow::on_actionNewTab_triggered() +{ + nodePanel_->slotNewTab(); +} + +void MainWindow::on_actionNewWindow_triggered() +{ + MainWindow::openWindow("",this); +} + +void MainWindow::on_actionClose_triggered() +{ + close(); +} + +void MainWindow::on_actionQuit_triggered() +{ + MainWindow::aboutToQuit(this); +} + +void MainWindow::on_actionRefresh_triggered() +{ + nodePanel_->refreshCurrent(); +} + +void MainWindow::on_actionReset_triggered() +{ + nodePanel_->resetCurrent(); +} + +void MainWindow::on_actionRefreshSelected_triggered() +{ + if(selection_ && selection_.get()) + { + if(ServerHandler* s=selection_->server()) + { + s->refresh(); + } + } +} + +void MainWindow::on_actionResetSelected_triggered() +{ + if(selection_ && selection_.get()) + { + if(ServerHandler* s=selection_->server()) + { + s->reset(); + } + } +} + +void MainWindow::on_actionPreferences_triggered() +{ + startPreferences(this,""); +} + +void MainWindow::on_actionManageSessions_triggered() +{ + QMessageBox::information(0, tr("Manage Sessions"), + tr("To manage sessions, please restart ecFlowUI with the -s command-line option")); +} + +void MainWindow::slotConfigChanged() +{ + configChanged(this); +} + +void MainWindow::on_actionConfigureNodeMenu_triggered() +{ + MenuConfigDialog menuConfigDialog; + + if(menuConfigDialog.exec() == QDialog::Accepted) + { + } +} + +void MainWindow::on_actionSearch_triggered() +{ + //It takes ownership of the dialogue. + nodePanel_->addSearchDialog(); +} + +void MainWindow::on_actionNotification_triggered() +{ + ChangeNotify::showDialog(); +} + +void MainWindow::on_actionCommandOutput_triggered() +{ + CommandOutputDialog::showDialog(); +} + +void MainWindow::on_actionManageServers_triggered() +{ + ServerListDialog dialog(ServerListDialog::SelectionMode,nodePanel_->serverFilter(),this); + dialog.exec(); +} + +void MainWindow::on_actionAddTreeWidget_triggered() +{ + nodePanel_->addToDashboard("tree"); +} + +void MainWindow::on_actionAddTableWidget_triggered() +{ + nodePanel_->addToDashboard("table"); +} + +void MainWindow::on_actionAddInfoPanel_triggered() +{ + nodePanel_->addToDashboard("info"); +} + +void MainWindow::on_actionAbout_triggered() +{ + AboutDialog d; + d.exec(); +} + +void MainWindow::on_actionSaveSessionAs_triggered() +{ + SaveSessionAsDialog d; + d.exec(); +} + +void MainWindow::slotCurrentChangedInPanel() +{ + slotSelectionChanged(nodePanel_->currentSelection()); + + //filterWidget_->reload(nodePanel_->viewFilter()); + + serverFilterMenu_->reload(nodePanel_->serverFilter()); + + //breadcrumbs_->setPath(folderPanel_->currentFolder()); + //slotUpdateNavigationActions(folderPanel_->folderNavigation()); + + //updateIconSizeActionState(); + //updateSearchPanel(); +} + +//The selection changed in one of the views +void MainWindow::slotSelectionChanged(VInfo_ptr info) +{ + selection_=info; + + //Get the set of visible info panel tabs for the selection + std::vector ids; + InfoPanelHandler::instance()->visible(selection_,ids); + + //Set status of the info panel actions in the toolbar accordingly + Q_FOREACH(QAction* ac,infoPanelActions_) + { + ac->setEnabled(false); + + std::string name=ac->data().toString().toStdString(); + + for(std::vector::const_iterator it=ids.begin(); it != ids.end(); ++it) + { + if((*it)->name() == name) + { + ac->setEnabled(true); + break; + } + } + } + + //Update the refres action/info to the selection + updateRefreshActions(); + + //Update the window titlebar + winTitle_->update(); +} + +void MainWindow::updateRefreshActions() +{ + ServerHandler* s=0; + + QString serverName; + if(selection_) + { + s=selection_->server(); + } + + serverComWidget_->setServer(s); + + + bool hasSel=(selection_!= 0); + actionRefreshSelected->setEnabled(hasSel); + actionResetSelected->setEnabled(hasSel); +} + + +void MainWindow::slotOpenInfoPanel() +{ + if(QAction* ac=static_cast(sender())) + { + std::string name=ac->data().toString().toStdString(); + nodePanel_->openDialog(selection_,name); + } +} + +void MainWindow::reloadContents() +{ + nodePanel_->reload(); +} + +//Rerender all the views and breadcrumbs +void MainWindow::rerenderContents() +{ + nodePanel_->rerender(); +} + +void MainWindow::slotContentsChanged() +{ + MainWindow::saveContents(NULL); +} + +bool MainWindow::selectInTreeView(VInfo_ptr info) +{ + return nodePanel_->selectInTreeView(info); +} + +void MainWindow::slotServerSyncNotify(bool) +{ + if(serverSyncNotifyTb_) + { + serverSyncNotifyTb_->hide(); + MainWindow::hideServerSyncNotify(this); + + ServerListDialog dialog(ServerListDialog::SelectionMode,nodePanel_->serverFilter(),this); + dialog.showSysSyncLog(); + dialog.exec(); + } +} + +void MainWindow::hideServerSyncNotify() +{ + if(serverSyncNotifyTb_) + serverSyncNotifyTb_->hide(); +} + +void MainWindow::slotEditServerSettings(ServerHandler* s) +{ + VInfo_ptr info=VInfoServer::create(s); + nodePanel_->openDialog(info,"server_settings"); +} + +void MainWindow::slotServerLoad() +{ +#ifdef ECFLOW_LOGVIEW + ServerHandler* s=0; + QString serverName; + if(selection_) + { + s=selection_->server(); + if(!logCom_) + { + logCom_ = new LogViewerCom(); + } + logCom_->addToApp(s); + } +#endif +} + +//============================================================== +// +// Close and quit +// +//============================================================== + +void MainWindow::closeEvent(QCloseEvent* event) +{ + if(MainWindow::aboutToClose(this)) + { + windows_.removeOne(this); + event->accept(); + } + else + { + event->ignore(); + } +} + +//On quitting we need to call the destructor of all the servers shown in the gui. +//This will guarantee that the server log out is properly done. +//Unfortunately when we quit qt does not call the destructor of the mainwindows. +//We tried to explicitely delete the mainwindows here but it caused a crash on the +//leap42 system. So here we only delete the nodePanel in the mainwindow. This panel +//contains all the tabs. Each tab contains a serverfilter and when the serverfilters +//get deleted in the end the destructors of the servers will be called. +void MainWindow::cleanUpOnQuit() +{ + Q_ASSERT(quitStarted_==true); + serverFilterMenu_->aboutToDestroy(); + delete nodePanel_; +} + +//==================================================== +// +// Read/write settings +// +//==================================================== + +void MainWindow::writeSettings(VComboSettings *vs) +{ + //Qt settings + vs->putQs("geometry",saveGeometry()); + vs->putQs("state",saveState()); + +//See ECFLOW-1090 +#if 0 + vs->putQs("minimized",(windowState() & Qt::WindowMinimized)?1:0); +#endif + + //Other setting + vs->put("infoPanelCount",findChildren().count()); + + //Saves nodePanel + nodePanel_->writeSettings(vs); +} + +void MainWindow::readSettings(VComboSettings *vs) +{ + int cnt=vs->get("infoPanelCount",0); + for(int i=0; i < cnt ; i++) + { + //addInfoPanel(); + } + + nodePanel_->readSettings(vs); + + //See ECFLOW-1090 +#if 0 + if(vs->getQs("minimized").toInt()== 1) + { + setWindowState(windowState() | Qt::WindowMinimized); + } +#endif + + if(vs->containsQs("geometry")) + restoreGeometry(vs->getQs("geometry").toByteArray()); + + if(vs->containsQs("state")) + restoreState(vs->getQs("state").toByteArray()); +} + +//==================================================== +// +// Static methods +// +//==================================================== + +MainWindow* MainWindow::makeWindow(VComboSettings* vs) +{ + MainWindow* win=MainWindow::makeWindow(); + win->readSettings(vs); + return win; +} + +MainWindow* MainWindow::makeWindow() +{ + QStringList idLst; + return MainWindow::makeWindow(idLst); +} + +MainWindow* MainWindow::makeWindow(QString id) +{ + QStringList idLst; + idLst << id; + return MainWindow::makeWindow(idLst); +} + +MainWindow* MainWindow::makeWindow(QStringList idLst) +{ + MainWindow *win=new MainWindow(idLst); + windows_ << win; + return win; +} + +void MainWindow::openWindow(QString id,QWidget *fromW) +{ + MainWindow* win=MainWindow::makeWindow(id); + win->init(findWindow(fromW)); + win->show(); +} + +void MainWindow::openWindow(QStringList idLst,QWidget *fromW) +{ + MainWindow* win=MainWindow::makeWindow(idLst); + win->init(findWindow(fromW)); + win->show(); +} + +void MainWindow::showWindows() +{ + Q_FOREACH(MainWindow *win,windows_) + win->show(); +} + +void MainWindow::configChanged(MainWindow*) +{ + Q_FOREACH(MainWindow *win,windows_) + win->rerenderContents(); +} + +void MainWindow::lookUpInTree(VInfo_ptr info) +{ + Q_FOREACH(MainWindow *win,windows_) + if(win->selectInTreeView(info)) + return; +} + +void MainWindow::hideServerSyncNotify(MainWindow*) +{ + Q_FOREACH(MainWindow *win,windows_) + win->hideServerSyncNotify(); +} + +void MainWindow::cleanUpOnQuit(MainWindow*) +{ + Q_FOREACH(MainWindow *win,windows_) + win->cleanUpOnQuit(); +} + +//Return true if close is allowed, false otherwise +bool MainWindow::aboutToClose(MainWindow* win) +{ + //If quit has already stared we ignore the close signal from + //the main windows. + if(quitStarted_) + { + return false; + } + + //Otherwise + else + { + if(windows_.count() > 1) + { + int tabCnt=win->nodePanel_->count(); + if(tabCnt > 1) + { + if(QMessageBox::question(0,tr("Confirm close"),tr("You are about to close ") + QString::number(tabCnt) + tr(" tabs. Are you sure you want to continue?"), + QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return false; + } + } + } + else if(windows_.count() == 1) + { + return MainWindow::aboutToQuit(win); + } + return true; + } +} + +bool MainWindow::aboutToQuit(MainWindow* topWin) +{ +#if 0 + if(QMessageBox::question(0,tr("Confirm quit"), + tr("Do you want to quit ") + + QString::fromStdString(VConfig::instance()->appName()) + "?", + QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Yes) + { +#endif + quitStarted_=true; + +#ifdef ECFLOW_LOGVIEW + if(logCom_) + logCom_->closeApp(); +#endif + + //Save browser settings + MainWindow::save(topWin); + + // handle session cleanup + // temporary sessions can be saved or deleted + SessionItem *si = SessionHandler::instance()->current(); + if (si->temporary()) + { + if (si->askToPreserveTemporarySession()) + { + if(QMessageBox::question(0,tr("Delete temporary session?"), + tr("This was a temporary session - would you like to preserve it for future use?"), + QMessageBox::Yes | QMessageBox::No,QMessageBox::No) == QMessageBox::No) + SessionHandler::destroyInstance(); + } + else // if askToPreserveTemporarySession() is false, then we assume we want to delete + { + SessionHandler::destroyInstance(); + } + } + + //Ensure the ServerHandler destructors are called + MainWindow::cleanUpOnQuit(topWin); + + //Exit ecFlowView + QApplication::quit(); +#if 0 + } +#endif + + return false; +} + +void MainWindow::init() +{ + SessionItem* cs=SessionHandler::instance()->current(); + assert(cs); + + VComboSettings vs(cs->sessionFile(),cs->windowFile()); + + //Read configuration. If it fails we create an empty window!! + if(!vs.read()) + { + MainWindow::makeWindow(&vs); + return; + } + + //Get number of windows and topWindow index. + int cnt=vs.get("windowCount",0); + int topWinId=vs.get("topWindowId",-1); + + if(cnt > maxWindowNum_) + { + cnt=maxWindowNum_; + } + + //Create all windows (except the topWindow) in a loop. The + //topWindow should be created last so that it should always appear on top. + std::string winPattern("window_"); + for(int i=0; i < cnt; i++) + { + if(i != topWinId) + { + std::string id=winPattern + boost::lexical_cast(i); + if(vs.contains(id)) + { + vs.beginGroup(id); + MainWindow::makeWindow(&vs); + vs.endGroup(); + } + } + } + + //Create the topwindow + if(topWinId != -1) + { + std::string id=winPattern + boost::lexical_cast(topWinId); + if(vs.contains(id)) + { + vs.beginGroup(id); + MainWindow::makeWindow(&vs); + vs.endGroup(); + } + } + + //If now windows were created we need to create an empty one + if(windows_.count() == 0) + { + MainWindow::makeWindow(&vs); + } +} + +void MainWindow::save(MainWindow *topWin) +{ + MainWindow::saveContents(topWin); + + //Save global config + VConfig::instance()->saveSettings(); + + //Save server list + ServerHandler::saveSettings(); + + //Save icon name list + VIcon::saveLastNames(); +} + +void MainWindow::saveContents(MainWindow *topWin) +{ + SessionItem* cs=SessionHandler::instance()->current(); + assert(cs); + + VComboSettings vs(cs->sessionFile(),cs->windowFile()); + + //We have to clear it so that not to remember all the previous windows + vs.clear(); + + //Add total window number and id of active window + vs.put("windowCount",windows_.count()); + vs.put("topWindowId",windows_.indexOf(topWin)); + + //Save info for all the windows + for(int i=0; i < windows_.count(); i++) + { + std::string id="window_"+boost::lexical_cast(i); + vs.beginGroup(id); + windows_.at(i)->writeSettings(&vs); + vs.endGroup(); + } + + //Write to json + vs.write(); +} + + +void MainWindow::reload() +{ + Q_FOREACH(MainWindow *w,windows_) + { + w->reloadContents(); + } +} + +void MainWindow::saveSession(SessionItem* s) +{ + +} + +MainWindow* MainWindow::findWindow(QWidget *childW) +{ + Q_FOREACH(MainWindow *w,windows_) + { + if(static_cast(w) == childW->window()) + return w; + } + + return 0; +} + +MainWindow* MainWindow::firstWindow() +{ + return (!windows_.isEmpty())?(windows_[0]):NULL; +} + +void MainWindow::startPreferences(QString option) +{ + if(windows_.count() > 0) + startPreferences(windows_.front(),option); +} + +void MainWindow::startPreferences(MainWindow *w,QString option) +{ + Q_ASSERT(w); + + PropertyDialog* d=new PropertyDialog; //belongs to the whole app + + d->showPage(option); + + connect(d,SIGNAL(configChanged()), + w,SLOT(slotConfigChanged())); + + if(d->exec() == QDialog::Accepted) + { + if(d->isConfigChanged()) + { + configChanged(w); + } + } + + delete d; +} + +void MainWindow::updateMenuMode(ServerHandler* sh) +{ + Q_FOREACH(MainWindow *w,windows_) + { + w->winTitle_->update(sh); + } +} + +//-------------------------------------------------------- +// +// MainWindowTitle (class to handle the MainWindow title) +// +//-------------------------------------------------------- + +MainWindowTitleHandler::MainWindowTitleHandler(MainWindow *win) : win_(win) +{ + Q_ASSERT(win_); + std::vector propVec; +} + +MainWindowTitleHandler::~MainWindowTitleHandler() +{ +} + +void MainWindowTitleHandler::update(ServerHandler *sh) +{ + Q_ASSERT(win_); + if(sh == win_->selectedServer()) + update(); +} + +void MainWindowTitleHandler::update() +{ + Q_ASSERT(win_); + + char *userTitle = getenv("ECFLOWUI_TITLE"); + std::string mainTitle = (userTitle != NULL) ? std::string(userTitle) + " (" + ecf::Version::raw() + ")" + : VConfig::instance()->appLongName(); + QString title=QString::fromStdString(mainTitle); + + if(ServerHandler* sh=win_->selectedServer()) + { + QString menuMode=sh->nodeMenuMode(); + if(!menuMode.isEmpty()) + title+=" - (menu: " + menuMode + ")"; + } + + // add the name of the session to the title bar? + QString sessionName = QString::fromStdString(SessionHandler::instance()->current()->name()); + if (sessionName != "default") + title+= " - (session: " + sessionName + ")"; + + win_->setWindowTitle(title); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.hpp ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,149 @@ +#ifndef MAINWINDOW_HPP_ +#define MAINWINDOW_HPP_ + +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include + +#include "ui_MainWindow.h" + +#include "VInfo.hpp" + +#include + +class QActionGroup; +class QLabel; +class QToolButton; +class QProcess; + +class ClockWidget; +class InfoPanel; +class LogViewerCom; +class NodePanel; +class ServerRefreshInfoWidget; +class ServerFilterMenu; +class SessionItem; +class VComboSettings; + +class MainWindow; + +class MainWindowTitleHandler +{ + friend class MainWindow; +public: + MainWindowTitleHandler(MainWindow*); + ~MainWindowTitleHandler(); + + void update(); + +protected: + void update(ServerHandler*); + + MainWindow* win_; +}; + +class MainWindow : public QMainWindow, private Ui::MainWindow +{ + Q_OBJECT + +public: + MainWindow(QStringList,QWidget *parent=0); + ~MainWindow(); + + ServerHandler* selectedServer() const; + + static void init(); + static void showWindows(); + static void openWindow(QString id,QWidget *fromW=0); + static void openWindow(QStringList id,QWidget *fromW=0); + static void reload(); + static void saveSession(SessionItem*); + static void lookUpInTree(VInfo_ptr); + static void startPreferences(QString); + static void updateMenuMode(ServerHandler*); + static MainWindow* firstWindow(); + +protected Q_SLOTS: + void on_actionNewTab_triggered(); + void on_actionNewWindow_triggered(); + void on_actionClose_triggered(); + void on_actionQuit_triggered(); + void on_actionRefresh_triggered(); + void on_actionReset_triggered(); + void on_actionRefreshSelected_triggered(); + void on_actionResetSelected_triggered(); + void on_actionConfigureNodeMenu_triggered(); + void on_actionManageServers_triggered(); + void on_actionAddTreeWidget_triggered(); + void on_actionAddTableWidget_triggered(); + void on_actionAddInfoPanel_triggered(); + void on_actionPreferences_triggered(); + void on_actionSearch_triggered(); + void on_actionNotification_triggered(); + void on_actionCommandOutput_triggered(); + void on_actionAbout_triggered(); + void on_actionSaveSessionAs_triggered(); + void on_actionManageSessions_triggered(); + + void slotCurrentChangedInPanel(); + void slotSelectionChanged(VInfo_ptr); + void slotOpenInfoPanel(); + void slotConfigChanged(); + void slotContentsChanged(); + void slotServerSyncNotify(bool); + void slotEditServerSettings(ServerHandler* s); + void slotServerLoad(); + +private: + void init(MainWindow*); + void closeEvent(QCloseEvent*); + void addInfoPanelActions(QToolBar *toolbar); + void reloadContents(); + void rerenderContents(); + bool selectInTreeView(VInfo_ptr info); + void updateRefreshActions(); + void hideServerSyncNotify(); + void cleanUpOnQuit(); + + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + + static MainWindow* makeWindow(VComboSettings* vs); + static MainWindow *makeWindow(); + static MainWindow *makeWindow(QString id); + static MainWindow *makeWindow(QStringList idLst); + static bool aboutToClose(MainWindow*); + static bool aboutToQuit(MainWindow*); + static void save(MainWindow *); + static void saveContents(MainWindow *); + static MainWindow* findWindow(QWidget *childW); + static void configChanged(MainWindow *); + static void hideServerSyncNotify(MainWindow*); + static void cleanUpOnQuit(MainWindow *); + static void startPreferences(MainWindow *,QString); + + ServerFilterMenu* serverFilterMenu_; + NodePanel* nodePanel_; + QList infoPanelActions_; + VInfo_ptr selection_; + QToolButton* serverSyncNotifyTb_; + ServerRefreshInfoWidget *serverComWidget_; + MainWindowTitleHandler* winTitle_; + ClockWidget* clockWidget_; + + static bool quitStarted_; + static QList windows_; + static int maxWindowNum_; + + static LogViewerCom* logCom_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.ui ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/MainWindow.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MainWindow.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,361 @@ + + + MainWindow + + + + 0 + 0 + 852 + 675 + + + + ecflow_ui + + + false + + + + + + 740 + 640 + 52 + 14 + + + + TextLabel + + + + + + + 0 + 0 + 852 + 24 + + + + false + + + + true + + + &File + + + + + + + + + + + + + + true + + + &Help + + + + + + true + + + Ser&vers + + + + + + + Pa&nels + + + + + + + + &Tools + + + + + + + + + + + + &Refresh + + + + + + + + + + + + + + + + + + View toolbar + + + false + + + Qt::AllToolBarAreas + + + TopToolBarArea + + + false + + + + + + + + :/viewer/close.svg:/viewer/close.svg + + + &Close + + + Close window + + + Ctrl+W + + + + + + :/viewer/images/exit.svg:/viewer/images/exit.svg + + + &Quit + + + Ctrl+Q + + + + + + :/viewer/images/configure.svg:/viewer/images/configure.svg + + + &Preferences ... + + + + + New &tab + + + Ctrl+T + + + + + New &window + + + + + Refresh all servers &in tab + + + Refresh <b>all servers</b> in current tab + + + Shift+F5 + + + + + &Reset all servers in tab + + + Reset + + + + + Configure Node &Menu + + + false + + + + + Contents + + + View contents + + + + + + :/viewer/manage_server.svg:/viewer/manage_server.svg + + + &Manage servers ... + + + Manage servers + + + Ctrl+N + + + + + + :/viewer/images/add_info.svg:/viewer/images/add_info.svg + + + Add &info panel + + + + + + :/viewer/images/add_tree.svg:/viewer/images/add_tree.svg + + + Add &tree view panel + + + Add tree widget + + + + + + :/viewer/images/add_table.svg:/viewer/images/add_table.svg + + + Add ta&ble view panel + + + Add table widget + + + + + &About EcflowUI ... + + + + + true + + + &Manage sessions ... + + + true + + + + + + :/viewer/search.svg:/viewer/search.svg + + + &Search ... + + + Start search dialogue + + + + + + :/viewer/reload_green.svg:/viewer/reload_green.svg + + + R&efresh selected server + + + F5 + + + + + Reset &selected server + + + Reset <b>selected</b> server in current tab + + + + + S&ave session as... + + + false + + + + + + :/viewer/notification.svg:/viewer/notification.svg + + + &Notifications ... + + + Start notifications dialogue + + + + + Shell command &output ... + + + Start shell command output dialogue + + + + + + :/viewer/monitor.svg:/viewer/monitor.svg + + + Server load (standalone app) ... + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ManualItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ManualItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ManualItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ManualItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,145 @@ +//============================================================================ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ManualItemWidget.hpp" + +#include "Highlighter.hpp" +#include "InfoProvider.hpp" +#include "MessageLabel.hpp" +#include "VConfig.hpp" +#include "VReply.hpp" + +ManualItemWidget::ManualItemWidget(QWidget *parent) : CodeItemWidget(parent) +{ + fileLabel_->hide(); + messageLabel_->setShowTypeTitle(false); + messageLabel_->hide(); + textEdit_->setShowLineNumbers(false); + + //The document becomes the owner of the highlighter + new Highlighter(textEdit_->document(),"manual"); + + infoProvider_=new ManualProvider(this); + + //Editor font + textEdit_->setFontProperty(VConfig::instance()->find("panel.manual.font")); +} + +ManualItemWidget::~ManualItemWidget() +{ +} + +QWidget* ManualItemWidget::realWidget() +{ + return this; +} + +void ManualItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + info_=info; + messageLabel_->hide(); + + //Info must be a node + if(info_ && info_->isNode() && info_->node()) + { + reloadTb_->setEnabled(false); + infoProvider_->info(info_); + } +} + +void ManualItemWidget::clearContents() +{ + InfoPanelItem::clear(); + textEdit_->clear(); + messageLabel_->hide(); + reloadTb_->setEnabled(true); + clearCurrentFileName(); +} + +void ManualItemWidget::infoReady(VReply* reply) +{ + Q_ASSERT(reply); + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); + + if(reply->hasWarning()) + { + messageLabel_->showWarning(QString::fromStdString(reply->warningText())); + } + else if(reply->hasInfo()) + { + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); + } + else if(s.isEmpty()) + { + messageLabel_->showInfo("Manual is not available"); + } + + fileLabel_->update(reply); + reloadTb_->setEnabled(true); + setCurrentFileName(reply->fileName()); +} + +void ManualItemWidget::infoProgress(VReply* reply) +{ + // QString s=QString::fromStdString(reply->text()); + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); +} + +void ManualItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); + messageLabel_->showError(s); + textEdit_->setPlainText( +"# You can create a man page with " +"a file .man located in or as " +"/.man"); + reloadTb_->setEnabled(true); +} + +void ManualItemWidget::reloadRequested() +{ + reload(info_); +} + +void ManualItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + reloadTb_->setEnabled(false); + } + //Resume + else + { + if(info_ && info_->node()) + { + reloadTb_->setEnabled(true); + } + else + { + clearContents(); + } + } + } +} + + +static InfoPanelItemMaker maker1("manual"); + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ManualItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ManualItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ManualItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ManualItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef MANUALITEMWIDGET_HPP_ +#define MANUALITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" +#include "CodeItemWidget.hpp" + +class ManualItemWidget : public CodeItemWidget, public InfoPanelItem +{ +public: + explicit ManualItemWidget(QWidget *parent=0); + ~ManualItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected: + void updateState(const ChangeFlags&); + void reloadRequested(); +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,114 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "MenuHandler.hpp" +#include "MenuConfigDialog.hpp" + + +MenuConfigDialog::MenuConfigDialog(QWidget *parent) : QDialog(parent) +{ + setupUi(this); + //connect (insertPushButton_, SIGNAL(clicked()), this, SLOT(insertCurrentText())); + connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); + + + + // load up the user menu file if it exists XXXX + + if (0) + { + } + else + { + // otherwise create a dummy menu to start with + + Menu *menu = new Menu("UserTemp"); + MenuHandler::addMenu(menu); + + MenuItem *item = new MenuItem("UserTemp"); + item->setAsSubMenu(); + item->setCommand("NoCommand"); + + MenuHandler::addItemToMenu(item, "Node"); + + + //MenuItem *item2 = new MenuItem("Com"); + //item2->setCommand("ecflow --whatever"); + //MenuHandler::addItemToMenu(item2, "UserTemp"); + + //updateMenuTree(menu); + updateMenuTree(MenuHandler::findMenu("Node")); + } + + +} + + +void MenuConfigDialog::updateMenuTree(Menu *menu) +{ + menuTreeWidget_->clear(); + menuTreeWidget_->setColumnCount(1); + + + QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(menuTreeWidget_); + topLevelItem->setText(0, QString::fromStdString(menu->name())); + //topLevelItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); + + addChildrenToMenuTree(menu, topLevelItem); + +} + + +void MenuConfigDialog::addChildrenToMenuTree(Menu *menu, QTreeWidgetItem *parent) +{ + if(!menu) + return; + + std::vector&items = menu->items(); + + for (std::vector::iterator itItems = items.begin(); itItems != items.end(); ++itItems) + { + QTreeWidgetItem *item = new QTreeWidgetItem(parent); + item->setText(0, QString::fromStdString((*itItems)->name())); + item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); + + if ((*itItems)->isSubMenu()) + { + Menu *submenu = MenuHandler::findMenu((*itItems)->name()); + if(menu) + { + addChildrenToMenuTree(submenu, item); + } + } + else if ((*itItems)->isDivider()) + { + //qmenu->addSeparator(); + } + else + { + //QAction *action = (*itItems)->action(); + //qmenu->addAction(action); + //action->setParent(parent); + //action->setEnabled(compatible); + } + } +} + + + +//void CommandDesignerWidget::insertCurrentText() +//{ +// //commandLineEdit_->setText("Silly"); +// commandLineEdit_->insert(componentsComboBox_->currentText() + " "); +//} + +void MenuConfigDialog::reject() +{ + QDialog::reject(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,88 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + + +#ifndef MENUCONFIGDIALOG_HPP_ +#define MENUCONFIGDIALOG_HPP_ + +#include +#include +#include + +#include "MenuHandler.hpp" + +class ConfigTreeWidget : public QTreeWidget +{ +public: + ConfigTreeWidget() + { + } + + explicit ConfigTreeWidget(QSplitter*s) : QTreeWidget(s) + { + resize(200, 300); + + setSelectionMode(QAbstractItemView::SingleSelection); + setDragEnabled(true); + viewport()->setAcceptDrops(true); + setDropIndicatorShown(true); + setDragDropMode(QAbstractItemView::InternalMove); +/* + QTreeWidgetItem* parentItem = new QTreeWidgetItem(this); + parentItem->setText(0, "Test"); + parentItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled); + + for(int i = 0; i < 10; ++i) + { + QTreeWidgetItem* pItem = new QTreeWidgetItem(parentItem); + pItem->setText(0, QString("Number %1").arg(i) ); + pItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); + pItem->addChild(pItem); + }*/ + }; + +/*private: + virtual void dropEvent(QDropEvent * event) + { + QModelIndex droppedIndex = indexAt( event->pos() ); + + if( !droppedIndex.isValid() ) + return; + + QTreeWidget::dropEvent(event); + }*/ +}; + + +#include "ui_MenuConfigDialog.h" + +class MenuConfigDialog : public QDialog, private Ui::MenuConfigDialog +{ + Q_OBJECT + +public: + explicit MenuConfigDialog(QWidget *parent = 0); + ~MenuConfigDialog() {}; + + void updateMenuTree(Menu *menu); + + void reject(); + + +//public Q_SLOTS: +// void insertCurrentText(); + + +private: + void addChildrenToMenuTree(Menu *menu, QTreeWidgetItem *parent); + +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/MenuConfigDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MenuConfigDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,256 @@ + + + MenuConfigDialog + + + + 0 + 0 + 882 + 627 + + + + Dialog + + + + + + + 16777215 + 64 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Qt::Horizontal + + + + 553 + 20 + + + + + + + + &New Item + + + + + + + &Duplicate Item + + + + + + + New &Submenu + + + + + + + + + + Qt::Horizontal + + + + + 1 + + + + + + + QFormLayout::ExpandingFieldsGrow + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + Name: + + + + + + + + + + Valid Node Types: + + + + + + + + + All + + + + + + + Task + + + + + + + Alias + + + + + + + Family + + + + + + + Suite + + + + + + + Server + + + + + + + Unselect all + + + + + + + + + Valid States: + + + + + + + + + Unknown + + + + + + + Queued + + + + + + + Suspended + + + + + + + Aborted + + + + + + + Unselect all + + + + + + + + + Valid Attributes: + + + + + + + CheckBox + + + + + + + Command: + + + + + + + + + + + + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + CommandDesignerWidget + QLineEdit +
      CommandDesignerWidget.hpp
      +
      + + ConfigTreeWidget + QTreeWidget +
      MenuConfigDialog.hpp
      +
      +
      + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MenuHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/MenuHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MenuHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MenuHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,920 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Str.hpp" +#include "MenuHandler.hpp" +#include "ServerHandler.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "VConfig.hpp" +#include "VNode.hpp" +#include "VProperty.hpp" +#include "CustomCommandHandler.hpp" + +int MenuItem::idCnt_=0; + +std::vector MenuHandler::menus_; +MenuHandler::ConfirmationMap MenuHandler::commandsWhichRequireConfirmation_; +TrueNodeCondition MenuHandler::trueCond_; +FalseNodeCondition MenuHandler::falseCond_; + + +MenuHandler::MenuHandler() +{ + menus_.clear(); +} + + +// --------------------------------------------------------- +// MenuHandler::readMenuConfigFile +// Read the given config file and store the resulting menus +// internally. +// --------------------------------------------------------- + +bool MenuHandler::readMenuConfigFile(const std::string &configFile) +{ + // parse the response using the boost JSON property tree parser + + using boost::property_tree::ptree; + ptree pt; + + try + { + read_json(configFile, pt); + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + std::string errorMessage = e.what(); + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse JSON menu file : " + errorMessage)); + return false; + } + + + + // iterate over the top level of the tree + + for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) + { + // parse the menu definitions? + + if (itTopLevel->first == "menus") + { + UiLog().dbg() << "Menus:"; + + ptree const &menusDef = itTopLevel->second; + + // iterate through all the menus + + for (ptree::const_iterator itMenus = menusDef.begin(); itMenus != menusDef.end(); ++itMenus) + { + ptree const &menuDef = itMenus->second; + + std::string cname = menuDef.get("name", "NoName"); + UiLog().dbg() << " " << cname; + Menu *menu = new Menu(cname); + + //ptree const &menuModesDef = menuDef.get_child("modes"); + + //for (ptree::const_iterator itMenuModes = menuModesDef.begin(); itMenuModes != menuModesDef.end(); ++itMenuModes) + //{ + // std::cout << " +" << itMenuModes->second.data() << std::endl; + //} + + std::string parentMenuName = menuDef.get("parent", "None"); + + if (parentMenuName != "None") + { + } + + addMenu(menu); // add to our list of available menus + + } + } + + // parse the menu items? + + else if (itTopLevel->first == "menu_items") + { + UiLog().dbg() << "Menu items:"; + + ptree const &itemsDef = itTopLevel->second; + + // iterate through all the items + + for (ptree::const_iterator itItems = itemsDef.begin(); itItems != itemsDef.end(); ++itItems) + { + ptree const &ItemDef = itItems->second; + + std::string name = ItemDef.get("name", "NoName"); + std::string menuName = ItemDef.get("menu", "NoMenu"); + std::string command = ItemDef.get("command", "NoCommand"); + std::string type = ItemDef.get("type", "Command"); + std::string enabled = ItemDef.get("enabled_for", ""); + std::string visible = ItemDef.get("visible_for", ""); + std::string questFor = ItemDef.get("question_for",""); + std::string question = ItemDef.get("question", ""); + std::string questionControl = ItemDef.get("question_control", ""); + std::string warning = ItemDef.get("warning", ""); + std::string handler = ItemDef.get("handler", ""); + std::string views = ItemDef.get("view", ""); + std::string icon = ItemDef.get("icon", ""); + std::string hidden = ItemDef.get("hidden", "false"); + std::string multiSelect = ItemDef.get("multi", "true"); + std::string statustip = ItemDef.get("status_tip", ""); + + //std::cout << " " << name << " :" << menuName << std::endl; + + UiLog().dbg() << " " << name; + MenuItem *item = new MenuItem(name); + item->setCommand(command); + + + BaseNodeCondition *enabledCond = NodeExpressionParser::instance()->parseWholeExpression(enabled); + if (enabledCond == NULL) + { + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse enabled condition: " + enabled)); + enabledCond = new FalseNodeCondition(); + } + item->setEnabledCondition(enabledCond); + + + BaseNodeCondition *visibleCond = NodeExpressionParser::instance()->parseWholeExpression(visible); + if (visibleCond == NULL) + { + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse visible condition: " + visible)); + visibleCond = new FalseNodeCondition(); + } + item->setVisibleCondition(visibleCond); + + BaseNodeCondition *questionCond = NodeExpressionParser::instance()->parseWholeExpression(questFor); + if (questionCond == NULL) + { + UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse question condition: " + questFor)); + questionCond = new FalseNodeCondition(); + } + item->setQuestionCondition(questionCond); + + item->setQuestion(question); + item->setQuestionControl(questionControl); + item->setWarning(warning); + item->setHandler(handler); + item->setIcon(icon); + item->setStatustip(statustip); + + if(!views.empty()) + { + std::vector viewsVec; + QStringList vLst=QString::fromStdString(views).split("/"); + for(int i=0; i < vLst.count(); i++) + { + viewsVec.push_back(vLst[i].toStdString()); + } + + item->setViews(viewsVec); + } + + + item->setHidden((hidden == "true")?1:0); + item->setMultiSelect((multiSelect == "true")?1:0); + + if (type == "Submenu") + item->setAsSubMenu(); + + addItemToMenu(item, menuName); + //std::cout << " added" << std::endl; + + + // parse the valid node types/states for this menu item + + //if( ItemDef.count("valid_types") > 0 ) // does this node exist on the tree? + //{ + // ptree ptValidTypes = ItemDef.get_child("valid_types"); + // for (ptree::const_iterator itTypes = ptValidTypes.begin(); itTypes != ptValidTypes.end(); ++itTypes) + // { + // std::string type(itTypes->second.data()); + // //item->addValidType(type); + // } + //} + //else + //{ + // //item->addValidType("all"); + //} + + + if( ItemDef.count("valid_states") > 0 ) // does this node exist on the tree? + { + ptree ptValidStates = ItemDef.get_child("valid_states"); + for (ptree::const_iterator itStates = ptValidStates.begin(); itStates != ptValidStates.end(); ++itStates) + { + std::string state(itStates->second.data()); + //item->addValidState(state); + } + } + else + { + //item->addValidState("all"); + } + } + } + } + + + //ptree ptMenus = pt.get_child("menus"); + + + + + //for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) + //{ + // if (itTopLevel->first == "menus") + + //} + + + + + return true; +} + + +// --------------------------------------------------------- +// MenuHandler::addCustomMenuCommands +// Obtains the current list of custom commands and adds them +// to the list of custom menu items. +// --------------------------------------------------------- + +void MenuHandler::refreshCustomMenuCommands() +{ + CustomCommandHistoryHandler *customRecentCmds = CustomCommandHistoryHandler::instance(); + CustomSavedCommandHandler *customSavedCmds = CustomSavedCommandHandler::instance(); + + Menu *menu = findMenu("User defined"); + if(menu) + { + menu->clearFixedList(); + + // create the 'compulsary' menu items + MenuItem *item1 = new MenuItem("Manage commands..."); + item1->setCommand("custom"); + menu->addItemToFixedList(item1); + item1->setEnabledCondition(&trueCond_); + item1->setVisibleCondition(&trueCond_); + item1->setQuestionCondition(&falseCond_); + item1->setIcon("configure.svg"); + + // Saved commands + MenuItem *item2 = new MenuItem("-"); + menu->addItemToFixedList(item2); + item2->setEnabledCondition(&trueCond_); + item2->setVisibleCondition(&trueCond_); + item2->setQuestionCondition(&falseCond_); + + int numSavedCommands = customSavedCmds->numCommands(); + + for (int i = 0; i < numSavedCommands; i++) + { + CustomCommand *cmd = customSavedCmds->commandFromIndex(i); + if (cmd->inContextMenu()) + { + MenuItem *item = new MenuItem(cmd->name()); + item->setCommand(cmd->command()); + item->setEnabledCondition(&trueCond_); + item->setVisibleCondition(&trueCond_); + item->setQuestionCondition(&trueCond_); + item->setCustom(true); + item->setStatustip("__cmd__"); + menu->addItemToFixedList(item); + } + } + + + // Recently executed commands + MenuItem *item3 = new MenuItem("-"); + menu->addItemToFixedList(item3); + item3->setEnabledCondition(&trueCond_); + item3->setVisibleCondition(&trueCond_); + item3->setQuestionCondition(&falseCond_); + + MenuItem *item4 = new MenuItem("Recent"); + menu->addItemToFixedList(item4); + item4->setEnabledCondition(&falseCond_); + item4->setVisibleCondition(&trueCond_); + item4->setQuestionCondition(&falseCond_); + + int numRecentCommands = customRecentCmds->numCommands(); + + for (int i = 0; i < numRecentCommands; i++) + { + CustomCommand *cmd = customRecentCmds->commandFromIndex(i); + + MenuItem *item = new MenuItem(cmd->name()); + item->setCommand(cmd->command()); + item->setEnabledCondition(&trueCond_); + item->setVisibleCondition(&trueCond_); + item->setQuestionCondition(&trueCond_); + item->setCustom(true); + item->setStatustip("__cmd__"); + menu->addItemToFixedList(item); + } + } +} + + +Menu *MenuHandler::findMenu(const std::string &name) +{ + for (std::vector::iterator itMenus = menus_.begin(); itMenus != menus_.end(); ++itMenus) + { + if ((*itMenus)->name() == name) + { + return (*itMenus); + } + } + + return NULL; // if we got to here, then the menu was not found +} + +MenuItem* MenuHandler::findItem(QAction* ac) +{ + // ac could be NULL, e.g. if the user clicked on a separator instead of a menu item + if (ac) + { + for(std::vector::iterator itMenus = menus_.begin(); itMenus != menus_.end(); ++itMenus) + { + for(std::vector::iterator it=(*itMenus)->items().begin(); it!=(*itMenus)->items().end(); ++it) + { + if((*it)->id() == ac->data().toInt()) + { + return *it; + } + } + } + } + + return NULL; +} + +MenuItem* MenuHandler::newItem(const std::string &name) +{ + return NULL; +} + +bool MenuHandler::addItemToMenu(MenuItem *item, const std::string &menuName) +{ + Menu *menu = findMenu(menuName); + // items_.push_back(item); // add to our global list of menu items + + if (menu) + { + menu->addItemToFixedList(item); + return true; + } + else + { + UiLog().err() << "Could not find menu called " << + menuName << " to add item " << item->name() << " to."; + return false; + } + + return false; +} + + +MenuItem *MenuHandler::invokeMenu(const std::string &menuName, std::vector nodes, QPoint pos, QWidget *parent,const std::string& view) +{ + MenuItem *selectedItem = NULL; + Menu *menu = findMenu(menuName); + + if (menu) + { + QList acLst; + + //While create the menus we collect all the actions created with "parent" as the parent. + //QMenu does not take ownership of these actions so we need to delete them. + QMenu *qMenu = menu->generateMenu(nodes, parent, NULL, view,acLst); + + if (qMenu) + { + QAction* selectedAction = qMenu->exec(pos); + selectedItem=MenuHandler::findItem(selectedAction); + + delete qMenu; + + //Delete all the actions with "parent" as the parent; + Q_FOREACH(QAction *ac,acLst) + { + assert(parent == ac->parent()); + delete ac; + } + } + } + + return selectedItem; +} + +MenuHandler::ConfirmationMap &MenuHandler::getCommandsThatRequireConfirmation() +{ + // populate the list only the first time this function is called + if (commandsWhichRequireConfirmation_.empty()) + { + // list the commands which require a prompt: + commandsWhichRequireConfirmation_["delete"] = "Do you really want to delete ?"; + commandsWhichRequireConfirmation_["terminate"] = "Do you really want to terminate ?"; + commandsWhichRequireConfirmation_["halt"] = "Do you really want to halt ?"; + } + return commandsWhichRequireConfirmation_; +} + + + +// some commands, such as --delete, prompt the user for confirmation on the command line, which +// causes the application to hang. The way we get around this is to intercept these commands and, +// where possible, add a "yes" argument, which will bypass the prompt. +void MenuHandler::interceptCommandsThatNeedConfirmation(MenuItem *item) +{ + std::string command = item->command(); + QString wholeCmd = QString::fromStdString(command); + + // find the verb in the command + //QRegExp rx("ecflow_client\\s+--(\\S+).*"); // \s=whitespace, \S=non-whitespace + QRegExp rx("ecflow_client\\s+--([a-zA-Z]+).*"); // \s=whitespace, \S=non-whitespace + int i = rx.indexIn(wholeCmd); + if (i != -1) // a command was found + { + QString commandName = rx.cap(1); + std::string cmdName = commandName.toStdString(); + + // is this command one of the ones that requires a prompt? + MenuHandler::ConfirmationMap &list = getCommandsThatRequireConfirmation(); + MenuHandler::ConfirmationMap::iterator it=list.find(cmdName); + if(it != list.end()) + { + // does the command already have a 'yes'? + QRegExp rx2(".*\\byes\\b.*"); // \b=word boundary + int j = rx2.indexIn(wholeCmd); + if (j == -1) // no + { + item->setQuestion((*it).second); // note that we need to ask the user + + // fix the command so that it has "yes" in it + std::string minusCmd = std::string("--") + cmdName; + std::string cmdEquals = minusCmd + "="; + std::string cmdEqualsYes = cmdEquals + "yes "; + std::string cmdYes = minusCmd + " yes "; + if (!ecf::Str::replace(command, cmdEquals, cmdEqualsYes)) // --command=foo -> --command=yes foo + { + ecf::Str::replace(command, minusCmd, cmdYes); // --command foo -> --command yes foo + } + item->setCommand(command); + } + } + } +} + +// ----------------------------------------------------------------- + + +/////////////////////////////////////////////////////////// + +// -------------------- +// Menu class functions +// -------------------- + + + +Menu::Menu(const std::string &name) : name_(name) +{ +} + + +Menu::~Menu() +{ + for (std::vector::iterator itItems = itemsCombined_.begin(); itItems != itemsCombined_.end(); ++itItems) + { + if (*itItems) + delete (*itItems); + } +} + + +QMenu *Menu::generateMenu(std::vector nodes, QWidget *parent,QMenu* parentMenu,const std::string& view,QList& acLst) +{ + QMenu *qmenu=NULL; + if(parentMenu) + { + qmenu=parentMenu->addMenu(QString::fromStdString(name())); + } + else + { + qmenu=new QMenu(parent); + qmenu->setObjectName("cm"); + qmenu->setTitle(QString::fromStdString(name())); + } + + if (nodes.empty()) + return NULL; + + //qmenu->setWindowFlags(Qt::Tool); + //qmenu->setWindowTitle("my title"); + + //TypeNodeCondition typeCondFamily (MenuItem::FAMILY); + //TypeNodeCondition typeCondTask (MenuItem::TASK); + //StateNodeCondition stateCondUnknown ("unknown"); + //OrNodeCondition orCond (&typeCondFamily, &typeCondTask); + //AndNodeCondition andCond (&orCond, &stateCondUnknown); + + //std::string condString("not task"); + //BaseNodeCondition *nodeCond = NodeExpressionParser::parseWholeExpression(condString); + + //if (nodeCond == NULL) + //{ + // UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse condition: " + condString)); + //} + + // add an inactive action(!) to the top of the menu in order to show which + // node has been selected + + buildMenuTitle(nodes, qmenu); + + // if multiple attributes are selected, then tell the user we can't help them + // NOTE that ActionHandler.cpp ensures that we cannot have a mix of attr and non-attr nodes + if (nodes[0]->isAttribute() && nodes.size() > 1) + { + QAction *noAction = new QAction("No action for multiple attributes", parent); + noAction->setEnabled(false); + qmenu->addAction(noAction); + return qmenu; + } + + // merge the fixed menu items (from the config file) with the dynamic ones + itemsCombined_ = itemsFixed_; + itemsCombined_.insert(itemsCombined_.end(), itemsCustom_.begin(), itemsCustom_.end()); + + for (std::vector::iterator itItems = itemsCombined_.begin(); itItems != itemsCombined_.end(); ++itItems) + { + // is this item valid for the current selection? + + if((*itItems)->hidden()) + continue; + + if(!(*itItems)->isValidView(view)) + continue; + + bool visible = true; + + for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) + { + //compatible = compatible && (*itItems)->compatibleWithNode(*itNodes); + //compatible = compatible && (nodeCond != NULL && nodeCond->execute(*itNodes)); + visible = visible && (*itItems)->visibleCondition()->execute(*itNodes); + } + + if (visible) + { + bool enabled = true; + + for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) + { + enabled = enabled && (*itItems)->enabledCondition()->execute(*itNodes); + } + + //Check multiple selection + if(nodes.size() > 1 && !(*itItems)->multiSelect()) + enabled = false; + + if ((*itItems)->isSubMenu()) + { + Menu *menu = MenuHandler::findMenu((*itItems)->name()); + if (menu) + { + //The submenu will be added to qmenu and it will take ownership of it. + QMenu *subMenu = menu->generateMenu(nodes, parent, qmenu, view, acLst); + subMenu->setEnabled(enabled); + } + } + else if ((*itItems)->isDivider()) + { + qmenu->addSeparator(); + } + else + { + //When we add the action to the menu its parent (NULL e.i. the QApplication) does not change. + //So when the menu is deleted the action is not deleted. + //At least this is the behaviour with Qt 4.8. and 5.5. + //QAction *action = (*itItems)->action(); + //action->setParent(parent); + + //These actions will have "parent" as the parent, otherwise the statustip would not work + //on qmainwindows. The downside is that we need to delete these actions separately when the qmenu is deleted. + //In theory the parent of the actions could be the qmenu as well, but in this case the statustip does not work! + QAction* action=(*itItems)->createAction(parent); + qmenu->addAction(action); + action->setEnabled(enabled); + acLst << action; + } + } + } + + return qmenu; +} + +/* +void Menu::addSubHeading(std::string &name) +{ + QLabel *nodeLabel = new QLabel(name); + + QFont menuTitleFont; + menuTitleFont.setBold(true); + menuTitleFont.setItalic(true); + nodeLabel->setFont(menuTitleFont); + nodeLabel->setAlignment(Qt::AlignHCenter); + nodeLabel->setObjectName("nodeLabel"); + + QWidget* titleW=new QWidget(qmenu); + QVBoxLayout *titleLayout=new QVBoxLayout(titleW); + titleLayout->setContentsMargins(2,2,2,2); + titleLayout->addWidget(nodeLabel); + nodeLabel->setParent(titleW); + + QWidgetAction *wAction = new QWidgetAction(qmenu); + //Qt doc says: the ownership of the widget is passed to the widgetaction. + //So when the action is deleted it will be deleted as well. + wAction->setDefaultWidget(titleW); + //wAction->setEnabled(false); + qmenu->addAction(wAction); + +} +*/ +void Menu::buildMenuTitle(std::vector nodes, QMenu* qmenu) +{ + QLabel *nodeLabel = NULL; + + + // we will only create a multiple-entry context menu if we have multiple non-attribute nodes + // it is already ensured that if we have multiple nodes, they will be non-attribute nodes + bool multiple = (nodes.size() > 1); + + if (!multiple) + { + VNode *node=nodes.at(0)->node(); + + if(!node) + return; + + //single node selected put a label with the node name + colour + nodeLabel = new QLabel(node->name()); + + QBrush bgBrush(node->stateColour()); + + if(VProperty* p=VConfig::instance()->find("view.common.node_gradient")) + { + if(p->value().toBool()) + { + int lighter=150; + QColor bg=bgBrush.color(); + QColor bgLight=bg.lighter(lighter); + + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(0,1); + + grad.setColorAt(0,bgLight); + grad.setColorAt(1,bg); + bgBrush=QBrush(grad); + } + } + + QPalette labelPalette; + labelPalette.setBrush(QPalette::Window,bgBrush); + labelPalette.setColor(QPalette::WindowText,node->stateFontColour());//QColor(96,96,96)); + nodeLabel->setAutoFillBackground(true); + nodeLabel->setPalette(labelPalette); + + QString titleQss="QLabel {padding: 2px;}"; + nodeLabel->setStyleSheet(titleQss); + } + else + { + // multiple nodes selected - say how many + nodeLabel = new QLabel(QObject::tr("%1 nodes selected").arg(nodes.size())); + } + + QFont menuTitleFont; + menuTitleFont.setBold(true); + menuTitleFont.setItalic(true); + nodeLabel->setFont(menuTitleFont); + nodeLabel->setAlignment(Qt::AlignHCenter); + nodeLabel->setObjectName("nodeLabel"); + + QWidget* titleW=new QWidget(qmenu); + QVBoxLayout *titleLayout=new QVBoxLayout(titleW); + titleLayout->setContentsMargins(2,2,2,2); + titleLayout->addWidget(nodeLabel); + nodeLabel->setParent(titleW); + + QWidgetAction *wAction = new QWidgetAction(qmenu); + wAction->setObjectName("title"); + //Qt doc says: the ownership of the widget is passed to the widgetaction. + //So when the action is deleted it will be deleted as well. + wAction->setDefaultWidget(titleW); + wAction->setEnabled(false); + qmenu->addAction(wAction); +} + + +// ------------------------ +// MenuItem class functions +// ------------------------ + +MenuItem::MenuItem(const std::string &name) : + name_(name), + id_(idCnt_++), + hidden_(false), + multiSelect_(true), + visibleCondition_(NULL), + enabledCondition_(NULL), + questionCondition_(NULL), + isSubMenu_(false), + isDivider_(false), + isCustom_(false) +{ + if (name == "-") + { + isDivider_ = true; + } +} + +MenuItem::~MenuItem() +{ +} + +void MenuItem::setCommand(const std::string &command) +{ + command_ = command; + + //if (action_) + // action_->setStatusTip(QString(command.c_str())); // so we see the command in the status bar +} + +void MenuItem::setHandler(const std::string& handler) +{ + handler_ = handler; +} + +void MenuItem::setIcon(const std::string& icon) +{ + if(!icon.empty()) + { + icon_=QIcon(QPixmap(":/viewer/" + QString::fromStdString(icon))); + } +} + +bool MenuItem::shouldAskQuestion(std::vector &nodes) +{ + bool askQuestion = false; + + // ask the question if any of the nodes require it + for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) + { + askQuestion = askQuestion || questionCondition()->execute(*itNodes); + } + + return askQuestion; +} + +bool MenuItem::isValidView(const std::string& view) const +{ + if(views_.empty()) + return true; + + return (std::find(views_.begin(),views_.end(),view) != views_.end()); +} + +QAction* MenuItem::createAction(QWidget* parent) +{ + QAction *ac=new QAction(parent); + ac->setObjectName(QString::fromStdString(name_)); + ac->setText(QString::fromStdString(name_)); + ac->setIcon(icon_); + + if(!statustip_.empty()) + { + if(statustip_ == "__cmd__") + ac->setStatusTip(QString::fromStdString(command_)); // so we see the command in the status bar + else + ac->setStatusTip(QString::fromStdString(statustip_)); + } + ac->setData(id_); + return ac; + +} + + +// // adds an entry to the list of valid node types for this menu item(*itItems) +// void MenuItem::addValidType(std::string type) +// { +// static NodeType all[] = {TASK, FAMILY, SUITE, SERVER, ALIAS}; +// +// if (type == "server") +// validNodeTypes_.push_back(SERVER); +// else if(type == "suite") +// validNodeTypes_.push_back(SUITE); +// else if(type == "task") +// validNodeTypes_.push_back(TASK); +// else if(type == "family") +// validNodeTypes_.push_back(FAMILY); +// else if(type == "alias") +// validNodeTypes_.push_back(ALIAS); +// else if(type == "all") +// validNodeTypes_.insert(validNodeTypes_.begin(), all, all+5); +// } +// +// +// // adds an entry to the list of valid node types for this menu item +// void MenuItem::addValidState(std::string state) +// { +// DState::State dstate; +// +// if (DState::isValid(state)) +// { +// dstate = DState::toState(state); +// validNodeStates_.push_back(dstate); +// } +// else if (state == "all") +// { +// // add the list of all states +// std::vector allDstates = DState::states(); +// validNodeStates_.insert(validNodeStates_.end(), allDstates.begin(), allDstates.end()); +// } +// else +// { +// UserMessage::message(UserMessage::ERROR, false, std::string("Bad node state in menu file: " + state)); +// } +// } + + +// bool MenuItem::isNodeTypeValidForMenuItem(NodeType type) +// { +// if(std::find(validNodeTypes_.begin(), validNodeTypes_.end(), type) == validNodeTypes_.end()) +// return false; +// else +// return true; +// } +// +// +// bool MenuItem::compatibleWithNode(VInfo_ptr nodeInfo) +// { +// // check each node type and return false if we don't match +// +// if(nodeInfo->isServer()) +// if(std::find(validNodeTypes_.begin(), validNodeTypes_.end(), SERVER) == validNodeTypes_.end()) +// return false; +// +// if(nodeInfo->isNode()) +// { +// Node *node = nodeInfo->node()->node(); +// +// if(node->isSuite()) +// if (!isNodeTypeValidForMenuItem(SUITE)) +// return false; +// +// if(node->isTask()) +// if (!isNodeTypeValidForMenuItem(TASK)) +// return false; +// +// if(node->isAlias()) +// if (!isNodeTypeValidForMenuItem(ALIAS)) +// return false; +// +// if(node->isFamily()) +// if (!isNodeTypeValidForMenuItem(FAMILY)) +// return false; +// } +// +// return true; +// } diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MenuHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/MenuHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MenuHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MenuHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,186 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef MENUHANDLER_HPP_ +#define MENUHANDLER_HPP_ + +#include +#include +#include +#include +#include +#include + +#include "VInfo.hpp" +#include "NodeExpression.hpp" + +class QMenu; +class QAction; +class QWidget; +class Node; +class BaseNodeCondition; + + + +// ------------------------- +// MenuItem +// A single item in a menu +// ------------------------- + +class MenuItem +{ +public: + explicit MenuItem(const std::string &name); + ~MenuItem(); + + void setCommand(const std::string &command); + //bool compatibleWithNode(VInfo_ptr nodeInfo); + //void addValidType(std::string type); + //void addValidState(std::string type); + void setHandler(const std::string &handler); + void setViews(const std::vector &views) {views_=views;} + void setQuestion(const std::string &question) {question_=question;} + void setQuestionControl(const std::string &questionControl) {questionControl_=questionControl;} + void setWarning(const std::string &warning) {warning_=warning;} + void setIcon(const std::string &icon); + void setStatustip(const std::string &statustip) {statustip_=statustip;} + void setHidden(bool b) {hidden_=b;} + void setMultiSelect(bool b) {multiSelect_=b;} + void setAsSubMenu() {isSubMenu_ = true;} + void setVisibleCondition(BaseNodeCondition *cond) {visibleCondition_ = cond;} + void setEnabledCondition(BaseNodeCondition *cond) {enabledCondition_ = cond;} + void setQuestionCondition(BaseNodeCondition *cond) {questionCondition_ = cond;} + void setCustom(bool b) {isCustom_ = b;} + BaseNodeCondition *visibleCondition() const {return visibleCondition_;} + BaseNodeCondition *enabledCondition() const {return enabledCondition_;} + BaseNodeCondition *questionCondition() const {return questionCondition_;} + bool shouldAskQuestion(std::vector &nodes); + bool isSubMenu() const {return isSubMenu_;} + bool isDivider() const {return isDivider_;} + bool isCustom() const {return isCustom_;} + const std::string& name() const {return name_;} + const std::string& handler() const {return handler_;} + bool isValidView(const std::string&) const; + const std::string& command() const {return command_;} + const std::string& question() const {return question_;} + const std::string& questionControl() const {return questionControl_;} + const std::string& warning() const {return warning_;} + bool hidden() const {return hidden_;} + bool multiSelect() const {return multiSelect_;} + int id() const {return id_;} + QAction* createAction(QWidget* parent); + +private: + //No copy allowed + MenuItem(const MenuItem&); + MenuItem& operator=(const MenuItem&); + + //bool isNodeTypeValidForMenuItem(NodeType type); + + std::string name_; + int id_; + std::string tooltip_; + std::string command_; + std::string statustip_; + std::string question_; + std::string questionControl_; + std::string defaultAnswer_; + std::string warning_; + std::string handler_; + std::vector views_; + bool hidden_; + bool multiSelect_; //multiple selecttion + + //std::vector validNodeTypes_; + //std::vector validNodeStates_; + + + BaseNodeCondition *visibleCondition_; + BaseNodeCondition *enabledCondition_; + BaseNodeCondition *questionCondition_; + + bool isSubMenu_; + bool isDivider_; + bool isCustom_; + + QIcon icon_; + + static int idCnt_; +}; + + + +// ------------------------------------------------------------- +// Menu +// Contains all the possible items for a given menu. These will +// be filtered at run-time according to the state of +// the given item which has been clicked. +// ------------------------------------------------------------- + +class Menu +{ +public: + explicit Menu(const std::string &name); + ~Menu(); + QString exec(std::vector nodes); + std::string &name() {return name_;}; + void addItemToFixedList(MenuItem *item) {itemsFixed_.push_back(item);}; + void addItemToCustomList(MenuItem *item) {itemsCustom_.push_back(item);}; + void clearFixedList() {itemsFixed_.clear();} + QMenu *generateMenu(std::vector nodes, QWidget *parent,QMenu* parentMenu,const std::string& view,QList&); + std::vector& items() {return itemsCombined_;}; + +private: + void buildMenuTitle(std::vector nodes, QMenu* qmenu); + + std::string name_; + std::vector itemsFixed_; + std::vector itemsCustom_; + std::vector itemsCombined_; // items from config file plus custom commands + +}; + + +// -------------------------------------------------------------- +// MenuHandler +// Responsible for creating menus (read from configuration files) +// and generating 'actual' (i.e. context-dependent filtered) +// menus at run-time. +// -------------------------------------------------------------- + +class MenuHandler +{ +public: + MenuHandler(); + + //Menu *createMenu(QString &name); + static bool readMenuConfigFile(const std::string &configFile); + static MenuItem *invokeMenu(const std::string &menuName, std::vector nodes, QPoint pos, QWidget *parent,const std::string& view); + static bool addItemToMenu(MenuItem *item, const std::string &menuName); + static Menu *findMenu(const std::string &name); + static MenuItem* newItem(const std::string &name); + static void addMenu(Menu *menu) {menus_.push_back(menu);} + static void interceptCommandsThatNeedConfirmation(MenuItem *item); + static void refreshCustomMenuCommands(); + +private: + typedef std::map ConfirmationMap; + static MenuItem* findItem(QAction*); + static ConfirmationMap &getCommandsThatRequireConfirmation(); + + static std::vector menus_; + static ConfirmationMap commandsWhichRequireConfirmation_; + static TrueNodeCondition trueCond_; + static FalseNodeCondition falseCond_; + //static std::vector items_; + +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,129 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "MessageItemWidget.hpp" + +#include +#include +#include + +#include "InfoProvider.hpp" +#include "VReply.hpp" + +#include "LogModel.hpp" + +static bool firstRun=true; + +MessageItemWidget::MessageItemWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + + searchTb_->setEnabled(false); + searchTb_->setVisible(false); + searchLine_->setVisible(false); + + infoProvider_=new MessageProvider(this); + + model_=new LogModel(this); + + treeView_->setProperty("log","1"); + treeView_->setModel(model_); + treeView_->setItemDelegate(new LogDelegate(this)); + treeView_->setContextMenuPolicy(Qt::ActionsContextMenu); + + //make the horizontal scrollbar work + treeView_->header()->setStretchLastSection(false); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + treeView_->header()->setSectionResizeMode(QHeaderView::ResizeToContents); +#endif + + syncTb_->hide(); + + //Define context menu + treeView_->addAction(actionCopyEntry_); + treeView_->addAction(actionCopyRow_); +} + +QWidget* MessageItemWidget::realWidget() +{ + return this; +} + +void MessageItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + info_=info; + + if(info_) + { + infoProvider_->info(info_); + } +} + +void MessageItemWidget::clearContents() +{ + InfoPanelItem::clear(); + model_->clearData(); +} + +void MessageItemWidget::infoReady(VReply* reply) +{ + model_->setData(reply->textVec()); + + //Adjust column size if it is the first run + if(firstRun && model_->hasData()) + { + firstRun=false; + for(int i=0; i < model_->columnCount()-1; i++) + { + treeView_->resizeColumnToContents(i); + } + } +} + +void MessageItemWidget::infoProgress(VReply* reply) +{ + QString s=QString::fromStdString(reply->text()); +} + +void MessageItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); +} + +void MessageItemWidget::on_actionCopyEntry__triggered() +{ + toClipboard(model_->entryText(treeView_->currentIndex())); +} + +void MessageItemWidget::on_actionCopyRow__triggered() +{ + toClipboard(model_->fullText(treeView_->currentIndex())); +} + +void MessageItemWidget::toClipboard(QString txt) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif +} + +static InfoPanelItemMaker maker1("message"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef MESSAGEITEMWIDGET_HPP_ +#define MESSAGEITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" + +#include "ui_MessageItemWidget.h" + +class LogModel; + +class MessageItemWidget : public QWidget, public InfoPanelItem, protected Ui::MessageItemWidget +{ + Q_OBJECT +public: + explicit MessageItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected Q_SLOTS: + void on_actionCopyEntry__triggered(); + void on_actionCopyRow__triggered(); + +protected: + void updateState(const ChangeFlags&) {} + void toClipboard(QString txt) const; + + LogModel* model_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/MessageItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MessageItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,158 @@ + + + MessageItemWidget + + + + 0 + 0 + 510 + 465 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show search bar + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + Refresh messages + + + ... + + + + :/viewer/sync.svg:/viewer/sync.svg + + + true + + + + + + + + + + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + true + + + + + + + + + + Copy text of &Entry + + + Copy text of the log entry + + + Ctrl+C + + + + + Copy text of full &row + + + Copy text of full row + + + + + + MessageLabel + QWidget +
      MessageLabel.hpp
      + 1 +
      + + PlainTextSearchLine + QWidget +
      PlainTextSearchLine.hpp
      + 1 +
      + + TreeView + QTreeView +
      TreeView.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,135 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "MeterEditor.hpp" + +#include +#include + +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "SessionHandler.hpp" + +MeterEditorWidget::MeterEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); +} + +MeterEditor::MeterEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"meter",parent), oriVal_(0) +{ + w_=new MeterEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "meter"); + + if(a->data().count() < 5) + return; + + oriVal_=a->data().at(2).toInt(); + int min=a->data().at(3).toInt(); + int max=a->data().at(4).toInt(); + + if(min > max || oriVal_ < min || oriVal_ > max) + return; + + w_->nameLabel_->setText(a->data().at(1)); + w_->valueSpin_->setRange(min,max); + w_->valueSpin_->setValue(oriVal_); + + w_->minLabel_->setText(a->data().at(3)); + w_->maxLabel_->setText(a->data().at(4)); + w_->thresholdLabel_->setText(a->data().at(5)); + + w_->valueSpin_->setFocus(); + + header_->setInfo(QString::fromStdString(info_->path()),"Meter"); + + connect(w_->valueSpin_,SIGNAL(valueChanged(int)), + this,SLOT(slotValueChanged(int))); + + checkButtonStatus(); + + readSettings(); +} + +MeterEditor::~MeterEditor() +{ + writeSettings(); +} + +void MeterEditor::apply() +{ + std::string val=QString::number(w_->valueSpin_->value()).toStdString(); + std::string name=w_->nameLabel_->text().toStdString(); + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","meter",name,val); + CommandHandler::run(info_,cmd); +} + +void MeterEditor::resetValue() +{ + w_->valueSpin_->setValue(oriVal_); + checkButtonStatus(); +} + +void MeterEditor::slotValueChanged(int) +{ + checkButtonStatus(); +} + +bool MeterEditor::isValueChanged() +{ + return (oriVal_ != w_->valueSpin_->value()); +} + +void MeterEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("MeterEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void MeterEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("MeterEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(310,200)); + } + + settings.endGroup(); +} + +static AttributeEditorMaker makerStr("meter"); + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,52 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef METEREDITOR_HPP +#define METEREDITOR_HPP + +#include "ui_MeterEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +class MeterEditor; + +class MeterEditorWidget : public QWidget, protected Ui::MeterEditorWidget +{ +friend class MeterEditor; +public: + MeterEditorWidget(QWidget *parent=0); +}; + +class MeterEditor : public AttributeEditor +{ +Q_OBJECT + +public: + MeterEditor(VInfo_ptr,QWidget* parent=0); + ~MeterEditor(); + +protected Q_SLOTS: + void slotValueChanged(int); + +protected: + void apply(); + void resetValue(); + bool isValueChanged(); + void readSettings(); + void writeSettings(); + + MeterEditorWidget* w_; + int oriVal_; +}; + +#endif // METEREDITOR_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/MeterEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/MeterEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,99 @@ + + + MeterEditorWidget + + + + 0 + 0 + 329 + 227 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Name: + + + + + + + TextLabel + + + + + + + Value: + + + + + + + + + + Minimum: + + + + + + + TextLabel + + + + + + + Maximum: + + + + + + + TextLabel + + + + + + + Threshold: + + + + + + + TextLabel + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ModelColumn.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ModelColumn.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ModelColumn.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ModelColumn.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,295 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ModelColumn.hpp" + +#include + +#include + +#include "DiagData.hpp" +#include "VConfig.hpp" +#include "VConfigLoader.hpp" +#include "VProperty.hpp" +#include "VSettingsLoader.hpp" + +static std::map defs; + +ModelColumn::ModelColumn(const std::string& id) : id_(id), diagStart_(-1), diagEnd_(-1) +{ + defs[id_]=this; +} + +ModelColumn* ModelColumn::def(const std::string& id) +{ + std::map::const_iterator it=defs.find(id); + if(it != defs.end()) + return it->second; + return NULL; +} + +ModelColumn* ModelColumn::tableModelColumn() +{ + return ModelColumn::def("table_columns"); +} + +//int ModelColumn::indexOf(const std::string& id) const +//{ +// return indexOf(QString::fromStdString(id)); +//} + +int ModelColumn::indexOf(QString id) const +{ + for(int i=0; i< items_.count(); i++) + { + if(items_.at(i)->id_ == id) + return i; + } + return -1; +} + +void ModelColumn::loadItem(VProperty *p) +{ + ModelColumnItem* obj=new ModelColumnItem(p->strName()); + obj->label_=p->param("label"); + obj->tooltip_=p->param("tooltip"); + obj->icon_=p->param("icon"); + obj->index_=items_.count(); + items_ << obj; +} + +void ModelColumn::loadExtraItem(QString name,QString label) +{ + ModelColumnItem* obj=new ModelColumnItem(name.toStdString(),true); + obj->label_=label; + obj->tooltip_=obj->label_; + //obj->icon_=p->param("icon"); + obj->index_=items_.count(); + obj->editable_=true; + + if(hasDiag()) + { + items_.insert(diagStart_,obj); + diagStart_++; + diagEnd_++; + } + else + { + items_ << obj; + } +} + +void ModelColumn::loadDiagItem(QString name,QString label) +{ + ModelColumnItem* obj=new ModelColumnItem(name.toStdString(),true); + obj->label_=label; + obj->tooltip_=obj->label_; + //obj->icon_=p->param("icon"); + obj->index_=items_.count(); + obj->editable_=false; + items_ << obj; +} + +void ModelColumn::addExtraItem(QString name,QString label) +{ + if(indexOf(name) != -1) + return; + + //Editable extra items are always inserted in front of the diag items + int pos=items_.count(); + if(hasDiag()) + { + pos=diagStart_; + Q_ASSERT(pos >=0); + } + + Q_EMIT addItemsBegin(pos,pos); + //Q_EMIT appendItemBegin(); + loadExtraItem(name,label); + //Q_EMIT appendItemEnd(); + Q_EMIT addItemsEnd(pos,pos); + + save(); +} + +void ModelColumn::changeExtraItem(int idx, QString name,QString label) +{ + if(indexOf(name) != -1) + return; + + if(!isExtra(idx) || !isEditable(idx)) + return; + + Q_EMIT changeItemBegin(idx); + + items_[idx]->id_=name; + items_[idx]->label_=label; + + Q_EMIT changeItemEnd(idx); + + save(); +} + +void ModelColumn::removeExtraItem(QString name) +{ + int idx=indexOf(name); + if(idx != -1 && + items_[idx]->isExtra() && items_[idx]->isEditable()) + { + Q_EMIT removeItemsBegin(idx,idx); + + ModelColumnItem* obj=items_[idx]; + items_.removeAt(idx); + delete obj; + + if(hasDiag()) + { + diagStart_--; + diagEnd_--; + Q_ASSERT(diagStart_ >= 0); + } + + Q_EMIT removeItemsEnd(idx,idx); + + save(); + } +} + +bool ModelColumn::isSameDiag(DiagData *diag) const +{ + if(diagStart_ >=0 && diagEnd_ >=0 && diag->count() == diagEnd_-diagStart_+1) + { + for(int i=diagStart_; i <= diagEnd_; i++) + { + if(items_[i]->id_ != + QString::fromStdString(diag->columnName(i-diagStart_))) + { + return false; + } + } + return true; + } + return false; +} + +void ModelColumn::setDiagData(DiagData *diag) +{ + if(isSameDiag(diag)) + return; + + //Remove the current diag items + if(diagStart_ >=0 && diagEnd_ >=0) + { + Q_EMIT removeItemsBegin(diagStart_,diagEnd_); + for(int i=diagStart_; i <= diagEnd_; i++) + { + ModelColumnItem* obj=items_[diagStart_]; + items_.removeAt(diagStart_); + delete obj; + } + Q_EMIT removeItemsEnd(diagStart_,diagEnd_); + diagStart_=-1; + diagEnd_=-1; + } + + //Add the current diag items to the back of the items + if(diag->count() <=0) + return; + + diagStart_=items_.count(); + diagEnd_=items_.count()+diag->count()-1; + Q_ASSERT(diagStart_ >= 0); + Q_ASSERT(diagStart_ <= diagEnd_); + Q_EMIT addItemsBegin(diagStart_,diagEnd_); + + for(int i=0; i < diag->count(); i++) + { + QString n=QString::fromStdString(diag->columnName(i)); + loadDiagItem(n,n);// these are not editable items!!! + } + Q_EMIT addItemsEnd(diagStart_,diagEnd_); +} + +void ModelColumn::save() +{ + if(!configPath_.empty()) + { + if(VProperty* prop=VConfig::instance()->find(configPath_)) + { + QStringList lst; + for(int i=0; i < items_.count(); i++) + { + if(items_[i]->isExtra() && items_[i]->isEditable()) + lst << items_[i]->id_; + } + if(lst.isEmpty()) + prop->setValue(prop->defaultValue()); + else + prop->setValue(lst.join("/")); + } + } +} + +void ModelColumn::load(VProperty* group) +{ + Q_ASSERT(group); + + ModelColumn* m=new ModelColumn(group->strName()); + for(int i=0; i < group->children().size(); i++) + { + VProperty *p=group->children().at(i); + m->loadItem(p); + } + + //Define extra config property + m->configPath_=group->param("__config__").toStdString(); +} + +//Called via VSettingsLoader after the users settings are read +void ModelColumn::loadSettings() +{ + for(std::map::iterator it=defs.begin(); it != defs.end(); ++it) + { + it->second->loadUserSettings(); + } +} + +//Load user defined settings +void ModelColumn::loadUserSettings() +{ + //Load extra config + if(!configPath_.empty()) + { + if(VProperty* p=VConfig::instance()->find(configPath_)) + { + QString pval=p->valueAsString(); + if(!pval.isEmpty() && pval != "__none__") + { + Q_FOREACH(QString s,pval.split("/")) + { + loadExtraItem(s,s); + } + } + } + } +} + +ModelColumnItem::ModelColumnItem(const std::string& id, bool extra) : + index_(-1), extra_(extra), editable_(extra) +{ + id_=QString::fromStdString(id); +} + +static SimpleLoader loaderQuery("query_columns"); +static SimpleLoader loaderTable("table_columns"); +static SimpleLoader loaderZombie("zombie_columns"); +static SimpleLoader loaderTriggerGraph("trigger_graph_columns"); +static SimpleLoader loaderOutput("output_columns"); + +static SimpleSettingsLoader settingsLoader; diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ModelColumn.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ModelColumn.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ModelColumn.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ModelColumn.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,97 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef MODELCOLUMN_H +#define MODELCOLUMN_H + +class VProperty; +class DiagData; + +#include + +#include +#include +#include + +class ModelColumnItem +{ +friend class ModelColumn; +public: + explicit ModelColumnItem(const std::string& id,bool extra=false); + bool isExtra() const {return extra_;} + bool isEditable() const {return editable_;} + +protected: + QString label_; + QString id_; + int index_; + QString icon_; + QString tooltip_; + bool extra_; + bool editable_; +}; + +class ModelColumn : public QObject +{ + Q_OBJECT +public: + explicit ModelColumn(const std::string& id); + + int count() const {return items_.size();} + int indexOf(QString) const; + QString id(int i) const {assert(i>=0 && i < count()); return items_.at(i)->id_;} + QString label(int i) const {assert(i>=0 && i < count()); return items_.at(i)->label_;} + QString tooltip(int i) const {assert(i>=0 && i < count()); return items_.at(i)->tooltip_;} + bool isExtra(int i) const {assert(i>=0 && i < count()); return items_.at(i)->isExtra();} + bool isEditable(int i) const {assert(i>=0 && i < count()); return items_.at(i)->isEditable();} + bool hasDiag() const {return diagStart_ >=0 && diagEnd_ > diagStart_;} + int diagStartIndex() const {return diagStart_;} + int diagEndIndex() const {return diagEnd_;} + + void addExtraItem(QString,QString); + void changeExtraItem(int,QString,QString); + void removeExtraItem(QString); + + void setDiagData(DiagData*); + + static ModelColumn* def(const std::string& id); + static ModelColumn* tableModelColumn(); + + //Called from VConfigLoader + static void load(VProperty* group); + + //Called from VSettingsLoader + static void loadSettings(); + +Q_SIGNALS: + void appendItemBegin(); + void appendItemEnd(); + void addItemsBegin(int,int); + void addItemsEnd(int,int); + void changeItemBegin(int); + void changeItemEnd(int); + void removeItemsBegin(int,int); + void removeItemsEnd(int,int); + +protected: + void save(); + void loadItem(VProperty*); + void loadExtraItem(QString,QString); + void loadDiagItem(QString,QString); + void loadUserSettings(); + bool isSameDiag(DiagData *diag) const; + + std::string id_; + QList items_; + std::string configPath_; + int diagStart_; + int diagEnd_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeExpression.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeExpression.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeExpression.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeExpression.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1116 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include + +#include + +#include "Str.hpp" +#include "Node.hpp" +#include "Submittable.hpp" + +#include "NodeExpression.hpp" +#include "MenuHandler.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" +#include "VNodeMover.hpp" + +//#define _UI_NODEXPRESSIONPARSEER_DEBUG + +// ------------------------- +// Expression parser classes +// ------------------------- + +NodeExpressionParser* NodeExpressionParser::instance_=NULL; + + +NodeExpressionParser* NodeExpressionParser::instance() +{ + if(!instance_) + instance_=new NodeExpressionParser; + + return instance_; +} + +NodeExpressionParser::NodeExpressionParser() +{ + nameToNodeType_["server"]=SERVER; + nameToNodeType_["suite"]=SUITE; + nameToNodeType_["family"]=FAMILY; + nameToNodeType_["task"]=TASK; + nameToNodeType_["alias"]=ALIAS; + nameToNodeType_["node"]=NODE; + + for(std::map::const_iterator it=nameToNodeType_.begin(); it != nameToNodeType_.end(); ++it) + { + nodeTypeToName_[it->second]=it->first; + } + + QStringList attrNames; + attrNames << "meter" << "event" << "repeat" << "trigger" << "label" << "time" << "date" << "late" << "limit" << + "limit" << "limiter" << "var" << "genvar"; + Q_FOREACH(QString s,attrNames) + { + VAttributeType *t=VAttributeType::find(s.toStdString()); + Q_ASSERT(t); + nameToAttrType_[s.toStdString()]=t; + } + + badTypeStr_="BAD"; + badAttributeStr_="BAD"; +} + +NodeExpressionParser::NodeType NodeExpressionParser::nodeType(const std::string &name) const +{ + std::map::const_iterator it=nameToNodeType_.find(name); + if(it != nameToNodeType_.end()) + return it->second; + + return BAD; +} + +const std::string& NodeExpressionParser::typeName(const NodeType& type) const +{ + std::map::const_iterator it=nodeTypeToName_.find(type); + if(it != nodeTypeToName_.end()) + return it->second; + + return badTypeStr_; +} + +VAttributeType* NodeExpressionParser::toAttrType(const std::string &name) const +{ + std::map::const_iterator it=nameToAttrType_.find(name); + if(it != nameToAttrType_.end()) + return it->second; + + return NULL; +} + +bool NodeExpressionParser::isMenuMode(const std::string &str) const +{ + if (str == "oper" || str == "admin" || str == "defStatusMenuModeControl") + return true; + + return false; +} + +bool NodeExpressionParser::isEnvVar(const std::string &str) const +{ + if (str == "ECFLOWUI_ECMWF_OPERATOR_MODE" || str == "ECFLOWUI_DEVELOP_MODE") + return true; + + return false; +} + +bool NodeExpressionParser::isNodeHasAttribute(const std::string &str) const +{ + if (str == "has_triggers" || str == "has_time" || str == "has_date" || str == "locked") + return true; + + return false; +} + +bool NodeExpressionParser::isNodeFlag(const std::string &str) const +{ + if (str == "is_late" || str == "has_message" || + str == "is_rerun" || str == "is_waiting" || str == "is_zombie" || str == "is_migrated" || + str == "is_ecfcmd_failed" || str == "is_killed") + return true; + + return false; +} + + +bool NodeExpressionParser::isWhatToSearchIn(const std::string &str, bool &isAttr) const +{ + // list of non-attribute items that we can search in + if (str == "node_name" || str == "node_path") + { + isAttr = false; + return true; + } + + // list of attributes that we can search in + else if (str == "var_name" || str =="var_value" || str =="var_type" || + str == "label_name" || str == "label_value" || + str == "meter_name" || str == "meter_value" || + str == "event_name" || str == "event_value" || + str == "date_name" || str == "time_name" || + str == "limit_name" || str == "limit_value" || str == "limit_max" || + str == "limiter_name" || + str == "repeat_name" || str == "repeat_value" || + str == "trigger_expression" ) + { + isAttr = true; + return true; + } + + return false; +} + + +bool NodeExpressionParser::isAttributeState(const std::string &str) const +{ + return (str == "event_set" || str == "event_clear" || + str == "repeat_date" || str == "repeat_int" || str == "repeat_string" || str == "repeat_enum" || + str == "repeat_day"); +} + +bool NodeExpressionParser::isIsoDate(const std::string &str) const +{ + if(str.size() == 19) + { + QDateTime d=QDateTime::fromString(QString::fromStdString(str),Qt::ISODate); + return d.isValid(); + } + return false; +} + +// NodeExpressionParser::popLastNOperands +// - utility function to remove and return the last n operands from +// - the stack +std::vector NodeExpressionParser::popLastNOperands(std::vector &inOperands, int n) +{ + std::vector resultVec; + + for (int i=0; i tokens; + char delimiter = ' '; + char insideQuote = '\0'; // \0 if not inside a quote, \' if we are inside a quote + // will not handle the case of nested quotes! + + UiLog().dbg() << "parseWholeExpression: " << expr; + + ecf::Str::replace_all(expr, std::string("("), std::string(" ( ")); + ecf::Str::replace_all(expr, std::string(")"), std::string(" ) ")); + + //boost::algorithm::to_lower(expr); // convert to lowercase + + int index = 0; + int length = expr.length(); + std::string token = ""; + + // loop through each character in the string + + while (index < length) + { + char c = expr[index]; + + if (c == '\'') // a quote character? + { + if (insideQuote == '\'') // this is the closing quote + insideQuote = '\0'; // note that we are no longer inside a quote + else + insideQuote = '\''; // this is an opening quote + } + else if (c == delimiter && insideQuote == '\0') // a delimeter but not inside a quote? + { + if (token.length()>0) + tokens.push_back(token); + token =""; + } + else + token += c; + + index++; + } + + if(token.length()>0) + tokens.push_back(token); + + + setTokens(tokens); + + return parseExpression(caseSensitiveStringMatch); +} + + +BaseNodeCondition *NodeExpressionParser::parseExpression(bool caseSensitiveStringMatch) +{ + bool returnEarly = false; + BaseNodeCondition *result = NULL; + + std::vector funcStack; + std::vector operandStack; + + + // short-circuit - if empty, then return a True condition + + if(tokens_.size() == 0) + { + result=new TrueNodeCondition(); + } + + while (!returnEarly && i_ != tokens_.end()) + { + bool tokenOk = true; + bool updatedOperands = false; + + if (i_ != tokens_.end()) + { + + // are we expecting an arbitrary string? + if ((funcStack.size() > 0) && (funcStack.back()->operand2IsArbitraryString())) + { + WhatToSearchForOperand *whatToSearchFor = new WhatToSearchForOperand(*i_); + operandStack.push_back(whatToSearchFor); + result = whatToSearchFor; + updatedOperands = true; + } + else + { + bool attr = false; + VAttributeType* attrType=NULL; + //NodeExpressionParser::AttributeType attrType=NodeExpressionParser::BADATTRIBUTE; + + // node types + NodeExpressionParser::NodeType type = nodeType(*i_); + if (type != BAD) + { + TypeNodeCondition *typeCond = new TypeNodeCondition(type); + operandStack.push_back(typeCond); + result = typeCond; + updatedOperands = true; + } + + // node/server states + else if (DState::isValid(*i_) || VSState::find(*i_)) + { + StateNodeCondition *stateCond = new StateNodeCondition(QString::fromStdString(*i_)); + operandStack.push_back(stateCond); + result = stateCond; + updatedOperands = true; + } + + // node menu mode + else if (isMenuMode(*i_)) + { + NodeMenuModeCondition *userCond = new NodeMenuModeCondition(QString::fromStdString(*i_)); + operandStack.push_back(userCond); + result = userCond; + updatedOperands = true; + } + + // node has attribute + else if (isNodeHasAttribute(*i_)) + { + NodeAttributeCondition *attrCond = new NodeAttributeCondition(QString::fromStdString(*i_)); + operandStack.push_back(attrCond); + result = attrCond; + updatedOperands = true; + } + // node flag + else if (isNodeFlag(*i_)) + { + NodeFlagCondition *flagCond = new NodeFlagCondition(QString::fromStdString(*i_)); + operandStack.push_back(flagCond); + result = flagCond; + updatedOperands = true; + } + // node status change date + else if (*i_ == "status_change_time") + { + NodeStatusChangeDateCondition *chDateCond = new NodeStatusChangeDateCondition(); + operandStack.push_back(chDateCond); + result = chDateCond; + updatedOperands = true; + } + // node attribute type + //else if ((attrType = toAttrType(*i_)) != NodeExpressionParser::BADATTRIBUTE) + else if ((attrType = toAttrType(*i_)) != NULL) + { + AttributeCondition *attrCond = new AttributeCondition(attrType); + operandStack.push_back(attrCond); + result = attrCond; + updatedOperands = true; + } + + // node attribute state + else if (isAttributeState(*i_)) + { + AttributeStateCondition *attrStateCond = new AttributeStateCondition(QString::fromStdString(*i_)); + operandStack.push_back(attrStateCond); + result = attrStateCond; + updatedOperands = true; + } + + // env var + else if (isEnvVar(*i_)) + { + EnvVarCondition *envCond = new EnvVarCondition(QString::fromStdString(*i_)); + operandStack.push_back(envCond); + result = envCond; + updatedOperands = true; + } + + else if (isWhatToSearchIn(*i_, attr)) + { + WhatToSearchInOperand *searchCond = new WhatToSearchInOperand(*i_, attr); + operandStack.push_back(searchCond); + result = searchCond; + updatedOperands = true; + } + + // isoDate + else if (isIsoDate(*i_)) + { + IsoDateCondition *dateCond = new IsoDateCondition(QString::fromStdString(*i_)); + operandStack.push_back(dateCond); + result = dateCond; + updatedOperands = true; + } + + //iso date operator + else if (*i_ == "date::<=") + { + IsoDateLessThanEqualCondition *dateCond = new IsoDateLessThanEqualCondition(); + funcStack.push_back(dateCond); + result = dateCond; + } + + //iso date operator + else if (*i_ == "date::>=") + { + IsoDateGreaterThanEqualCondition *dateCond = new IsoDateGreaterThanEqualCondition(); + funcStack.push_back(dateCond); + result = dateCond; + } + + else if (*i_ == "marked") + { + UIStateCondition *uiCond = new UIStateCondition(*i_); + operandStack.push_back(uiCond); + result = uiCond; + updatedOperands = true; + } + + // logical operators + else if (*i_ == "and") + { + AndNodeCondition *andCond = new AndNodeCondition(); + funcStack.push_back(andCond); + result = andCond; + } + + else if (*i_ == "or") + { + OrNodeCondition *orCond = new OrNodeCondition(); + funcStack.push_back(orCond); + result = orCond; + } + + else if (*i_ == "not") + { + NotNodeCondition *notCond = new NotNodeCondition(); + funcStack.push_back(notCond); + result = notCond; + } + + else if(StringMatchMode::operToMode(*i_) != StringMatchMode::InvalidMatch) + { + StringMatchCondition *stringMatchCond = new StringMatchCondition(StringMatchMode::operToMode(*i_), caseSensitiveStringMatch); + funcStack.push_back(stringMatchCond); + result = stringMatchCond; + } + + else if (*i_ == "(") + { + ++i_; + result = parseExpression(caseSensitiveStringMatch); + operandStack.push_back(result); + } + else if (*i_ == ")") + { + returnEarly = true; + } + + else + { + tokenOk = false; + } + } + } + + else + { + // got to the end of the tokens, but we may still need to + // update the condition stacks + updatedOperands = true; + } + + + if (tokenOk) + { + // if there are enough operands on the stack for the last + // function, pop them off and create a small tree for that function + // but do not do this if the last function asks to delay this process + if (!funcStack.empty() && !funcStack.back()->delayUnwinding()) + { + if(updatedOperands && (static_cast(operandStack.size()) >= funcStack.back()->numOperands())) + { + std::vector operands; + result = funcStack.back(); // last function is the current result + operands = popLastNOperands(operandStack, result->numOperands()); // pop its operands off the stack + result->setOperands(operands); + funcStack.pop_back(); // remove the last function from the stack + operandStack.push_back(result); // store the current result + } + } + } + else + { + UiLog().err() << "Error parsing expression " << *i_; + result = new FalseNodeCondition(); + return result; + } + + if (i_ != tokens_.end() && !returnEarly) + ++i_; // move onto the next token + } + + + int iterCnt=0; //to avoid infinite loop we use this counter + + // final unwinding of the stack + while (!funcStack.empty()) + { + if(static_cast(operandStack.size()) >= funcStack.back()->numOperands()) + { + std::vector operands; + result = funcStack.back(); // last function is the current result + operands = popLastNOperands(operandStack, result->numOperands()); // pop its operands off the stack + result->setOperands(operands); + funcStack.pop_back(); // remove the last function from the stack + operandStack.push_back(result); // store the current result + iterCnt=0; + } + else + { + iterCnt++; + if(iterCnt > 10) + { + if(result) + { + delete result; + result=NULL; + } + break; + } + } + } + + if(result) + UiLog().dbg() << " " << result->print(); + + return result; +} + + +bool BaseNodeCondition::execute(VInfo_ptr nodeInfo) +{ + if(!nodeInfo) + return true; + + if(nodeInfo->isServer()) + return execute(nodeInfo->server()->vRoot()); + else if(nodeInfo->isNode()) + return execute(nodeInfo->node()); + else if(nodeInfo->isAttribute()) + return execute(nodeInfo->attribute()); + + return false; +} + +// ----------------------------------------------------------------- + +bool BaseNodeCondition::containsAttributeSearch() +{ + bool contains = false; + + // check child condition nodes + for(std::size_t i = 0; i < operands_.size(); i++) + { + contains = contains | operands_[i]->containsAttributeSearch(); + } + + // check this condition node + contains = contains | searchInAttributes(); + + return contains; +} + +//========================================================================= +// +// AndNodeCondition +// +//========================================================================= + +bool AndNodeCondition::execute(VItem* node) +{ + return operands_[0]->execute(node) && operands_[1]->execute(node); +} + +//========================================================================= +// +// OrNodeCondition +// +//========================================================================= + +bool OrNodeCondition::execute(VItem* node) +{ +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << "OrNodeCondition::execute --->"; + UiLog().dbg() << operands_[0]->execute(node) << " " << operands_[1]->execute(node); +#endif + return operands_[0]->execute(node) || operands_[1]->execute(node); +} + + +//========================================================================= +// +// NotNodeCondition +// +//========================================================================= + +bool NotNodeCondition::execute(VItem* node) +{ + return !(operands_[0]->execute(node)); +} + +//========================================================================= +// +// TypeNodeCondition +// +//========================================================================= + +bool TypeNodeCondition::execute(VItem* item) +{ + if (type_ == NodeExpressionParser::SERVER) + { + return (item->isServer() != NULL); + } + + else if(item->isNode()) + { +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << "TypeNodeCondition::execute --> " << NodeExpressionParser::instance()->typeName(type_); + UiLog().dbg() << item->isNode() << " " << item->isSuite() << " " << item->isFamily() << + " " << item->isTask() << " " << item->isAlias(); +#endif + switch(type_) + { + case NodeExpressionParser::NODE: +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << " NODE"; +#endif + return true; + break; + case NodeExpressionParser::SUITE: +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << " SUITE"; +#endif + return (item->isSuite() != NULL); + break; + case NodeExpressionParser::TASK: +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << " TASK"; +#endif + return (item->isTask() != NULL); + break; + case NodeExpressionParser::FAMILY: +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << " FAMILY"; +#endif + return (item->isFamily() != NULL); + break; + case NodeExpressionParser::ALIAS: +#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG + UiLog().dbg() << " ALIAS"; +#endif + return (item->isAlias() != NULL); + break; + default: + break; + } + } + + return false; +} + +//========================================================================= +// +// StateNodeCondition +// +//========================================================================= + +bool StateNodeCondition::execute(VItem* item) +{ + if(item->isServer()) + { + VServer* s=static_cast(item); + assert(s); + return (s->serverStateName() == stateName_); + } + else + { + VNode* n=static_cast(item); + assert(n); + return (n->stateName() == stateName_); + } + return false; +} + +//========================================================================= +// +// NodeMenuModeCondition +// +//========================================================================= + +bool NodeMenuModeCondition::execute(VItem* item) +{ + if(item) + { + if(menuModeName_ == "defStatusMenuModeControl") + { + Q_FOREACH(QString s,item->defStatusNodeMenuMode().split("/")) + { + if(item->nodeMenuMode() == s) + return true; + } + + } + else + { + return (item->nodeMenuMode() == menuModeName_); + } + } + return false; +} + +//========================================================================= +// +// NodeMenuModeCondition +// +//========================================================================= + +bool EnvVarCondition::execute(VItem* item) +{ + if(item) + { + if(defined_ == -1) + { + defined_=0; + if(const char* ch=getenv(envVarName_.toStdString().c_str())) + { + defined_=(strcmp(ch,"1") == 0)?1:0; + } + } + + return defined_ == 1; + } + return false; +} + +//========================================================================= +// +// UIStateCondition +// +//========================================================================= + +bool UIStateCondition::execute(VItem*) +{ + if (uiStateName_ == "marked") + { + return VNodeMover::hasMarkedForMove(); + } + + return false; +} + + +//========================================================================= +// +// String match utility functions +// +//========================================================================= + +bool StringMatchExact::match(std::string searchFor, std::string searchIn) +{ + return searchFor == searchIn; +} + +bool StringMatchContains::match(std::string searchFor, std::string searchIn) +{ + Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp regexp(QString::fromStdString(searchFor), cs); + int index = regexp.indexIn(QString::fromStdString(searchIn)); + return (index != -1); // -1 means no match +} + +bool StringMatchWildcard::match(std::string searchFor, std::string searchIn) +{ + Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp regexp(QString::fromStdString(searchFor), cs); + regexp.setPatternSyntax(QRegExp::Wildcard); + return regexp.exactMatch(QString::fromStdString(searchIn)); +} + +bool StringMatchRegexp::match(std::string searchFor, std::string searchIn) +{ + Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; + QRegExp regexp(QString::fromStdString(searchFor), cs); + return regexp.exactMatch(QString::fromStdString(searchIn)); +} + +//========================================================================= +// +// String match condition +// +//========================================================================= + +StringMatchCondition::StringMatchCondition(StringMatchMode::Mode matchMode, bool caseSensitive) +{ + switch (matchMode) + { + case StringMatchMode::ContainsMatch: + matcher_ = new StringMatchContains(caseSensitive); + break; + case StringMatchMode::WildcardMatch: + matcher_ = new StringMatchWildcard(caseSensitive); + break; + case StringMatchMode::RegexpMatch: + matcher_ = new StringMatchRegexp(caseSensitive); + break; + default: + UiLog().dbg() << "StringMatchCondition: bad matchMode"; + matcher_ = new StringMatchExact(caseSensitive); + break; + } +} + +bool StringMatchCondition::execute(VItem *item) +{ + WhatToSearchForOperand *searchForOperand = static_cast (operands_[0]); + WhatToSearchInOperand *searchInOperand = static_cast (operands_[1]); + + std::string searchIn = searchInOperand->what(); + + if(item->isNode()) + { + VNode* n=static_cast(item); + //TODO XXXX check - name, label, variable, etc + if(searchIn == "node_name") + { + return matcher_->match(searchForOperand->what(), n->strName()); + } + + else if (searchIn == "node_path") + { + return matcher_->match(searchForOperand->what(), n->absNodePath()); + } + } + else if(VAttribute* a=item->isAttribute()) + { + std::string str; + if(a->value(searchIn,str)) + return matcher_->match(searchForOperand->what(),str); + else + return false; + } + + return false; +} + +// ----------------------------------------------------------------- + +bool NodeAttributeCondition::execute(VItem* item) +{ + if (item->isServer()) + { + if(nodeAttrName_ == "locked") + { + return false; // XXX temporary for now + } + } + + else if(item->isNode()) + { + VNode* n=static_cast(item); + node_ptr node = n->node(); + + if (nodeAttrName_ == "has_time") + { + return (node->timeVec().size() > 0 || + node->todayVec().size() > 0 || + node->crons().size() > 0); + } + else if (nodeAttrName_ == "has_date") + { + return (node->days().size() > 0 || + node->dates().size() > 0); + } + else if (nodeAttrName_ == "has_triggers") + { + return (node->triggerAst() || + node->completeAst()); + } + } + + return false; +} +// ----------------------------------------------------------------- + +bool NodeFlagCondition::execute(VItem* item) +{ + if(item->isServer()) + { + return false; + } + else if(item->isNode()) + { + VNode* vnode=static_cast(item); + + if(nodeFlagName_ == "is_zombie") + return vnode->isFlagSet(ecf::Flag::ZOMBIE); + + if(nodeFlagName_ == "has_message") + return vnode->isFlagSet(ecf::Flag::MESSAGE); + + else if(nodeFlagName_ == "is_late") + return vnode->isFlagSet(ecf::Flag::LATE); + + else if(nodeFlagName_ == "is_rerun") + { + node_ptr node=vnode->node(); + if(!node.get()) return false; + + if(Submittable* s = node->isSubmittable()) + { + return (s->try_no() > 1); + } + return false; + } + else if(nodeFlagName_ == "is_waiting") + return vnode->isFlagSet(ecf::Flag::WAIT); + + else if(nodeFlagName_ == "is_migrated") + return vnode->isFlagSet(ecf::Flag::MIGRATED); + + else if(nodeFlagName_ == "is_ecfcmd_failed") + return vnode->isFlagSet(ecf::Flag::JOBCMD_FAILED); + + else if(nodeFlagName_ == "is_killed") + return vnode->isFlagSet(ecf::Flag::KILLED); + + + } + + return false; +} + +WhatToSearchInOperand::WhatToSearchInOperand(std::string what, bool &attr) +{ + what_ = what; + searchInAttributes_ = attr; +} + + +WhatToSearchInOperand::~WhatToSearchInOperand() {} +WhatToSearchForOperand::~WhatToSearchForOperand() {} + +//==================================================== +// +// Attribute condition +// +//==================================================== + +bool AttributeCondition::execute(VItem* item) +{ + if(!item) + return false; + + VAttribute* a=item->isAttribute(); + if(!a) + return false; + + Q_ASSERT(a->type()); + + return a->type() == type_; +} + +//==================================================== +// +// Attribute state condition +// +//==================================================== + +bool AttributeStateCondition::execute(VItem* item) +{ + if(!item) + return false; + + VAttribute* a=item->isAttribute(); + if(!a) + return false; + + assert(a->type()); + + if(attrState_.startsWith("event_")) + { + if(a->type()->name() == "event" && a->data().count() >= 3) + { + QString v=a->data()[2]; + if(attrState_ == "event_set") + return v == "1"; + else if(attrState_ == "event_clear") + return v == "0"; + } + } + else if(attrState_.startsWith("repeat_")) + { + if(a->type()->name() == "repeat" && a->data().count() >= 2) + { + QString v=a->data()[1]; + if(attrState_ == "repeat_date") + return v == "date"; + else if(attrState_ == "repeat_int") + return v == "integer"; + else if(attrState_ == "repeat_string") + return v == "string"; + else if(attrState_ == "repeat_enum") + return v == "enumeration"; + else if(attrState_ == "repeat_day") + return v == "day"; + } + } + return false; +} + +//========================================== +// +// ISO date condition +// +//========================================== + +IsoDateCondition::IsoDateCondition(QString dateStr) : secsSinceEpoch_(-1) +{ + QDateTime d=QDateTime::fromString(dateStr,Qt::ISODate); + if(d.isValid()) + secsSinceEpoch_=d.toMSecsSinceEpoch()/1000; +} + +std::string IsoDateCondition::print() +{ + if(secsSinceEpoch_ > 0) + return QDateTime::fromMSecsSinceEpoch(secsSinceEpoch_*1000).toString(Qt::ISODate).toStdString(); + return std::string(); +} + +//========================================== +// +// Node status change date condition +// +//========================================== + +qint64 NodeStatusChangeDateCondition::secsSinceEpoch(VItem* item) const +{ + Q_ASSERT(item); + if(item->isNode()) + { + VNode* vnode=static_cast(item); + Q_ASSERT(vnode); + return vnode->statusChangeTime(); + } + + return -1; +} + +std::string NodeStatusChangeDateCondition::print() +{ + return "status_change_time"; +} + +//========================================== +// +// ISO date greater than equal condition +// +//========================================== + +bool IsoDateGreaterThanEqualCondition::execute(VItem *node) +{ + UI_ASSERT(operands_.size() == 2,"operands size=" < (operands_[1]); + IsoDateCondition* rightOperand=static_cast (operands_[0]); + Q_ASSERT(leftOperand); + Q_ASSERT(rightOperand); + + return leftOperand->secsSinceEpoch(node) >= rightOperand->secsSinceEpoch(node); +} + +std::string IsoDateGreaterThanEqualCondition::print() +{ + UI_ASSERT(operands_.size() == 2,"operands size=" <print() + " >= " + operands_[0]->print(); +} + +//========================================== +// +// ISO date less than equal condition +// +//========================================== + +bool IsoDateLessThanEqualCondition::execute(VItem *node) +{ + UI_ASSERT(operands_.size() == 2,"operands size=" < (operands_[1]); + IsoDateCondition* rightOperand=static_cast (operands_[0]); + Q_ASSERT(leftOperand); + Q_ASSERT(rightOperand); + + return leftOperand->secsSinceEpoch(node) <= rightOperand->secsSinceEpoch(node); +} + +std::string IsoDateLessThanEqualCondition::print() +{ + UI_ASSERT(operands_.size() == 2,"operands size=" <print() + " <= " + operands_[0]->print(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeExpression.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeExpression.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeExpression.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeExpression.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,534 @@ +#ifndef NODEEXPRESSION_HPP_ +#define NODEEXPRESSION_HPP_ + +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "DState.hpp" + +#include "VInfo.hpp" +#include "VItem.hpp" +#include "VNState.hpp" +#include "VSState.hpp" +#include "StringMatchMode.hpp" +#include "VAttribute.hpp" + +class VItem; +class VAttributeType; + +// ---------------------- +// Node condition classes +// ---------------------- + +class BaseNodeCondition; // forward declaration + +class NodeExpressionParser +{ +public: + enum NodeType {SERVER, SUITE, FAMILY, TASK, ALIAS, NODE, BAD}; + + enum AttributeType {ATTRIBUTE, METER, EVENT, REPEAT, TRIGGER, LABEL, TIME, DATE, + LATE, LIMIT, LIMITER, VAR, GENVAR, BADATTRIBUTE}; + + static NodeExpressionParser* instance(); + + BaseNodeCondition *parseWholeExpression(const std::string&, bool caseSensitiveStringMatch=true); + + NodeType nodeType(const std::string &name) const; + const std::string& typeName(const NodeType&) const; + VAttributeType* toAttrType(const std::string &name) const; + //AttributeType toAttrType(const std::string &name) const; + //const std::string& toAttrName(const AttributeType&) const; + +protected: + NodeExpressionParser(); + + bool isMenuMode(const std::string &str) const; + bool isEnvVar(const std::string &str) const; + bool isNodeHasAttribute(const std::string &str) const; + bool isNodeFlag(const std::string &str) const; + bool isWhatToSearchIn(const std::string &str, bool &isAttribute) const; +#if 0 + bool isAttribute(const std::string &str) const; +#endif + bool isAttributeState(const std::string &str) const; + bool isIsoDate(const std::string &str) const; + + BaseNodeCondition *parseExpression(bool caseSensitiveStringMatch); + void setTokens(std::vector &tokens) {tokens_ = tokens; i_ = tokens_.begin();} + std::vector popLastNOperands(std::vector &inOperands, int n); + + static NodeExpressionParser* instance_; + std::vector tokens_; + std::vector::const_iterator i_; + std::map nameToNodeType_; + std::map nodeTypeToName_; + + std::map nameToAttrType_; + + //std::map nameToAttrType_; + //std::map attrTypeToName_; + std::string badTypeStr_; + std::string badAttributeStr_; +}; + +// ----------------------------------------------------------------- +// BaseNodeCondition +// The parent class for all node conditions. +// delayUnwinding: choose whether to unwind the function stack +// immediately after parsing this condition, or delay until we've +// reached the end of the current sub-expression. Set to true for +// loosely-coupled operators such as 'and', and set to false for +// others which need to consume their arguments immediately. +// ----------------------------------------------------------------- + +class BaseNodeCondition +{ +public: + BaseNodeCondition() {delayUnwinding_ = false;} + virtual ~BaseNodeCondition() {} + + bool execute(VInfo_ptr nodeInfo); + virtual bool execute(VItem* item)=0; + + virtual int numOperands() {return 0;} + virtual std::string print() = 0; + virtual bool operand2IsArbitraryString() {return false;} + + void setOperands(std::vector ops) {operands_ = ops;} + bool containsAttributeSearch(); + bool delayUnwinding() const {return delayUnwinding_;} + +protected: + virtual bool searchInAttributes() {return false;} + + std::vector operands_; + bool delayUnwinding_; +}; + +// ----------------------------------------------------------------- + +class AndNodeCondition : public BaseNodeCondition +{ +public: + AndNodeCondition() {delayUnwinding_ = true;} + ~AndNodeCondition() {} + + bool execute(VItem* node); + int numOperands() {return 2;} + std::string print() {return std::string("and") + "(" + operands_[0]->print() + "," + operands_[1]->print() + ")";} +}; + +// ----------------------------------------------------------------- + +class OrNodeCondition : public BaseNodeCondition +{ +public: + OrNodeCondition() {delayUnwinding_ = true;} + ~OrNodeCondition() {} + + bool execute(VItem* node); + int numOperands() {return 2;} + std::string print() {return std::string("or") + "(" + operands_[0]->print() + "," + operands_[1]->print() + ")";} +}; + +// ----------------------------------------------------------------- + +class NotNodeCondition : public BaseNodeCondition +{ +public: + NotNodeCondition() {} + ~NotNodeCondition() {} + + bool execute(VItem* node); + int numOperands() {return 1;} + std::string print() {return std::string("not") + "(" + operands_[0]->print() + ")";} +}; + + + +// -------------------------------- +// String matching utitlity classes +// -------------------------------- + +// note that it would be ideal for the match() function to take references to strings +// for efficiency, but this is not always possible. + +class StringMatchBase +{ +public: + StringMatchBase(bool caseSensitive) {caseSensitive_ = caseSensitive;} + virtual ~StringMatchBase() {} + + virtual bool match(std::string searchFor, std::string searchIn) = 0; + +protected: + bool caseSensitive_; +}; + +class StringMatchExact : public StringMatchBase +{ +public: + StringMatchExact(bool caseSensitive) : StringMatchBase(caseSensitive) {} + ~StringMatchExact() {} + + bool match(std::string searchFor, std::string searchIn); +}; + +class StringMatchContains : public StringMatchBase +{ +public: + StringMatchContains(bool caseSensitive) : StringMatchBase(caseSensitive) {} + ~StringMatchContains() {} + + bool match(std::string searchFor, std::string searchIn); +}; + +class StringMatchWildcard : public StringMatchBase +{ +public: + StringMatchWildcard(bool caseSensitive) : StringMatchBase(caseSensitive) {} + ~StringMatchWildcard() {} + + bool match(std::string searchFor, std::string searchIn); +}; + +class StringMatchRegexp : public StringMatchBase +{ +public: + StringMatchRegexp(bool caseSensitive) : StringMatchBase(caseSensitive) {} + ~StringMatchRegexp() {} + + bool match(std::string searchFor, std::string searchIn); +}; + +// ----------------------------------------------------------------- + +// ------------------------- +// String matching condition +// ------------------------- + +class StringMatchCondition : public BaseNodeCondition +{ +public: + StringMatchCondition(StringMatchMode::Mode matchMode, bool caseSensitive); + ~StringMatchCondition() {if (matcher_) delete matcher_;} + + bool execute(VItem *node); + int numOperands() {return 2;} + std::string print() {return operands_[0]->print() + " = " + operands_[1]->print();} + bool operand2IsArbitraryString() {return true;} +private: + StringMatchBase *matcher_; +}; + +// ----------------------------------------------------------------- + +// --------------------------- +// Basic true/false conditions +// --------------------------- + +class TrueNodeCondition : public BaseNodeCondition +{ +public: + TrueNodeCondition() {} + ~TrueNodeCondition() {} + + bool execute(VItem*) {return true;} + std::string print() {return std::string("true");} +}; + +class FalseNodeCondition : public BaseNodeCondition +{ +public: + FalseNodeCondition() {} + ~FalseNodeCondition() {} + + bool execute(VItem*) {return false;} + std::string print() {return std::string("false");} +}; + +// ----------------------------------------------------------------- + +// ------------------- +// Node type condition +// ------------------- + +class TypeNodeCondition : public BaseNodeCondition +{ +public: + explicit TypeNodeCondition(NodeExpressionParser::NodeType type) {type_ = type;} + ~TypeNodeCondition() {} + + bool execute(VItem* node); + std::string print() {return NodeExpressionParser::instance()->typeName(type_);} + +private: + NodeExpressionParser::NodeType type_; +}; + +// ----------------------------------------------------------------- + +// -------------------- +// Node state condition +// -------------------- + +class StateNodeCondition : public BaseNodeCondition +{ +public: + explicit StateNodeCondition(QString stateName) : stateName_(stateName) {} + ~StateNodeCondition() {} + + bool execute(VItem* node); + std::string print() {return stateName_.toStdString();} + +private: + QString stateName_; +}; + +// ----------------------------------------------------------------- + +// -------------------- +// User level condition +// -------------------- + +class NodeMenuModeCondition : public BaseNodeCondition +{ +public: + explicit NodeMenuModeCondition(QString menuModeName) : menuModeName_(menuModeName) {} + ~NodeMenuModeCondition() {} + + bool execute(VItem*); + std::string print() {return menuModeName_.toStdString();} + +private: + QString menuModeName_; +}; + +// ----------------------------------------------------------------- + +// -------------------- +// Envvar condition +// -------------------- + +class EnvVarCondition : public BaseNodeCondition +{ +public: + explicit EnvVarCondition(QString envVarName) : envVarName_(envVarName), defined_(-1) {} + ~EnvVarCondition() {} + + bool execute(VItem*); + std::string print() {return envVarName_.toStdString();} + +private: + QString envVarName_; + int defined_; +}; + +// ----------------------------------------------------------------- + + +// -------------------- +// UI state condition +// -------------------- + +class UIStateCondition : public BaseNodeCondition +{ +public: + explicit UIStateCondition(const std::string& uiStateName) : uiStateName_(uiStateName) {} + ~UIStateCondition() {} + + bool execute(VItem*); + std::string print() {return uiStateName_;} + +private: + std::string uiStateName_; +}; + + +// ----------------------------------------------------------------- + +// ------------------------ +// Node attribute condition +// ------------------------ + +class NodeAttributeCondition : public BaseNodeCondition +{ +public: + explicit NodeAttributeCondition(QString nodeAttrName) : nodeAttrName_(nodeAttrName) {} + ~NodeAttributeCondition() {} + + bool execute(VItem*); + std::string print() {return nodeAttrName_.toStdString();} + +private: + QString nodeAttrName_; +}; + +// ----------------------------------------------------------------- + +// ------------------------ +// Node flag condition +// ------------------------ + +class NodeFlagCondition : public BaseNodeCondition +{ +public: + explicit NodeFlagCondition(QString nodeFlagName) : nodeFlagName_(nodeFlagName) {} + ~NodeFlagCondition() {} + + bool execute(VItem*); + std::string print() {return nodeFlagName_.toStdString();} + +private: + QString nodeFlagName_; +}; +// ----------------------------------------------------------------- + +// ---------------------------------- +// ISO date condition +// ---------------------------------- + +class IsoDateCondition : public BaseNodeCondition +{ +public: + explicit IsoDateCondition(QString str=QString()); + ~IsoDateCondition() {} + + bool execute(VItem*) {return false;} + std::string print(); + virtual qint64 secsSinceEpoch(VItem*) const {return secsSinceEpoch_;} + +private: + qint64 secsSinceEpoch_; + +}; + +// ---------------------------------- +// Node status change date condition +// ---------------------------------- + +class NodeStatusChangeDateCondition : public IsoDateCondition +{ +public: + explicit NodeStatusChangeDateCondition() {} + ~NodeStatusChangeDateCondition() {} + + bool execute(VItem*) {return false;} + std::string print(); + qint64 secsSinceEpoch(VItem*) const; +}; + +// -------------------------------- +// ISO date comparison conditions +// -------------------------------- + +class IsoDateGreaterThanEqualCondition : public BaseNodeCondition +{ +public: + IsoDateGreaterThanEqualCondition() {} + ~IsoDateGreaterThanEqualCondition() {} + + bool execute(VItem *node); + int numOperands() {return 2;} + std::string print(); +}; + +class IsoDateLessThanEqualCondition : public BaseNodeCondition +{ +public: + IsoDateLessThanEqualCondition() {} + ~IsoDateLessThanEqualCondition() {} + + bool execute(VItem *node); + int numOperands() {return 2;} + std::string print(); +}; + +// ----------------------------------------------------------------- + +// ----------------- +// Search conditions +// ----------------- + +class WhatToSearchInOperand : public BaseNodeCondition +{ +public: + explicit WhatToSearchInOperand(std::string what, bool &attr); + ~WhatToSearchInOperand(); + + const std::string& name() const {return what_;} + bool execute(VItem* node) {return false;} // not called + std::string print() {return what_;} + const std::string& what() const {return what_;} + +private: + std::string what_; // TODO XXX: optimise - we should store an enum here + bool searchInAttributes_; + + bool searchInAttributes() {return searchInAttributes_;} +}; + +// ----------------------------------------------------------------- + +class WhatToSearchForOperand : public BaseNodeCondition +{ +public: + explicit WhatToSearchForOperand(const std::string& what) : what_(what) {} + ~WhatToSearchForOperand(); + + std::string name() {return what_;} + bool execute(VItem* node) {return false;} // not called + std::string print() {return what_;} + std::string what() {return what_;} + +private: + std::string what_; +}; + +// ------------------------ +// Attribute condition +// ------------------------ + +class AttributeCondition : public BaseNodeCondition +{ +public: + //explicit AttributeCondition(NodeExpressionParser::AttributeType type) {type_ = type;} + explicit AttributeCondition(VAttributeType* type) : type_(type) {} + ~AttributeCondition() {} + + bool execute(VItem*); + //std::string print() {return NodeExpressionParser::instance()->toAttrName(type_);} + std::string print() {return ""; /*type_->strName();*/} + +private: + //NodeExpressionParser::AttributeType type_; + VAttributeType *type_; +}; + +//--------------------------------- +// Node attribute state condition +// ---------------------------- + +class AttributeStateCondition : public BaseNodeCondition +{ +public: + explicit AttributeStateCondition(QString attrState) : attrState_(attrState) {} + ~AttributeStateCondition() {} + + bool execute(VItem*); + std::string print() {return attrState_.toStdString();} + +private: + QString attrState_; +}; + + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,119 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include +#include + +#include "NodeFilterDialog.hpp" +#include "SessionHandler.hpp" +#include "VConfig.hpp" + +NodeFilterDialog::NodeFilterDialog(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + //setAttribute(Qt::WA_DeleteOnClose); + editor_->setFilterMode(true); + + QString wt=windowTitle(); + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + connect(applyPb_,SIGNAL(clicked()), + this,SLOT(accept())); + + connect(cancelPb_,SIGNAL(clicked()), + this,SLOT(reject())); + + leftVb_->addStretch(1); + + //Read the qt settings + readSettings(); +} + +NodeFilterDialog::~NodeFilterDialog() +{ +} + +void NodeFilterDialog::setQuery(NodeQuery* q) +{ + editor_->setQuery(q); +} + +NodeQuery* NodeFilterDialog::query() const +{ + return editor_->query(); +} + +void NodeFilterDialog::setServerFilter(ServerFilter* sf) +{ + editor_->setServerFilter(sf); +} + +void NodeFilterDialog::closeEvent(QCloseEvent * event) +{ + event->accept(); + writeSettings(); +} + +void NodeFilterDialog::accept() +{ + writeSettings(); + QDialog::accept(); +} + + +void NodeFilterDialog::reject() +{ + writeSettings(); + QDialog::reject(); +} + +//------------------------------------------ +// Settings read/write +//------------------------------------------ + +void NodeFilterDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeFilterDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void NodeFilterDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeFilterDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(590,460)); + } + + settings.endGroup(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,46 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEFILTERDIALOG_HPP_ +#define VIEWER_SRC_NODEFILTERDIALOG_HPP_ + +#include + +#include "ui_NodeFilterDialog.h" + +class ServerFilter; + +class NodeFilterDialog : public QDialog, protected Ui::NodeFilterDialog +{ + Q_OBJECT + +public: + explicit NodeFilterDialog(QWidget *parent = 0); + ~NodeFilterDialog(); + + void setQuery(NodeQuery*); + NodeQuery* query() const; + void setServerFilter(ServerFilter*); + +protected Q_SLOTS: + void accept(); + void reject(); + +protected: + void closeEvent(QCloseEvent * event); + +private: + void readSettings(); + void writeSettings(); +}; + + + +#endif /* VIEWER_SRC_NODEFILTERDIALOG_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeFilterDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeFilterDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,71 @@ + + + NodeFilterDialog + + + + 0 + 0 + 738 + 696 + + + + Define filter for table view + + + + + + + + + + + + + + + Apply + + + + :/viewer/submit.svg:/viewer/submit.svg + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Cancel + + + + + + + + + + NodeQueryEditor + QWidget +
      NodeQueryEditor.hpp
      + 1 +
      +
      + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeObserver.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeObserver.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeObserver.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeObserver.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef NODEOBSERVER_HPP_ +#define NODEOBSERVER_HPP_ + +#include "Aspect.hpp" +#include "Node.hpp" + +class VNode; +class VNodeChange; + +class NodeObserver +{ +public: + NodeObserver() {} + virtual ~NodeObserver() {} + + virtual void notifyBeginNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&)=0; + virtual void notifyEndNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&)=0; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodePanel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodePanel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodePanel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodePanel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,444 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "NodePanel.hpp" + +#include "Dashboard.hpp" +#include "DashboardTitle.hpp" +#include "InfoPanel.hpp" +#include "ServerHandler.hpp" +#include "VSettings.hpp" + +#include +#include + +NodePanel::NodePanel(QWidget* parent) : + TabWidget(parent) + +{ + setObjectName("p"); + + connect(this,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotCurrentWidgetChanged(int))); + + connect(this,SIGNAL(newTabRequested()), + this,SLOT(slotNewTab())); + + connect(this,SIGNAL(tabRemoved()), + this,SLOT(slotTabRemoved())); + +} + +NodePanel::~NodePanel() +{ +} + +//============================================= +// +// Tab management desktopAction +// +//============================================= + +Dashboard *NodePanel::addWidget(QString id) +{ + Dashboard *nw=new Dashboard("",this); + + QString name(""); + QPixmap pix; + + addTab(nw,pix,name); + + connect(nw,SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChangedInWidget(VInfo_ptr))); + + connect(nw->titleHandler(),SIGNAL(changed(DashboardTitle*)), + this,SLOT(slotTabTitle(DashboardTitle*))); + + connect(nw,SIGNAL(contentsChanged()), + this,SIGNAL(contentsChanged())); + + adjustTabTitle(); + + //init the title + slotTabTitle(nw->titleHandler()); + + return nw; +} + + +void NodePanel::resetWidgets(QStringList idLst) +{ + if(idLst.count() ==0) + return; + + clear(); + + Q_FOREACH(QString id,idLst) + { + addWidget(id); + } +} + +//Handle the tab bar context menu actions +void NodePanel::tabBarCommand(QString name,int index) +{ + if(name == "reloadTab") + { + Dashboard *w=nodeWidget(index); + if(w) w->reload(); + } + else if(name == "closeOtherTabs") + { + removeOtherTabs(index); + } + else if(name == "closeTab") + { + removeTab(index); + } +} + +Dashboard *NodePanel::nodeWidget(int index) +{ + QWidget *w=widget(index); + return (w)?static_cast(w):0; +} + +Dashboard *NodePanel::currentDashboard() +{ + QWidget *w=currentWidget(); + return static_cast(w); +} + +void NodePanel::slotCurrentWidgetChanged(int /*index*/) +{ + for(int i=0; i < count(); i++) + { + if(QWidget *w=widget(i)) + { + if(Dashboard* nw=static_cast(w)) + nw->titleHandler()->setCurrent(i==currentIndex()); + } + } + + Q_EMIT currentWidgetChanged(); + //setDefaults(this); +} + +void NodePanel::slotNewTab() +{ + Dashboard *w=addWidget(""); + w->addWidget("tree"); +} + +VInfo_ptr NodePanel::currentSelection() +{ + if(Dashboard *w=currentDashboard()) + return w->currentSelection(); + + return VInfo_ptr(); +} + +void NodePanel::slotSelection(VInfo_ptr n) +{ + if(Dashboard *w=currentDashboard()) + w->currentSelection(n); +} + +void NodePanel::slotSelectionChangedInWidget(VInfo_ptr n) +{ + if(Dashboard *w=static_cast(sender())) + { + if(w == currentDashboard()) + Q_EMIT selectionChangedInCurrent(n); + + } +} + +bool NodePanel::selectInTreeView(VInfo_ptr info) +{ + for(int i=0; i < count(); i++) + { + if(QWidget *w=widget(i)) + { + if(Dashboard* nw=static_cast(w)) + if(nw->selectInTreeView(info)) + { + setCurrentIndex(i); + return true; + } + } + } + return false; +} + +void NodePanel::setViewMode(Viewer::ViewMode mode) +{ + Dashboard *w=currentDashboard(); + if(w) w->setViewMode(mode); + //setDefaults(this); +} + +Viewer::ViewMode NodePanel::viewMode() +{ + Dashboard *w=currentDashboard(); + return (w)?w->viewMode():Viewer::NoViewMode; +} + +ServerFilter* NodePanel::serverFilter() +{ + Dashboard *w=currentDashboard(); + return (w)?w->serverFilter():NULL; +} + +void NodePanel::addToDashboard(const std::string& type) +{ + if(Dashboard *w=currentDashboard()) + { + w->addWidget(type); + } +} + +void NodePanel::openDialog(VInfo_ptr info,const std::string& type) +{ + if(Dashboard *w=currentDashboard()) + { + w->slotPopInfoPanel(info,QString::fromStdString(type)); + } +} +void NodePanel::addSearchDialog() +{ + if(Dashboard *w=currentDashboard()) + { + w->addSearchDialog(); + } +} + + +void NodePanel::slotTabTitle(DashboardTitle* t) +{ + int index=indexOfWidget(t->dashboard()); + if(index != -1) + { + setTabText(index,t->title()); + setTabIcon(index,t->pix()); + setTabToolTip(index,t->tooltip()); + setTabWht(index,t->desc()); + setTabData(index,t->descPix()); + } +} + +void NodePanel::slotTabRemoved() +{ + adjustTabTitle(); +} + +int NodePanel::tabAreaWidth() const +{ + return width()-80; +} + +void NodePanel::adjustTabTitle() +{ + if(count() > 1) + { + int tabWidth=tabAreaWidth()/count(); + if(tabWidth < 30) + tabWidth=30; + + for(int i=0; i < count(); i++) + { + if(QWidget *w=widget(i)) + { + if(Dashboard* nw=static_cast(w)) + nw->titleHandler()->setMaxPixWidth(tabWidth); + } + } + } +} + +void NodePanel::resizeEvent(QResizeEvent *e) +{ + if(abs(e->oldSize().width()-width()) > 5) + adjustTabTitle(); +} + +/*void NodePanel::slotNewWindow(bool) +{ + //MainWindow::openWindow("",this); + + if(Folder* f=currentFolder()) + { + QString p=QString::fromStdString(f->fullName()); + MvQFileBrowser::openBrowser(p,this); + } +}*/ + +/*void NodePanel::setViewMode(Viewer::NodeViewMode mode) +{ + NodeWidget *w=currentNodeWidget(); + if(w) w->setViewMode(mode); + //setDefaults(this); +} + +Viewer::FolderViewMode NodePanel::viewMode() +{ + NodeWidget *w=currentNodeWidget(); + return (w)?w->viewMode():MvQ::NoViewMode; +}*/ + +//========================================================== +// +// +// +//========================================================== + +void NodePanel::reload() +{ + for(int i=0; i < count(); i++) + { + if(QWidget *w=widget(i)) + { + if(Dashboard* nw=static_cast(w)) + nw->reload(); + } + } +} + +void NodePanel::rerender() +{ + for(int i=0; i < count(); i++) + { + if(QWidget *w=widget(i)) + { + if(Dashboard* nw=static_cast(w)) + nw->rerender(); + } + } +} + + +void NodePanel::refreshCurrent() +{ + ServerFilter* filter=serverFilter(); + if(!filter) + return; + + for(std::vector::const_iterator it=filter->items().begin(); it != filter->items().end(); ++it) + { + if(ServerHandler *sh=(*it)->serverHandler()) + { + sh->refresh(); + } + } +} + + +void NodePanel::resetCurrent() +{ + ServerFilter* filter=serverFilter(); + if(!filter) + return; + + for(std::vector::const_iterator it=filter->items().begin(); it != filter->items().end(); ++it) + { + if(ServerHandler *sh=(*it)->serverHandler()) + { + sh->reset(); + } + } +} + +void NodePanel::init() +{ + Dashboard* nw=addWidget(""); + if(nw) + { + nw->addWidget("tree"); + } +} + +//========================================================== +// +// Save/restore settings +// +//========================================================== + +void NodePanel::writeSettings(VComboSettings *vs) +{ + int currentIdx=(currentIndex()>=0)?currentIndex():0; + + vs->put("tabCount",count()); + vs->put("currentTabId",currentIdx); + + for(int i=0; i < count(); i++) + { + //boost::property_tree::ptree ptTab; + if(Dashboard* nw=nodeWidget(i)) + { + std::string id=NodePanel::tabSettingsId(i); + vs->beginGroup(id); + nw->writeSettings(vs); + vs->endGroup(); + //pt.add_child("tab_"+ boost::lexical_cast(i),ptTab); + } + } +} + +void NodePanel::readSettings(VComboSettings *vs) +{ + using boost::property_tree::ptree; + + int cnt=vs->get("tabCount",0); + int currentIndex=vs->get("currentTabId",-1); + + for(int i=0; i < cnt; i++) + { + std::string id=NodePanel::tabSettingsId(i); + if(vs->contains(id)) + { + Dashboard* nw=addWidget(""); + if(nw) + { + vs->beginGroup(id); + nw->readSettings(vs); + vs->endGroup(); + } + } + } + + //Set current tab + if(currentIndex >=0 && currentIndex < count()) + { + setCurrentIndex(currentIndex); + } + + //If no tabs have been created + if(count()==0) + { + addWidget(""); + setCurrentIndex(0); + if(Dashboard* d=currentDashboard()) + { + d->addWidget("tree"); + } + } + + if(QWidget *w=currentDashboard()) + w->setFocus(); + + //We emit it to trigger the whole window ui update! + Q_EMIT currentWidgetChanged(); +} + +std::string NodePanel::tabSettingsId(int i) +{ + return "tab_" + boost::lexical_cast(i); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodePanel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodePanel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodePanel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodePanel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,83 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef NODEPANEL_HPP_ +#define NODEPANEL_HPP_ + +#include "Viewer.hpp" +#include "TabWidget.hpp" +#include "VInfo.hpp" + +#include + +#include + +class Dashboard; +class DashboardTitle; +class ServerFilter; +class VComboSettings; + +class NodePanel : public TabWidget +{ + Q_OBJECT + +public: + explicit NodePanel(QWidget* parent=0); + virtual ~NodePanel(); + + void setViewMode(Viewer::ViewMode); + Viewer::ViewMode viewMode(); + + ServerFilter* serverFilter(); + + Dashboard* currentDashboard(); + void addWidget(); + void resetWidgets(QStringList); + void reload(); + void rerender(); + void refreshCurrent(); + void resetCurrent(); + VInfo_ptr currentSelection(); + bool selectInTreeView(VInfo_ptr); + void addToDashboard(const std::string& type); + void init(); + void openDialog(VInfo_ptr,const std::string& type); + void addSearchDialog(); + + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + +public Q_SLOTS: + void slotCurrentWidgetChanged(int); + void slotSelection(VInfo_ptr); + void slotNewTab(); + void slotSelectionChangedInWidget(VInfo_ptr); + +protected Q_SLOTS: + void slotTabRemoved(); + void slotTabTitle(DashboardTitle* w); + +Q_SIGNALS: + void itemInfoChanged(QString); + void currentWidgetChanged(); + void selectionChangedInCurrent(VInfo_ptr); + void contentsChanged(); + +protected: + void resizeEvent(QResizeEvent *e); + void adjustTabTitle(); + int tabAreaWidth() const; + + Dashboard* addWidget(QString); + void tabBarCommand(QString, int); + Dashboard* nodeWidget(int index); + static std::string tabSettingsId(int i); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodePathWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodePathWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodePathWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodePathWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1426 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#include "NodePathWidget.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "VNode.hpp" +#include "VProperty.hpp" +#include "Palette.hpp" +#include "PropertyMapper.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "VNState.hpp" +#include "VSState.hpp" +#include "VSettings.hpp" + +static std::vector propVec; + +QColor NodePathItem::disabledBgCol_; +QColor NodePathItem::disabledBorderCol_; +QColor NodePathItem::disabledFontCol_; +int NodePathItem::triLen_=10; +int NodePathItem::height_=0; +int NodePathItem::hPadding_=2; +int NodePathItem::vPadding_=0; + +//#define _UI_NODEPATHWIDGET_DEBUG + +BcWidget::BcWidget(QWidget* parent) : + QWidget(parent), + hMargin_(1), + vMargin_(1), + gap_(5), + width_(0), + maxWidth_(0), + itemHeight_(0), + text_("No selection"), + textCol_(Qt::white), + textDisabledCol_(QColor(220,220,220)), + useGrad_(true), + gradLighter_(150), + hovered_(-1), + elided_(false) +{ + font_=QFont(); + font_.setPointSize(font_.pointSize()-1); + QFontMetrics fm(font_); + + itemHeight_=NodePathItem::height(font_); + height_=itemHeight_+2*vMargin_; + + setMouseTracking(true); + + //Property + if(propVec.empty()) + { + propVec.push_back("view.common.node_style"); + propVec.push_back("view.common.node_gradient"); + } + + prop_=new PropertyMapper(propVec,this); + + updateSettings(); + + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); + setMinimumSize(width_,height_); + + ellipsisItem_ = new NodePathEllipsisItem(this); + ellipsisItem_->visible_=false; + + reset(items_,100); + + //setAutoFillBackground(true); + //QPalette pal=palette(); + //pal.setColor(QPalette::Window,Qt::transparent); + //setPalette(pal); +} + +BcWidget::~BcWidget() +{ + delete prop_; + delete ellipsisItem_; +} + +void BcWidget::notifyChange(VProperty *p) +{ + updateSettings(); +} + +void BcWidget::updateSettings() +{ + if(VProperty *p=prop_->find("view.common.node_gradient")) + useGrad_=p->value().toBool(); +} + +bool BcWidget::isFull() const +{ + return !elided_ && !ellipsisItem_->visible_; +} + +void BcWidget::clear() +{ + items_.clear(); + reset(items_,100); +} + +void BcWidget::resetBorder(int idx) +{ + if(idx >=0 && idx < items_.count()) + { + items_.at(idx)->resetBorder(idx == hovered_); + updatePixmap(idx); + update(); + } +} + +void BcWidget::reset(QString txt, int maxWidth) +{ + maxWidth_=maxWidth; + hovered_=-1; + ellipsisItem_->visible_=false; + elided_=false; + + QFontMetrics fm(font_); + int xp=hMargin_; + int yp=vMargin_; + + if(!txt.isEmpty()) + { + text_=txt; + } + else + { + text_="No selection"; + } + + int len=fm.width(text_); + textRect_=QRect(xp,yp,len,itemHeight_); + width_=xp+len+4; + + crePixmap(); + resize(width_,height_); + update(); +} + +void BcWidget::reset(int idx,QString text,QColor bgCol,QColor fontCol) +{ + if(idx >=0 && idx < items_.count()) + { + bool newText=(text != items_.at(idx)->text_); + items_[idx]->reset(text,bgCol,fontCol,idx == hovered_); + + if(newText) + reset(items_,maxWidth_); + else + { + updatePixmap(idx); + update(); + } + } +} + +void BcWidget::reset(QList items, int maxWidth) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "BcWidget::reset -->"; + UiLog().dbg() << " maxWidth=" << maxWidth; +#endif + + maxWidth_=maxWidth; + items_=items; + hovered_=-1; + ellipsisItem_->visible_=false; + elided_=false; + + QFontMetrics fm(font_); + int xp=hMargin_; + int yp=vMargin_; + + if(items_.count() ==0) + { + int len=fm.width(text_); + textRect_=QRect(xp,yp,len,itemHeight_); + width_=xp+len+4; + } + else + { + // + // xp is the top right corner of the shape (so it is not the rightmost edge) + // + // server shape: + // + // ******** + // * * + // ******** + // + // other shape: + // + // ******** + // * * + // ******** + // + + NodePathItem *lastItem=items_[items_.count()-1]; + Q_ASSERT(lastItem); + int maxRedTextLen=0; + + //Defines the shapes and positions for all the items + for(int i=0; i < items_.count(); i++) + { + xp=items_[i]->adjust(xp,yp); + + if(i != items_.count()-1) + { + xp+=gap_; + int tl=items_[i]->textLen(); + if(tl > maxRedTextLen) + maxRedTextLen=tl; + } + } + + //The total width + width_=xp+NodePathItem::triLen_+hMargin_; + +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " full width=" << width_; +#endif + + //maxWidth-=2*hMargin_; + + //If the total width is too big we try to use elidedtext in the items + //(with the execption of the last item) + int redTextLen=0; + if(width_ > maxWidth) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " try elided text"; +#endif + //Try different elided text lenghts + for(int i=20; i >= 3; i--) + { + QString t; + for(int j=0; j < i; j++) + { + t+="A"; + } + t+="..."; + + //We only check the elided texts that are shorter then the max text len + redTextLen=fm.width(t); + if(redTextLen < maxRedTextLen) + { + //Estimate the total size with the elided text items + xp=hMargin_; + int estWidth=estimateWidth(0,xp,redTextLen); + + //if the size fits into maxWidth we adjust all the items + if(estWidth < maxWidth) + { + int xp=hMargin_; + width_ = adjustItems(0,xp,yp,redTextLen); + elided_=true; + + Q_ASSERT(width_== estWidth); + Q_ASSERT(width_ < maxWidth); + break; //This breaks the whole for loop + } + } + } + } + + //If the total width is still too big we start hiding items from the left + //and insert an ellipsis item to the front. + int xpAfterEllipsis=0; + if(width_ > maxWidth) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " insert ellipsis to front + remove items"; + UiLog().dbg() << " redTextLen=" << redTextLen; +#endif + Q_ASSERT(elided_==false); + + //width_=maxWidth; + + xp=hMargin_; + ellipsisItem_->visible_=true; + ellipsisItem_->adjust(xp,yp); + xp=ellipsisItem_->estimateRightPos(xp); + xpAfterEllipsis=xp+gap_; + bool fitOk=false; + int estWidth=0; + for(int i=0; i < items_.count()-1; i++) + { + xp=xpAfterEllipsis; + items_[i]->visible_=false; +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " omit item " << i; +#endif + estWidth=estimateWidth(i+1,xp,redTextLen); +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " estWidth " << estWidth; +#endif + if(estWidth < maxWidth) + { + fitOk=true; + break; + } + } + + if(fitOk) + { + xp=xpAfterEllipsis; + width_=adjustVisibleItems(0,xp,yp,redTextLen); + Q_ASSERT(width_ == estWidth); + Q_ASSERT(width_ < maxWidth); + } + else + { + xp=xpAfterEllipsis; + xp=lastItem->estimateRightPos(xp); + estWidth=xp+NodePathItem::triLen_+hMargin_; + if(estWidth < maxWidth) + { + xp=xpAfterEllipsis; + xp=lastItem->adjust(xp,yp); + width_=xp+NodePathItem::triLen_+hMargin_; + Q_ASSERT(width_ == estWidth); + Q_ASSERT(width_ < maxWidth); + } + + } + } + + //If the total width is still too big we try to use elidedtext in the last item + //(at this point all the other items are hidden) + if(width_ > maxWidth) + { + int len=lastItem->textLen(); + + //Try different elided text lenghts + for(int i=30; i >= 3; i--) + { + QString t; + for(int j=0; j < i; j++) + { + t+="A"; + } + t+="..."; + + //We only check the elided texts that are shorter then the max text len + redTextLen=fm.width(t); + if(redTextLen < len) + { + //Estimate the total size with the elided text item + xp=xpAfterEllipsis; + xp=lastItem->estimateRightPos(xp,redTextLen); + int estWidth=xp+NodePathItem::triLen_+hMargin_; + if(estWidth < maxWidth) + { + xp=xpAfterEllipsis; + xp=lastItem->adjust(xp,yp,redTextLen); + width_=xp+NodePathItem::triLen_+hMargin_; + Q_ASSERT(width_ == estWidth); + Q_ASSERT(width_ < maxWidth); + break; + } + } + } + } + + //If the total width is still too big we also hide the last item and + //only show the ellipsis item + if(width_ > maxWidth) + { + lastItem->visible_=false; + width_=maxWidth; + } + } + + crePixmap(); + + resize(width_,height_); + + update(); +} + +int BcWidget::estimateWidth(int startIndex,int xp,int redTextLen) +{ + for(int i=startIndex; i < items_.count(); i++) + { + if(i != items_.count()-1) + { + xp=items_[i]->estimateRightPos(xp,redTextLen); + xp+=gap_; + } + else + xp=items_[i]->estimateRightPos(xp); + } + + return xp+NodePathItem::triLen_+hMargin_; +} + +int BcWidget::adjustItems(int startIndex,int xp,int yp,int redTextLen) +{ + for(int i=startIndex; i < items_.count(); i++) + { + if(i != items_.count()-1) + { + xp=items_[i]->adjust(xp,yp,redTextLen); + xp+=gap_; + } + else + xp=items_[i]->adjust(xp,yp); + } + + return xp+NodePathItem::triLen_+hMargin_; +} + +int BcWidget::adjustVisibleItems(int startIndex,int xp,int yp,int redTextLen) +{ + for(int i=startIndex; i < items_.count(); i++) + { + if(items_[i]->visible_) + { + if(i != items_.count()-1) + { + xp=items_[i]->adjust(xp,yp,redTextLen); + xp+=gap_; + } + else + xp=items_[i]->adjust(xp,yp); + } + } + return xp+NodePathItem::triLen_+hMargin_; +} + +void BcWidget::adjustSize(int maxWidth) +{ + if(isFull()) + { + if(width_ > maxWidth) + reset(items_,maxWidth); + } + else + { + reset(items_,maxWidth); + } +} + +void BcWidget::crePixmap() +{ + pix_=QPixmap(width_,height_); + pix_.fill(Qt::transparent); + + QPainter painter(&pix_); + painter.setRenderHints(QPainter::Antialiasing,true); + + painter.setFont(font_); + + if(items_.count() == 0) + { + if(isEnabled()) + painter.setPen(textCol_); + else + painter.setPen(textDisabledCol_); + + painter.drawText(textRect_,Qt::AlignHCenter | Qt::AlignVCenter, text_); + } + else + { + for(int i=0; i < items_.count(); i++) + { + items_.at(i)->enabled_=isEnabled(); + items_.at(i)->draw(&painter,useGrad_,gradLighter_); + } + } + + if(ellipsisItem_->visible_) + { + ellipsisItem_->enabled_=isEnabled(); + ellipsisItem_->draw(&painter,false,gradLighter_); + } +} + +void BcWidget::updatePixmap(int idx) +{ + if(idx >=0 && idx < items_.count()) + { + QPainter painter(&pix_); + painter.setRenderHints(QPainter::Antialiasing,true); + painter.setFont(font_); + items_.at(idx)->draw(&painter,useGrad_,gradLighter_); + } +} + +void BcWidget::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + painter.drawPixmap(0,0,pix_); +} + +void BcWidget::mouseMoveEvent(QMouseEvent *event) +{ + for(int i=0; i < items_.count(); i++) + { + if(items_.at(i)->shape_.containsPoint(event->pos(),Qt::OddEvenFill)) + { + if(hovered_ == -1) + { + hovered_=i; + resetBorder(i); + } + else if(hovered_ != i) + { + int prev=hovered_; + hovered_=i; + resetBorder(prev); + resetBorder(i); + } + + return; + } + } + + if(hovered_ != -1) + { + int prev=hovered_; + hovered_=-1; + resetBorder(prev); + } + + QWidget::mouseMoveEvent(event); +} + +void BcWidget::mousePressEvent(QMouseEvent *event) +{ + if(event->button() != Qt::RightButton && event->button() != Qt::LeftButton) + return; + + for(int i=0; i < items_.count(); i++) + { + if(items_[i]->visible_ && + items_[i]->shape_.containsPoint(event->pos(),Qt::OddEvenFill)) + { + if(event->button() == Qt::RightButton) + { + Q_EMIT menuSelected(i,event->pos()); + return; + } + else if(event->button() == Qt::LeftButton) + { + Q_EMIT itemSelected(i); + return; + } + } + } + + QWidget::mousePressEvent(event); +} + +void BcWidget::changeEvent(QEvent* event) +{ + if(event->type() == QEvent::EnabledChange) + { + crePixmap(); +//TODO: Will update be called automatically? + } + + QWidget::changeEvent(event); +} + +//===================================================== +// +// NodePathItem +// +//===================================================== + +NodePathItem::NodePathItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current) : + owner_(owner), + index_(index), + text_(text), + bgCol_(bgCol), + fontCol_(fontCol), + current_(current), + hasMenu_(hasMenu), + visible_(false), + enabled_(true) +{ + height(owner_->font()); + + if(!disabledBgCol_.isValid()) + { + disabledBgCol_=QColor(200,200,200); + disabledBorderCol_=QColor(170,170,170); + disabledFontCol_=QColor(40,40,40); + } + + grad_.setCoordinateMode(QGradient::ObjectBoundingMode); + grad_.setStart(0,0); + grad_.setFinalStop(0,1); +} + +int NodePathItem::height(QFont f) +{ + if(height_==0) + { + QFontMetrics fm(f); + height_=fm.height()+2*vPadding_; + } + return height_; +} + +void NodePathItem::setCurrent(bool) +{ +} + +int NodePathItem::textLen() const +{ + QFontMetrics fm(owner_->font()); + return fm.width(text_); +} + +void NodePathItem::makeShape(int xp,int yp,int len) +{ + QVector vec; + vec << QPoint(0,0); + vec << QPoint(len+triLen_,0); + vec << QPoint(len+2*triLen_,height_/2); + vec << QPoint(len+triLen_,height_); + vec << QPoint(0,height_); + vec << QPoint(triLen_,height_/2); + + shape_=QPolygon(vec).translated(xp,yp); + + textRect_=QRect(xp+triLen_+hPadding_,yp,len,height_); +} + +int NodePathItem::adjust(int xp,int yp,int elidedLen) +{ + visible_=true; + + QFontMetrics fm(owner_->font()); + int len; + if(elidedLen == 0) + { + elidedText_=QString(); + len=fm.width(text_); + } + else + { + elidedText_=fm.elidedText(text_,Qt::ElideRight,elidedLen); + len=fm.width(elidedText_); + } + + borderCol_=bgCol_.darker(125); + + makeShape(xp,yp,len); + + return rightPos(xp,len); +} + + +//It returns the x position of the top right corner! +int NodePathItem::rightPos(int xp,int len) const +{ + return xp+len+triLen_; +} + + +//It returns the x position of the top right corner! +int NodePathItem::estimateRightPos(int xp,int elidedLen) +{ + QFontMetrics fm(owner_->font()); + int len; + + if(elidedLen==0) + len=fm.width(text_); + else + len=fm.width(fm.elidedText(text_,Qt::ElideRight,elidedLen)); + + return rightPos(xp,len); +} + +void NodePathItem::resetBorder(bool hovered) +{ + if(!hovered) + borderCol_=bgCol_.darker(125); + else + borderCol_=bgCol_.darker(240); +} + +void NodePathItem::reset(QString text,QColor bgCol,QColor fontCol,bool hovered) +{ + text_=text; + bgCol_=bgCol; + fontCol_=fontCol; + + if(!hovered) + borderCol_=bgCol_.darker(125); + else + borderCol_=bgCol_.darker(240); +} + +void NodePathItem::draw(QPainter *painter,bool useGrad,int lighter) +{ + if(!visible_) + return; + + QColor border, bg, fontCol; + if(enabled_) + { + border=borderCol_; + bg=bgCol_; + fontCol=fontCol_; + } + else + { + border=disabledBorderCol_; + bg=disabledBgCol_; + fontCol=disabledFontCol_; + } + + QBrush bgBrush; + if(useGrad) + { + QColor bgLight; + Palette::statusColours(bg,bgLight,border); + + //QColor bgLight=bg.lighter(lighter); + grad_.setColorAt(0,bgLight); + grad_.setColorAt(1,bg); + bgBrush=QBrush(grad_); + } + else + bgBrush=QBrush(bg); + + painter->setPen(QPen(border,0)); + painter->setBrush(bgBrush); + painter->drawPolygon(shape_); + + /*if(current_) + { + painter->setPen(QPen(borderCol_,0)); + }*/ + + painter->setPen(fontCol); + painter->drawText(textRect_,Qt::AlignVCenter | Qt::AlignHCenter,(elidedText_.isEmpty())?text_:elidedText_); + +} + +//===================================================== +// +// NodePathServerItem +// +//===================================================== + +//It returns the x position of the top right corner! +int NodePathServerItem::rightPos(int xp,int len) const +{ + return xp+len; +} + +void NodePathServerItem::makeShape(int xp,int yp,int len) +{ + QVector vec; + vec << QPoint(0,0); + vec << QPoint(len,0); + vec << QPoint(len+triLen_,height_/2); + vec << QPoint(len,height_); + vec << QPoint(0,height_); + + shape_=QPolygon(vec).translated(xp,yp); + + textRect_=QRect(xp+hPadding_,yp,len,height_); +} + +//===================================================== +// +// NodePathEllipsisItem +// +//===================================================== + +NodePathEllipsisItem::NodePathEllipsisItem(BcWidget* owner) : + NodePathItem(owner,-1,QString(0x2026),QColor(240,240,240),QColor(Qt::black),false,false) +{ + borderCol_=QColor(190,190,190); +} + +//============================================================= +// +// NodePathWidget +// +//============================================================= + +NodePathWidget::NodePathWidget(QWidget *parent) : + QWidget(parent), + mode_(GuiMode) +{ + layout_=new QHBoxLayout(this); + layout_->setSpacing(0); + layout_->setContentsMargins(2,2,3,2); + setLayout(layout_); + + bc_=new BcWidget(this); + layout_->addWidget(bc_); + + connect(bc_,SIGNAL(itemSelected(int)), + this,SLOT(slotNodeSelected(int))); + connect(bc_,SIGNAL(menuSelected(int,QPoint)), + this,SLOT(slotMenuSelected(int,QPoint))); + + setAutoFillBackground(true); + + //We make the background transparent + QPalette pal=palette(); + pal.setColor(QPalette::Window,Qt::transparent); + setPalette(pal); +} + +NodePathWidget::~NodePathWidget() +{ + clear(true); +} + +void NodePathWidget::useTransparentBg(bool b) +{ + QPalette pal=palette(); + + if(b) + { + pal.setColor(QPalette::Window,Qt::transparent); + bc_->setTextColour(Qt::white); + bc_->setTextDisabledColour(QColor(220,220,220)); + } + else + { + pal.setColor(QPalette::Window,Qt::white); + bc_->setTextColour(Qt::black); + bc_->setTextDisabledColour(QColor(60,60,60)); + } + setPalette(pal); +} + +void NodePathWidget::clear(bool detachObservers) +{ + setEnabled(true); + + if(detachObservers && info_ && info_->server()) + { + info_->server()->removeNodeObserver(this); + info_->server()->removeServerObserver(this); + } + + if(detachObservers && info_) + { + info_->removeObserver(this); + } + + if(info_) + info_->removeObserver(this); + + info_.reset(); + + clearItems(); + + setEnabled(true); +} + +void NodePathWidget::clearItems() +{ + bc_->clear(); + + int cnt=nodeItems_.count(); + for(int i=0; i < cnt; i++) + { + delete nodeItems_.takeLast(); + } + nodeItems_.clear(); +} + +void NodePathWidget::setMode(Mode mode) +{ + if(mode_ != mode) + { + mode_=mode; + VInfo_ptr info=info_; + clear(true); + setPath(info); + } +} + +void NodePathWidget::slotContextMenu(const QPoint& pos) +{ +} + +void NodePathWidget::adjust(VInfo_ptr info,ServerHandler** serverOut,bool &sameServer) +{ + ServerHandler* server=0; + + //Check if there is data in info + if(info) + { + server=info->server(); + + sameServer=(info_)?(info_->server() == server):false; + + //Handle observers + if(!sameServer) + { + if(info_ && info_->server()) + { + info_->server()->removeServerObserver(this); + info_->server()->removeNodeObserver(this); + } + + info->server()->addServerObserver(this); + info->server()->addNodeObserver(this); + +#if 0 + if(server) + { + if(reloadTb_) + { + reloadTb_->setToolTip("Refresh server " + QString::fromStdString(server->name()) + ""); + reloadTb_->setEnabled(true); + } + } + else + { + reloadTb_->setToolTip(""); + reloadTb_->setEnabled(false); + } +#endif + } + } + //If the there is no data we clean everything and return + else + { + if(info_ && info_->server()) + { + info_->server()->removeServerObserver(this); + info_->server()->removeNodeObserver(this); + } +#if 0 + reloadTb_->setToolTip(""); + reloadTb_->setEnabled(false); +#endif + } + + //Set the info + if(info_) + { + info_->removeObserver(this); + } + + info_=info; + + if(info_) + { + info_->addObserver(this); + } + + *serverOut=server; +} + + +void NodePathWidget::reset() +{ + setPath(info_); +} + + +void NodePathWidget::setPath(VInfo_ptr info) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::setPath -->"; +#endif + + setEnabled(true); + + ServerHandler *server=0; + bool sameServer=false; + + VInfo_ptr info_ori=info_; + + adjust(info,&server,sameServer); + + if(!info_ || !info_->server()) + { + clear(); + return; + } + else + { + clearItems(); + } + + //------------------------------------ + // Only text is displayed + //------------------------------------ + + if(mode_ == TextMode) + { + info_=info; + QString pt; + if(info_) + pt=QString::fromStdString(info_->path()); + + bc_->reset(pt,bcWidth()); + return; + } + + //------------------------------------ + // Interactive breadcrumsbs + //------------------------------------ + + Q_ASSERT(mode_ = GuiMode); + + //Get the node list including the server + std::vector lst; + if(info_->node()) + { + lst=info_->node()->ancestors(VNode::ParentToChildSort); + } + + //-------------------------------------------- + // Reset/rebuild the contents + //-------------------------------------------- + + for(unsigned int i=0; i < lst.size(); i++) + { + //--------------------------- + // Create node/server item + //--------------------------- + + QColor col; + QString name; + NodePathItem* nodeItem=0; + + VNode *n=lst.at(i); + col=n->stateColour(); +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " state=" << n->stateName(); +#endif + QColor fontCol=n->stateFontColour(); + name=n->name(); + bool hasChildren=(n->numOfChildren() >0); + + if(i==0) + { + nodeItem=new NodePathServerItem(bc_,i,name,col,fontCol,hasChildren,(i == lst.size()-1)?true:false); + } + else + { + nodeItem=new NodePathItem(bc_,i,name,col,fontCol,hasChildren,(i == lst.size()-1)?true:false); + } + nodeItems_ << nodeItem; + } + + bc_->reset(nodeItems_,bcWidth()); + +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "<-- setPath"; +#endif +} + +int NodePathWidget::bcWidth() +{ + //return width()-reloadTb_->width()-5; + return width()-5; +} + +void NodePathWidget::slotNodeSelected(int idx) +{ + Q_ASSERT(mode_ == GuiMode); + if(idx != -1) + { + Q_EMIT selected(nodeAt(idx)); + } +} + +void NodePathWidget::slotMenuSelected(int idx,QPoint bcPos) +{ + Q_ASSERT(mode_ == GuiMode); + if(idx != -1) + { + loadMenu(bc_->mapToGlobal(bcPos),nodeAt(idx)); + } +} + +//------------------------------------------------------------------------------------------- +// Get the object from nodeItems_ at position idx. +// This is the order/position of the items: +// +// 0 1 2 .... nodeItems_.count()-2 nodeItems_.count()-1 +// server node's parent node (=info_) +//-------------------------------------------------------------------------------------------- + +VInfo_ptr NodePathWidget::nodeAt(int idx) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::nodeAt idx=" << idx; +#endif + + Q_ASSERT(mode_ == GuiMode); + if(mode_ == TextMode) + return VInfo_ptr(); + + ServerHandler* server=info_->server(); + + if(info_ && server) + { + if(VNode *n=info_->node()->ancestorAt(idx,VNode::ParentToChildSort)) + { + if(n == info_->node()) + return info_; + else if(n->isServer()) + return VInfoServer::create(n->server()); + else + return VInfoNode::create(n); + } + } + + return VInfo_ptr(); +} + +void NodePathWidget::loadMenu(const QPoint& pos,VInfo_ptr p) +{ + Q_ASSERT(mode_ == GuiMode); + if(mode_ == TextMode) + return; + + if(p && p->node()) + { + QList acLst; + VNode* node=p->node(); + + for(int i=0; i < node->numOfChildren(); i++) + { + QAction *ac=new QAction(node->childAt(i)->name(),this); + ac->setData(i); + acLst << ac; + } + + if(acLst.count() > 0) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().info() << "NodePathWidget::loadMenu"; +#endif + + if(QAction *ac=QMenu::exec(acLst,pos,acLst.front(),this)) + { + int idx=ac->data().toInt(); + VInfo_ptr res=VInfoNode::create(node->childAt(idx)); + Q_EMIT selected(res); + } + } + + Q_FOREACH(QAction* ac,acLst) + { + delete ac; + } + } +} + +void NodePathWidget::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange&) +{ + Q_ASSERT(mode_ == GuiMode); + if(mode_ == TextMode) + return; + +#if 0 + if(!active_) + return; +#endif + + //Check if there is data in info + if(info_ && !info_->isServer() && info_->node()) + { + //TODO: MAKE IT SAFE!!!! + + //State changed + if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::STATE) != aspect.end() || + std::find(aspect.begin(),aspect.end(),ecf::Aspect::SUSPENDED) != aspect.end()) + { + std::vector nodes=info_->node()->ancestors(VNode::ParentToChildSort); + for(int i=0; i < static_cast(nodes.size()); i++) + { + if(nodes[i] == node) + { + if(i < nodeItems_.count()) + { + bc_->reset(i,node->name(),node->stateColour(),node->stateFontColour()); + } + return; + } + } + } + + //A child was removed or added + else if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_NODE) != aspect.end()) + { + std::vector nodes=info_->node()->ancestors(VNode::ParentToChildSort); + for(unsigned int i=0; i < nodes.size(); i++) + { + if(node == nodes.at(i)) + { + //Reload everything + setPath(info_); + } + } + } + } +} + +void NodePathWidget::notifyDefsChanged(ServerHandler* server,const std::vector& aspect) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::notifyDefsChanged -->"; +#endif + + Q_ASSERT(mode_ == GuiMode); + if(mode_ == TextMode) + return; +#if 0 + if(!active_) + return; +#endif + + //Check if there is data in info + if(info_ && info_->server() && info_->server() == server) + { + UiLog().dbg() << "Server change"; + + //State changed + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::SERVER_STATE) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " update server item"; +#endif + if(nodeItems_.count() > 0) + { + bc_->reset(0,server->vRoot()->name(), + server->vRoot()->stateColour(), + server->vRoot()->stateFontColour()); + } + + } + } + } +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "<-- notifyDefsChanged"; +#endif +} + +//This must be called at the beginning of a reset +void NodePathWidget::notifyBeginServerClear(ServerHandler* server) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::notifyBeginServerClear -->"; +#endif + if(info_) + { + if(info_->server() && info_->server() == server) + { + setEnabled(false); + } + } +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "<-- notifyBeginServerClear"; +#endif +} + +//This must be called at the end of a reset +void NodePathWidget::notifyEndServerScan(ServerHandler* server) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::notifyEndServerScan -->"; +#endif + if(info_) + { + if(info_->server() && info_->server() == server) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " setEnabled(true)"; +#endif + setEnabled(true); + +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " regainData"; +#endif + //We try to ressurect the info. We have to do it explicitly because it is not guaranteed that + //notifyEndServerScan() will be first called on the VInfo then on the breadcrumbs. So it + //is possible that the node still exists but it is still set to NULL in VInfo. + info_->regainData(); + + //If the info is not available dataLost() must have already been called and + //the breadcrumbs were reset! + if(!info_) + return; + + Q_ASSERT(info_->server() && info_->node()); + +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " reset"; +#endif + reset(); + } + } + +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "<-- notifyEndServerScan"; +#endif +} + +void NodePathWidget::notifyServerDelete(ServerHandler* server) +{ + if(info_ && info_->server() == server) + { + //We do not want to detach ourselves as an observer the from the server. When this function is + //called the server actually loops through its observers and notify them. + clear(false); + } +} + +void NodePathWidget::notifyServerConnectState(ServerHandler* server) +{ + //TODO: we need to indicate the state here! + if(info_ && info_->server() == server) + { + reset(); + } +} + +void NodePathWidget::notifyServerActivityChanged(ServerHandler* /*server*/) +{ + //reset(); +} + +void NodePathWidget::notifyDataLost(VInfo* info) +{ +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "NodePathWidget::notifyDataLost -->"; +#endif + + if(info_ && info_.get() == info) + { +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << " clear(true)"; +#endif + clear(true); + } +#ifdef _UI_NODEPATHWIDGET_DEBUG + UiLog().dbg() << "<-- notifyDataLost"; +#endif +} + +void NodePathWidget::slotRefreshServer() +{ + Q_ASSERT(mode_ == GuiMode); + if(mode_ == TextMode) + return; + + if(info_ && info_->server()) + { + info_->server()->refresh(); + } +} + +void NodePathWidget::rerender() +{ + reset(); +} + +void NodePathWidget::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void NodePathWidget::resizeEvent(QResizeEvent *) +{ + bc_->adjustSize(bcWidth()); +} + +void NodePathWidget::writeSettings(VSettings *vs) +{ + vs->beginGroup("breadcrumbs"); + vs->put("mode",(mode_==TextMode)?"text":"gui"); + vs->endGroup(); +} + +void NodePathWidget::readSettings(VSettings* vs) +{ + vs->beginGroup("breadcrumbs"); + std::string modeStr=vs->get("mode",""); + + if(modeStr == "text") + setMode(TextMode); + else + setMode(GuiMode); + + vs->endGroup(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodePathWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodePathWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodePathWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodePathWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,244 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef NODEPATHWIDGET_H +#define NODEPATHWIDGET_H + +#include "NodeObserver.hpp" +#include "ServerObserver.hpp" +#include "VInfo.hpp" +#include "VProperty.hpp" + +#include +#include + +#include + +class QAction; +class QHBoxLayout; +class QMenu; +class QToolButton; + +class Node; +class NodePathWidget; +class PropertyMapper; +class VProperty; +class VSettings; + +class NodePathItem; +class NodePathEllipsisItem; + +class BcWidget : public QWidget, public VPropertyObserver +{ + +Q_OBJECT + +public: + explicit BcWidget(QWidget *parent=0); + ~BcWidget(); + + void reset(QString txt, int maxWidth); + void reset(QList,int); + void reset(int idx,QString text,QColor bgCol,QColor fontCol); + void clear(); + void adjustSize(int); + QFont font() const {return font_;} + void setTextColour(QColor c) {textCol_=c;} + void setTextDisabledColour(QColor c) {textDisabledCol_=c;} + + void notifyChange(VProperty*); + +Q_SIGNALS: + void itemSelected(int); + void menuSelected(int,QPoint); + +protected: + void paintEvent(QPaintEvent*); + void mouseMoveEvent(QMouseEvent *event); + void mousePressEvent(QMouseEvent* event); + void changeEvent(QEvent* event); + + void updateSettings(); + void reset(int); + void resetBorder(int); + void crePixmap(); + void updatePixmap(int); + bool isFull() const; + int estimateWidth(int,int,int); + int adjustItems(int,int,int,int); + int adjustVisibleItems(int,int,int,int); + + QFont font_; + QPixmap pix_; + int hMargin_; + int vMargin_; + int gap_; + int width_; + int maxWidth_; + int height_; + int itemHeight_; + QString text_; + QRect textRect_; + QColor textCol_; + QColor textDisabledCol_; + QList items_; + NodePathEllipsisItem* ellipsisItem_; + + PropertyMapper* prop_; + bool useGrad_; + int gradLighter_; + int hovered_; + bool elided_; +}; + +class NodePathItem +{ + +friend class BcWidget; + +public: + NodePathItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current); + virtual ~NodePathItem() {} + + void setCurrent(bool); + virtual void draw(QPainter*,bool,int); + int adjust(int xp,int yp, int elidedLen=0); + int estimateRightPos(int xp,int elidedLen=0); + void resetBorder(bool hovered); + void reset(QString text,QColor bgCol,QColor fontCol,bool hovered); + int textLen() const; + static int height(QFont); + +protected: + virtual void makeShape(int xp,int yp,int len); + virtual int rightPos(int xp,int len) const; + + BcWidget* owner_; + int index_; + QString text_; + QString elidedText_; + QColor bgCol_; + QColor borderCol_; + QColor fontCol_; + QPolygon shape_; + QRect textRect_; + QFont font_; + + bool current_; + bool hasMenu_; + bool visible_; + QLinearGradient grad_; + bool enabled_; + static QColor disabledBgCol_; + static QColor disabledBorderCol_; + static QColor disabledFontCol_; + + static int triLen_; + static int height_; + static int hPadding_; + static int vPadding_; +}; + +class NodePathServerItem : public NodePathItem +{ + +friend class BcWidget; + +public: + NodePathServerItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current) : + NodePathItem(owner,index,text,bgCol,fontCol,hasMenu,current) {} +protected: + void makeShape(int xp,int yp,int len); + int rightPos(int xp,int len) const; +}; + + +class NodePathEllipsisItem : public NodePathItem +{ + +friend class BcWidget; + +public: + NodePathEllipsisItem(BcWidget* owner); +}; + + +class NodePathWidget : public QWidget, public NodeObserver, public ServerObserver, public VInfoObserver +{ +Q_OBJECT + +public: + explicit NodePathWidget(QWidget* parent=0); + ~NodePathWidget(); + + enum Mode {TextMode,GuiMode}; + + void clear(bool detachObservers=true); + void setMode(Mode mode); + bool isGuiMode() const {return mode_==GuiMode;} + void useTransparentBg(bool b); + + //From NodeObserver + void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); + void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) {} + + //From ServerObserver + void notifyDefsChanged(ServerHandler* server,const std::vector&); + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + void notifyEndServerClear(ServerHandler* server) {} + void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + void notifyEndServerScan(ServerHandler* server); + void notifyServerConnectState(ServerHandler* server); + void notifyServerActivityChanged(ServerHandler* server); + + //From VInfoObserver + void notifyDelete(VInfo*) {} + void notifyDataLost(VInfo*); + + void rerender(); + + void writeSettings(VSettings *vs); + void readSettings(VSettings *vs); + +public Q_SLOTS: + void setPath(VInfo_ptr); + +protected Q_SLOTS: + void slotContextMenu(const QPoint&); + void slotNodeSelected(int); + void slotMenuSelected(int,QPoint); + void slotRefreshServer(); + +Q_SIGNALS: + void selected(VInfo_ptr); + void activeStateChanged(); + +protected: + void clearItems(); + void adjust(VInfo_ptr info,ServerHandler** serverOut,bool &sameServer); + void loadMenu(const QPoint& pos,VInfo_ptr p); + VInfo_ptr nodeAt(int); + void infoIndex(int idx); + void paintEvent(QPaintEvent *); + void resizeEvent(QResizeEvent *); + void reset(); + void updateSettings(); + int bcWidth(); + + QList nodeItems_; + + QHBoxLayout* layout_; + VInfo_ptr info_; + VInfo_ptr infoFull_; + BcWidget* bc_; + Mode mode_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryCombo.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryCombo.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryCombo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryCombo.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,43 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryCombo.hpp" + +#include "NodeQuery.hpp" +#include "NodeQueryHandler.hpp" + +#include + +NodeQueryCombo::NodeQueryCombo(QWidget* parent) : QComboBox(parent) +{ + for(std::vector::const_iterator it=NodeQueryHandler::instance()->items().begin(); + it != NodeQueryHandler::instance()->items().end(); ++it) + { + addItem(QString::fromStdString((*it)->name())); + } + + connect(this,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotCurrentChanged(int))); +} + +void NodeQueryCombo::slotCurrentChanged(int current) +{ + if(current != -1) + Q_EMIT changed(itemData(current).toString()); +} + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryCombo.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryCombo.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryCombo.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryCombo.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_NODEQUERYCOMBO_HPP_ +#define VIEWER_SRC_NODEQUERYCOMBO_HPP_ + +#include + +class NodeQueryCombo : public QComboBox +{ +Q_OBJECT + +public: + explicit NodeQueryCombo(QWidget* parent=0); + +protected Q_SLOTS: + void slotCurrentChanged(int current); + +Q_SIGNALS: + void changed(QString); +}; + +#endif /* VIEWER_SRC_NODEQUERYCOMBO_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuery.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuery.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuery.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuery.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,496 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQuery.hpp" + +#include "NodeQueryOption.hpp" +#include "UiLog.hpp" +#include "VAttributeType.hpp" +#include "VSettings.hpp" + +int NodeQuery::defaultMaxNum_=50000; + +#define _UI_NODEQUERY_DEBUG + +QString NodeQueryAttrGroup::query() const +{ + QStringList lst; + Q_FOREACH(NodeQueryOption* op,options_) + { + QString s=op->query().simplified(); + if(!s.isEmpty()) + lst << op->query(); + } + + if(lst.count() == 0) + return name_; + else if(lst.count() == 1) + { + return lst[0]; + } + else + { + return "(" + lst.join(" and ") + ")"; + } + + return QString(); +} + +bool NodeQueryVarAttrGroup::hasType(VAttributeType* t) const +{ + if(types_.contains(t)) + { + Q_FOREACH(NodeQueryOption* op,options_) + { + if(op->name() == "var_type") + { + QString v=op->valueAsString(); +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << "NodeQueryVarAttrGroup::hasType var_type=" << v.toStdString(); +#endif + if(v == "any") + return true; + else + return (v == t->name()); + } + } + + Q_ASSERT(false); + } + + return false; +} + +NodeQuery::NodeQuery(const std::string& name,bool ignoreMaxNum) : + name_(name), + advanced_(false), + caseSensitive_(false), + maxNum_(defaultMaxNum_), + ignoreMaxNum_(ignoreMaxNum) +{ +#ifdef _UI_NODEQUERY_DEBUG + UI_FUNCTION_LOG +#endif + + //Build options from defs. Options store the actual query settings. They are + //editable through the gui. + NodeQueryOption::build(this); +} + +NodeQuery::~NodeQuery() +{ + qDeleteAll(options_); +} + +NodeQuery* NodeQuery::clone() +{ + return clone(name_); +} + +NodeQuery* NodeQuery::clone(const std::string& name) +{ + NodeQuery *q=new NodeQuery(name); + q->swap(this); + + return q; +} + +void NodeQuery::swap(const NodeQuery* q) +{ + advanced_=q->advanced_; + //query_=q->query_; + extQuery_=q->extQuery_; + rootNode_=q->rootNode_; + servers_=q->servers_; + //allServers_=q->allServers_; + caseSensitive_=q->caseSensitive_; + maxNum_=q->maxNum_; + ignoreMaxNum_=q->ignoreMaxNum_; + + for(QMap::const_iterator it = options_.constBegin(); it != options_.constEnd(); ++it) + it.value()->swap(q->option(it.key())); + + buildQueryString(); +} + +void NodeQuery::setName(const std::string& name) +{ + name_=name; +} + +bool NodeQuery::hasServer(const std::string& name) const +{ +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << "NodeQuery::hasServer -->"; +#endif + + if(servers_.empty()) + return true; + + return servers_.contains(QString::fromStdString(name)); +} + +NodeQueryOption* NodeQuery::option(QString name) const +{ + QMap::const_iterator it = options_.find(name); + if(it != options_.constEnd()) + return it.value(); + return NULL; +} + +QString NodeQuery::query() const +{ + QString s1=nodeQueryPart(); + QString s2=attrQueryPart(); + if(!s1.isEmpty()) + if(!s2.isEmpty()) + return s1+ " and " + s2; + else + return s1; + else + return s2; + + return QString(); +} + + + +QString NodeQuery::nodeQueryPart() const +{ + QStringList lst; + QStringList keys; + keys << "node" << "type" << "state" << "flag" << "status_change_time"; + Q_FOREACH(QString s,keys) + { + if(!extQuery_.value(s).isEmpty()) + lst << extQuery_.value(s); + } + + //TODO : handle all + return lst.join(" and "); +} + +bool NodeQuery::hasBasicNodeQueryPart() const +{ + QStringList keys; + keys << "node" << "type" << "state" << "flag"; + Q_FOREACH(QString s,keys) + { + if(!extQuery_.value(s).isEmpty()) + return true; + } + return false; +} + +bool NodeQuery::hasPeriodQueryPart() const +{ + return !extQuery_.value("status_change_time").isEmpty(); +} + +QString NodeQuery::attrQueryPart() const +{ + return extQuery_["attr"]; +} + +QString NodeQuery::attrQueryPart(VAttributeType* t) const +{ + //TODO + QStringList attrSel=attrSelection(); + QString q; + Q_FOREACH(NodeQueryAttrGroup* tm,attrGroup_.values()) + { + if(tm->hasType(t) && + attrSel.contains(tm->name())) + { + QStringList qLst; + + Q_FOREACH(NodeQueryOption* op,tm->options()) + { + Q_ASSERT(op); + QString s=op->query(); + if(!s.isEmpty()) + { + qLst << s; + } + } + + if(qLst.isEmpty()) + q=t->name(); + else + q=qLst.join(" and "); + + break; + } + } + return q; +} + +bool NodeQuery::hasAttribute(VAttributeType *t) const +{ + Q_FOREACH(NodeQueryAttrGroup* d,attrGroup_.values()) + { + if(d->hasType(t)) + { + return attrSelection().contains(d->name()); + } + } + return false; +} + +QStringList NodeQuery::attrSelection() const +{ + NodeQueryOption *a=options_["attribute"]; + Q_ASSERT(a); + NodeQueryListOption* op=static_cast(a); + Q_ASSERT(op); + return op->selection(); +} + +NodeQueryListOption* NodeQuery::stateOption() const +{ + NodeQueryOption* op=option("state"); + Q_ASSERT(op); + return op->isList(); +} + +void NodeQuery::buildQueryString() +{ +#ifdef _UI_NODEQUERY_DEBUG + UI_FUNCTION_LOG +#endif + + extQuery_.clear(); + + //Node + QStringList nodePart; + QString name=options_["node_name"]->query(); + QString path=options_["node_path"]->query(); + if(!name.isEmpty()) nodePart << name; + if(!path.isEmpty()) nodePart << path; + if(nodePart.count() > 0) + extQuery_["node"]="(" +nodePart.join(" and ") + ")"; + + //Type + QString typePart=options_["type"]->query(" or "); + if(!typePart.isEmpty()) + extQuery_["type"]="(" + typePart + ")"; + + //State + QString statePart=options_["state"]->query(" or "); + if(!statePart.isEmpty()) + extQuery_["state"]="(" + statePart + ")"; + + //Flag + QString flagPart=options_["flag"]->query(" or "); + if(!flagPart.isEmpty()) + extQuery_["flag"]="(" + flagPart + ")";; + + //Status change time + QString periodPart=options_["status_change_time"]->query(); + if(!periodPart.isEmpty()) + extQuery_["status_change_time"]="(" + periodPart + ")"; + + //Attributes + QString attrPart; + + Q_FOREACH(QString attrName,attrSelection()) + { + NodeQueryAttrGroup* grp=attrGroup_.value(attrName); + Q_ASSERT(grp); + QString grpPart=grp->query(); + + if(!attrPart.isEmpty()) + attrPart+=" or "; + + attrPart+=grpPart; + } + + if(!attrPart.isEmpty()) + //extQuery_["attr"]="(" + attrPart + ")"; + extQuery_["attr"]=attrPart; + + bool hasEq=QStringList(extQuery_.values()).join("").contains("="); + + //Scope + QString scopePart; + if(!servers_.isEmpty()) + scopePart="servers = \'" + servers_.join(", ") + "\'"; + + if(servers_.size() <= 1 && !rootNode_.empty()) + { + if(!scopePart.isEmpty()) + scopePart+=" and "; + + scopePart+="root_node = \'" + QString::fromStdString(rootNode_) + "\'"; + } + if(!scopePart.isEmpty()) + extQuery_["scope"]=scopePart; + + //Other options + QString opPart; + if(!ignoreMaxNum_) + { + opPart="max_results = " + QString::number(maxNum_); + } + + if(hasEq) + { + if(!opPart.isEmpty()) + opPart+=" and "; + if(caseSensitive_) + opPart+="case_sensitive"; + else + opPart+="case_insensitive"; + } + + if(!opPart.isEmpty()) + extQuery_["options"]=opPart; + + //SQL-like query + sqlQuery_.clear(); + QStringList selectPart; + QStringList fromPart; + QStringList wherePart; + sqlQuery_="SELECT"; + QStringList nodeParts; + nodeParts << "node" << "type" << "state" << "flag"; + Q_FOREACH(QString s,nodeParts) + { + QString vs=extQuery_.value(s); + if(!vs.isEmpty()) + { + vs.replace("<","<"); + vs.replace(">",">"); + wherePart << vs; + } + } + + QString sqlPeriodPart=options_["status_change_time"]->sqlQuery(); + if(!sqlPeriodPart.isEmpty()) + { + sqlPeriodPart="(" + sqlPeriodPart + ")"; + sqlPeriodPart.replace("<","<"); + sqlPeriodPart.replace(">",">"); + wherePart << sqlPeriodPart; + } + + selectPart << "node"; + + //Attribute + Q_FOREACH(QString attrName,attrSelection()) + { + NodeQueryAttrGroup* grp=attrGroup_.value(attrName); + Q_ASSERT(grp); + selectPart << grp->name(); + selectPart.removeOne("node"); + QString grpPart=grp->query(); + if(grpPart != grp->name()) + wherePart << grpPart; + } + + sqlQuery_+=" " + selectPart.join(", "); + + //FROM + if(!servers_.isEmpty()) + { + if(servers_.size() ==1 && !rootNode_.empty()) + { + fromPart << servers_[0] + ":/" + QString::fromStdString(rootNode_); + } + else + fromPart=servers_; + + sqlQuery_+=" FROM " + fromPart.join(", "); + } + else + { + //sqlQuery_+=" FROM *"; + } + + if(wherePart.count() > 0) + sqlQuery_+=" WHERE " + wherePart.join(" and "); + + if(!ignoreMaxNum_) + { + sqlQuery_+=" LIMIT " + QString::number(maxNum_); + } +} + +void NodeQuery::load(VSettings* vs) +{ + advanced_=vs->getAsBool("advanced",advanced_); + caseSensitive_=vs->getAsBool("case",caseSensitive_); + + int maxNum=vs->get("maxNum",maxNum_); + if(maxNum_ > 1 && maxNum < 5000000) + maxNum_=maxNum; + + std::vector v; + vs->get("servers",v); + servers_.clear(); + for(std::vector::const_iterator it=v.begin(); it != v.end(); ++it) + servers_ << QString::fromStdString(*it); + + //allServers_=vs->getAsBool("allServers",allServers_); + + rootNode_=vs->get("rootNode",rootNode_); + + Q_FOREACH(QString s,options_.keys()) + { + options_[s]->load(vs); + } +#if 0 + Q_FOREACH(QString s,selectOptions_.keys()) + { + selectOptions_[s]->load(vs); + } +#endif + buildQueryString(); + + //query_=QString::fromStdString(vs->get("query",query_.toStdString())); +} + + +void NodeQuery::save(VSettings* vs) +{ + if(advanced_) + vs->putAsBool("advanced",advanced_); + + if(caseSensitive_) + vs->putAsBool("case",caseSensitive_); + + if(maxNum_ != defaultMaxNum_) + vs->put("maxNum",maxNum_); + + std::vector v; + Q_FOREACH(QString s, servers_) + v.push_back(s.toStdString()); + + //if(!allServers_) + // vs->putAsBool("allServers",allServers_); + + if(!v.empty()) + vs->put("servers",v); + + if(!rootNode_.empty()) + vs->put("rootNode",rootNode_); + + Q_FOREACH(QString s,options_.keys()) + { + options_[s]->save(vs); + } +#if 0 + Q_FOREACH(QString s,selectOptions_.keys()) + { + selectOptions_[s]->save(vs); + } + #endif + //vs->put("query",query_.toStdString()); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,624 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryEditor.hpp" + +#include "ComboMulti.hpp" +#include "CustomListWidget.hpp" +#include "Highlighter.hpp" +#include "NodeQuery.hpp" +#include "NodeQueryHandler.hpp" +#include "NodeQueryOption.hpp" +#include "NodeQueryOptionEdit.hpp" +#include "ServerFilter.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" +#include "VNState.hpp" + +#include +#include +#include +#include +#include +#include + +//====================================================== +// +// NodeQuerySaveDialog +// +//====================================================== + +NodeQuerySaveDialog::NodeQuerySaveDialog(QWidget *parent) : QDialog(parent) +{ + setupUi(this); +} + +QString NodeQuerySaveDialog::name() const +{ + return nameLe_->text(); +} + +void NodeQuerySaveDialog::accept() +{ + QString name=nameLe_->text(); + + if(!name.contains(QRegExp("[\\w|\\s]+"))) + { + QMessageBox::critical(0,tr("Invalid character"), + "Query names can only contain alphanumeric characters, whitespaces and \"_\". Please choose a different name."); + return; + + } + + if(NodeQueryHandler::instance()->find(name.toStdString())) + { + QMessageBox::critical(0,tr("Duplicated"), + "The specified name is already used by another query. Please choose a different name."); + return; + } + + QDialog::accept(); +} + +//====================================================== +// +// NodeQueryEditor +// +//====================================================== + +NodeQueryEditor::NodeQueryEditor(QWidget *parent) : + QWidget(parent), + query_(NULL), + serverFilter_(NULL), + queryTeCanExpand_(false), + initIsOn_(false), + canBeRun_(false), + filterMode_(false) +{ + setupUi(this); + + query_=new NodeQuery("tmp"); + //attrPanel_->setQuery(query_); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + rootLe_->setClearButtonEnabled(true); +#endif + + Q_ASSERT(tab_->count() == 2); + nodeTabText_=tab_->tabText(0); + attrTabText_=tab_->tabText(1); + + //------------------------- + // Query display + //------------------------- + + + //QFont f("Courier"); + /*QFont f("Monospace"); + f.setStyleHint(QFont::TypeWriter); + f.setFixedPitch(true); + f.setPointSize(10); + f.setStyleStrategy(QFont::PreferAntialias); + queryTe_->setFont(f);*/ + + QFont f; + QFontMetrics fm(f); + + queryTe_->setFixedHeight((fm.height()+2)*3+6); + queryTe_->setReadOnly(true); + queryTe_->setWordWrapMode(QTextOption::WordWrap); + + //The document becomes the owner of the highlighter + new Highlighter(queryTe_->document(),"query"); + + //------------------ + // Options + //------------------ + + //Max item num + numSpin_->setRange(10,250000); + numSpin_->setValue(query_->maxNum()); + numSpin_->setToolTip(tr("The maximum possible value is: ") + QString::number(numSpin_->maximum())); + + connect(numSpin_,SIGNAL(valueChanged(int)), + this,SLOT(slotMaxNum(int))); + + caseCb_->setChecked(query_->caseSensitive()); + + connect(caseCb_,SIGNAL(clicked(bool)), + this,SLOT(slotCase(bool))); + + //------------------------- + // Scope + //------------------------- + + //Servers + serverCb_->setMode(ComboMulti::FilterMode); + + serverResetTb_->setEnabled(serverCb_->hasSelection()); + + connect(serverCb_,SIGNAL(selectionChanged()), + this,SLOT(slotServerCbChanged())); + + connect(serverResetTb_,SIGNAL(clicked()), + serverCb_,SLOT(clearSelection())); + + //Root + connect(rootLe_,SIGNAL(textChanged(QString)), + this,SLOT(slotRootNodeEdited(QString))); + + //nodePathGrid_ + + //Name + path + nameEdit_=new NodeQueryStringOptionEdit(query_->option("node_name"),nodeGrid_,this,false); + pathEdit_=new NodeQueryStringOptionEdit(query_->option("node_path"),nodeGrid_,this,false); + + //Status change time + periodEdit_=new NodeQueryPeriodOptionEdit(query_->option("status_change_time"),nodeGrid_,this); + + //------------------------- + // Filter + //------------------------- + + //Node type + typeEdit_=new NodeQueryListOptionEdit(query_->option("type"),typeList_,typeResetTb_,this); + stateEdit_=new NodeQueryListOptionEdit(query_->option("state"),stateList_,stateResetTb_,this); + flagEdit_=new NodeQueryListOptionEdit(query_->option("flag"),flagList_,flagResetTb_,this); + + attrEdit_=new NodeQueryListOptionEdit(query_->option("attribute"),attrList_,attrResetTb_,this); + + int listHeight=(fm.height()+2)*6+6; + typeList_->setFixedHeight(listHeight); + stateList_->setFixedHeight(listHeight); + flagList_->setFixedHeight(listHeight); + + listHeight=(fm.height()+2)*10+6; + int listWidth=fm.width("variable")+60; + attrList_->setFixedHeight(listHeight); + attrList_->setFixedWidth(listWidth); + + //Attr panel + //connect(attrPanel_,SIGNAL(queryChanged()), + // this,SLOT(slotAttrPanelChanged())); + + + //Attributes + attrPanel_->setProperty("attrArea","1"); + Q_FOREACH(NodeQueryAttrGroup* aGrp,query_->attrGroup().values()) + { + Q_ASSERT(aGrp); + QString grName=aGrp->name(); + + Q_FOREACH(NodeQueryOption* op,aGrp->options()) + { + NodeQueryOptionEdit *e=0; + //TODO: use factory here + if(op->type() == "string") + e=new NodeQueryStringOptionEdit(op,attrGrid_,this,false); + else if(op->type() == "combo") + e=new NodeQueryComboOptionEdit(op,attrGrid_,this); + + Q_ASSERT(e); + attr_[grName] << e; + e->setVisible(false); + } + } + + attrPanelLayout_->addStretch(1); + //attrPanel_->hide(); + + //-------------------------------- + // Select tab + //-------------------------------- + tab_->setCurrentIndex(0); + + //-------------------------------- + // Query management + //-------------------------------- + + connect(saveAsTb_,SIGNAL(clicked()), + this,SLOT(slotSaveQueryAs())); + + connect(advModeTb_,SIGNAL(clicked(bool)), + this,SLOT(slotAdvMode(bool))); + + queryManageW_->hide(); + + /*advModeTb_->hide(); + queryCbLabel_->hide(); + queryCb_->hide(); + saveAsTb_->hide(); + saveTb_->hide();*/ + + //attrList_->hide(); + //attrLabel_->hide(); + //attrResetTb_->hide(); + + init(); + + checkGuiState(); +} + +NodeQueryEditor::~NodeQueryEditor() +{ + delete query_; + + if(serverFilter_) + serverFilter_->removeObserver(this); +} + +void NodeQueryEditor::setFilterMode(bool b) +{ + if(filterMode_ != b) + { + filterMode_=b; + Q_ASSERT(tab_->count() == 2); + tab_->setTabEnabled(1,!filterMode_); + } +} + +void NodeQueryEditor::init() +{ + initIsOn_=true; + + numSpin_->setValue(query_->maxNum()); + caseCb_->setChecked(query_->caseSensitive()); + + numSpin_->setEnabled(!query_->ignoreMaxNum()); + numLabel_->setEnabled(!query_->ignoreMaxNum()); + + //Servers + QStringList servers=query_->servers(); + if(servers == serverCb_->all()) + serverCb_->clearSelection(); + else + serverCb_->setSelection(servers); + + rootLe_->setText(QString::fromStdString(query_->rootNode())); + + //Node name + nameEdit_->init(query_); + pathEdit_->init(query_); + + //Lists + typeEdit_->init(query_); + stateEdit_->init(query_); + flagEdit_->init(query_); + attrEdit_->init(query_); + + //period + periodEdit_->init(query_); + + //attrPanel_->init(); + + initIsOn_=false; + + updateQueryTe(); + checkGuiState(); + + //switch to the first + tab_->setCurrentIndex(0); +} + +void NodeQueryEditor::showDefPanel(bool b) +{ + scopeBox_->setVisible(b); + optionBox_->setVisible(b); + tab_->setVisible(b); +} + +bool NodeQueryEditor::isDefPanelVisible() const +{ + return scopeBox_->isVisible(); +} + +void NodeQueryEditor::showQueryPanel(bool b) +{ + queryLabel_->setVisible(b); + queryTe_->setVisible(b); +} + +bool NodeQueryEditor::isQueryPanelVisible() const +{ + return queryTe_->isVisible(); +} + +void NodeQueryEditor::slotAdvMode(bool b) +{ +#if 0 + // filterBox_->setVisible(!b); + queryTe_->setReadOnly(!b); +#endif +} + +void NodeQueryEditor::slotMaxNum(int v) +{ + if(!initIsOn_) + { + query_->setMaxNum(v); + updateQueryTe(); + } +} + +void NodeQueryEditor::slotCase(bool b) +{ + if(!initIsOn_) + { + query_->setCaseSensitive(b); + updateQueryTe(); + } +} + +void NodeQueryEditor::slotServerCbChanged() +{ + serverResetTb_->setEnabled(serverCb_->hasSelection()); + + if(!initIsOn_) + { + query_->setServers(serverCb_->selection()); + updateQueryTe(); + checkGuiState(); + } +} + +void NodeQueryEditor::slotRootNodeEdited(QString s) +{ + if(!initIsOn_) + { + query_->setRootNode(rootLe_->text().simplified().toStdString()); + updateQueryTe(); + checkGuiState(); + } +} + +void NodeQueryEditor::slotOptionEditChanged() +{ + if(!initIsOn_) + { + NodeQueryOptionEdit *e=static_cast(sender()); + Q_ASSERT(e); + if(e->optionId() == "attribute") + setAttributePanel(attrList_->selection()); + + updateQueryTe(); + checkGuiState(); + } +} + +void NodeQueryEditor::setAttributePanel(QStringList lst) +{ + QMapIterator > it(attr_); + while (it.hasNext()) + { + it.next(); + bool st=lst.contains(it.key()); + Q_FOREACH(NodeQueryOptionEdit* e,it.value()) + { + e->setVisible(st); + } + } +#if 0 + if(lst.isEmpty()) + attrPanel_->hide(); + else + attrPanel_->show(); +#endif +} + + +#if 0 +void NodeQueryEditor::slotAttrListChanged() +{ + if(!initIsOn_) + { + attrResetTb_->setEnabled(attrList_->hasSelection()); + attrPanel_->setSelection(attrList_->selection()); + query_->setAttrGroupSelection(attrList_->selection()); + updateQueryTe(); + checkGuiState(); + } +} +#endif + + +void NodeQueryEditor::slotAttrPanelChanged() +{ + if(!initIsOn_) + { + updateQueryTe(); + checkGuiState(); + } +} + +void NodeQueryEditor::checkGuiState() +{ + serverResetTb_->setEnabled(serverCb_->hasSelection()); + + bool oneServer=(serverCb_->count() == 1 || serverCb_->selection().count() == 1); + + rootLabel_->setEnabled(oneServer); + rootLe_->setEnabled(oneServer); + + QString t=nodeTabText_; + if(query_->hasBasicNodeQueryPart()) + t+="*"; + tab_->setTabText(0,t); + + if(!filterMode_) + { + t=attrTabText_; + if(!query_->attrQueryPart().isEmpty()) + t+="*"; + tab_->setTabText(1,t); + } +} + +void NodeQueryEditor::updateQueryTe() +{ + query_->buildQueryString(); + + QColor bg(241,241,241); + queryTe_->setHtml(query_->sqlQuery()); +} + +void NodeQueryEditor::slotClear() +{ + nameEdit_->clear(); + pathEdit_->clear(); + typeEdit_->clear(); + stateEdit_->clear(); + flagEdit_->clear(); + periodEdit_->clear(); + attrEdit_->clear(); +} + +//------------------------------------------ +// Servers +//------------------------------------------ + +QStringList NodeQueryEditor::allServers() const +{ + return serverCb_->all(); +} + +void NodeQueryEditor::updateServers() +{ + serverCb_->clear(); + + if(serverFilter_) + { + for(std::vector::const_iterator it=serverFilter_->items().begin(); it != serverFilter_->items().end(); ++it) + { + serverCb_->addItem(QString::fromStdString((*it)->name())); + } + } + + //Init + if(serverCb_->count() == 1) + { + //serverCb_->selectSoleItem(); + } + else + { + //slotServerCbChanged(); + } + + slotServerCbChanged(); + +} + +void NodeQueryEditor::setServerFilter(ServerFilter* sf) +{ + if(serverFilter_ == sf) + return; + + if(serverFilter_) + { + serverFilter_->removeObserver(this); + } + + serverFilter_=sf; + + if(serverFilter_) + { + serverFilter_->addObserver(this); + } + + updateServers(); +} + +void NodeQueryEditor::notifyServerFilterAdded(ServerItem* item) +{ + /*ServerHandler* s=item->serverHandler(); + s->addServerObserver(this); + updateTitle();*/ +} + +void NodeQueryEditor::notifyServerFilterRemoved(ServerItem* item) +{ + /*ServerHandler* s=item->serverHandler(); + s->removeServerObserver(this); + updateTitle();*/ +} + +void NodeQueryEditor::notifyServerFilterChanged(ServerItem*) +{ + //updateTitle(); +} + +void NodeQueryEditor::notifyServerFilterDelete() +{ + serverFilter_->removeObserver(this); + serverFilter_=0; +} + +//------------------------------------------ +// Root node +//------------------------------------------ + +void NodeQueryEditor::setRootNode(VInfo_ptr info) +{ + if(info && info.get()) + { + if(ServerHandler *server=info->server()) + { + QStringList sel(QString::fromStdString(server->name())); + serverCb_->setSelection(sel); + + if(info->isNode()) + { + if(VNode *node=info->node()) + { + rootLe_->setText(QString::fromStdString(node->absNodePath())); + } + } + else + { + rootLe_->clear(); + } + } + } +} + +//------------------------------------------ +// Query +//------------------------------------------ + +int NodeQueryEditor::maxNum() const +{ + return numSpin_->value(); +} + +NodeQuery* NodeQueryEditor::query() const +{ + return query_; +} + +//------------------------------------------ +// Query management +//------------------------------------------ + +void NodeQueryEditor::setQuery(NodeQuery* q) +{ + query_->swap(q); + init(); +} + + +void NodeQueryEditor::slotSaveQueryAs() +{ + NodeQuerySaveDialog d(this); + if(d.exec() == QDialog::Accepted) + { + std::string name=d.name().toStdString(); + NodeQuery* q=query_->clone(name); + NodeQueryHandler::instance()->add(q,true); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,111 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef NODEQUERYEDITOR_HPP_ +#define NODEQUERYEDITOR_HPP_ + +#include "ui_NodeQueryEditor.h" +#include "ui_NodeQuerySaveDialog.h" + +#include +#include +#include + +#include "ServerFilter.hpp" +#include "VInfo.hpp" + +class NodeQuery; +class NodeQueryDef; +class NodeQueryListModel; +class NodeQueryOptionEdit; + +class NodeQuerySaveDialog : public QDialog, protected Ui::NodeQuerySaveDialog +{ +Q_OBJECT + +public: + explicit NodeQuerySaveDialog(QWidget *parent = 0); + ~NodeQuerySaveDialog() {} + QString name() const; + +public Q_SLOTS: + void accept(); +}; + +class NodeQueryEditor : public QWidget, protected Ui::NodeQueryEditor, public ServerFilterObserver +{ + Q_OBJECT + +public: + explicit NodeQueryEditor(QWidget *parent = 0); + ~NodeQueryEditor(); + + void setServerFilter(ServerFilter*); + void setRootNode(VInfo_ptr); + void setQuery(NodeQuery*); + NodeQuery* query() const; + void setQueryTeCanExpand(bool); + bool isDefPanelVisible() const; + void showDefPanel(bool); + bool isQueryPanelVisible() const; + void showQueryPanel(bool); + int maxNum() const; + QStringList allServers() const; + void setFilterMode(bool); + + void notifyServerFilterAdded(ServerItem*); + void notifyServerFilterRemoved(ServerItem*); + void notifyServerFilterChanged(ServerItem*); + void notifyServerFilterDelete(); + +public Q_SLOTS: + void slotClear(); + +protected Q_SLOTS: + void slotOptionEditChanged(); + void slotServerCbChanged(); + void slotRootNodeEdited(QString); + void slotAttrPanelChanged(); + void slotSaveQueryAs(); + void slotAdvMode(bool b); + void slotMaxNum(int); + void slotCase(bool); + +Q_SIGNALS: + void queryEnabledChanged(bool); + +private: + void updateServers(); + void init(); + void initAttr(); + void updateQueryTe(); + void checkGuiState(); + void setAttributePanel(QStringList lst); + + NodeQuery* query_; + ServerFilter* serverFilter_; + bool queryTeCanExpand_; + bool initIsOn_; + bool canBeRun_; + bool filterMode_; + + NodeQueryOptionEdit* nameEdit_; + NodeQueryOptionEdit* pathEdit_; + NodeQueryOptionEdit* typeEdit_; + NodeQueryOptionEdit* stateEdit_; + NodeQueryOptionEdit* flagEdit_; + NodeQueryOptionEdit* periodEdit_; + NodeQueryOptionEdit* attrEdit_; + QMap > attr_; + QString nodeTabText_; + QString attrTabText_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.ui ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEditor.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEditor.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,401 @@ + + + NodeQueryEditor + + + + 0 + 0 + 1564 + 1113 + + + + + 0 + 0 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Queries: + + + + + + + + + + Save query as ... + + + + + + + Save query + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Advanced mode + + + true + + + + + + + + + + + + Scope + + + + + + + + + Clear server filter + + + ... + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + true + + + + + + + Search root node: + + + + + + + Search in servers: + + + + + + + + + + + + + Global options + + + + + + + + Max results: + + + + + + + + 0 + 0 + + + + + + + + + + Case sensitive + + + + + + + + + + + + 1 + + + + Nodes + + + + + + + + + + + Flag + + + + + + + Type + + + + + + + Clear status filter + + + ... + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + true + + + + + + + Clear type filter + + + ... + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + true + + + + + + + Status + + + + + + + Clear flag filter + + + ... + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + true + + + + + + + + + + + + + + + + + + + Attributes + + + + + + + + Types + + + + + + + .a.. + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + true + + + + + + + + + + 0 + 0 + + + + + + + + true + + + + + 0 + 0 + 1281 + 646 + + + + + + + + + + + + + + Attribute-specific options + + + + + + + + + + + 4 + + + + + Query + + + + + + + + 0 + 0 + + + + This is the query presented in a MySQL-like syntax. + + + true + + + + + + + + + + ComboMulti + QComboBox +
      ComboMulti.hpp
      +
      + + CustomListWidget + QListWidget +
      CustomListWidget.hpp
      +
      + + NodeQueryCombo + QComboBox +
      NodeQueryCombo.hpp
      +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEngine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEngine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEngine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEngine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,422 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryEngine.hpp" + +#include +#include + +#include "NodeExpression.hpp" +#include "NodeQuery.hpp" +#include "ServerDefsAccess.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "VAttributeType.hpp" +#include "VFilter.hpp" +#include "VNode.hpp" + +static bool metaRegistered=false; + +NodeQueryEngine::NodeQueryEngine(QObject* parent) : + QThread(parent), + query_(new NodeQuery("tmp")), + parser_(NULL), + cnt_(0), + scanCnt_(0), + maxNum_(250000), + chunkSize_(100), + stopIt_(false), + maxReached_(false), + rootNode_(0) +{ + //We will need to pass various non-Qt types via signals and slots + //So we need to register these types. + if(!metaRegistered) + { + qRegisterMetaType("NodeQueryResultTmp_ptr"); + qRegisterMetaType >("QList"); + metaRegistered=true; + } + + connect(this,SIGNAL(finished()), + this,SLOT(slotFinished())); +} + +NodeQueryEngine::~NodeQueryEngine() +{ + delete query_; + + if(parser_) + delete parser_; +} + +bool NodeQueryEngine::runQuery(NodeQuery* query,QStringList allServers) +{ + UiLog().dbg() << "NodeQueryEngine::runQuery -->"; + + if(isRunning()) + wait(); + + stopIt_=false; + maxReached_=false; + res_.clear(); + cnt_=0; + scanCnt_=0; + rootNode_=0; + + query_->swap(query); + + maxNum_=query_->maxNum(); + + servers_.clear(); + + //Init the parsers + if(parser_) + { + delete parser_; + parser_=NULL; + } + + qDeleteAll(attrParser_); + attrParser_.clear(); + + //The nodequery parser + UiLog().dbg() << " node part: " << query_->nodeQueryPart().toStdString(); + + parser_=NodeExpressionParser::instance()->parseWholeExpression(query_->nodeQueryPart().toStdString(), query->caseSensitive()); + if(parser_ == NULL) + { + UiLog().err() << " unable to parse node query: " << query_->nodeQueryPart().toStdString(); + UserMessage::message(UserMessage::ERROR,true,"Error, unable to parse node query: " + query_->nodeQueryPart().toStdString()); + return false; + } + + QStringList serverNames=query_->servers(); + if(query_->servers().isEmpty()) + serverNames=allServers; + + Q_FOREACH(QString s,serverNames) + { + if(ServerHandler* server=ServerHandler::find(s.toStdString())) + { + servers_.push_back(server); + } + } + + if(!query_->rootNode().empty()) + { + if(servers_.size() != 1) + return false; + + rootNode_=servers_.at(0)->vRoot()->find(query_->rootNode()); + if(!rootNode_) + { + UiLog().err() << " the specified root node does not exist: " << query_->rootNode(); + UserMessage::message(UserMessage::ERROR,true, + "Error, the specified root node does not exist: " + query_->rootNode()); + return false; + } + } + + //The attribute parser + UiLog().dbg() << " full attr part: " << query_->attrQueryPart().toStdString(); + + for(std::vector::const_iterator it=VAttributeType::types().begin(); + it != VAttributeType::types().end(); ++it) + { + if(query_->hasAttribute(*it)) + { + QString attrPart=(query_->attrQueryPart(*it)); + UiLog().dbg() << " " << (*it)->strName() << ": " << attrPart.toStdString(); + BaseNodeCondition* ac=NodeExpressionParser::instance()->parseWholeExpression(attrPart.toStdString(), query->caseSensitive()); + if(!ac) + { + UiLog().err() << " unable to parse attribute query: " << attrPart.toStdString(); + UserMessage::message(UserMessage::ERROR,true, "Error, unable to parse attribute query: " + attrPart.toStdString()); + return false; + } + attrParser_[*it]=ac; + } + } + + //Notify the servers that the search began + Q_FOREACH(ServerHandler* s,servers_) + { + s->searchBegan(); + } + + //Start thread execution + start(); + + UiLog().dbg() << "<-- runQuery"; + + return true; +} + +void NodeQueryEngine::stopQuery() +{ + stopIt_=true; + wait(); +} + +void NodeQueryEngine::run() +{ + if(rootNode_) + { + run(servers_.at(0),rootNode_); + } + else + { + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + { + ServerHandler *server=*it; + + run(server,server->vRoot()); + + if(stopIt_) + { + broadcastChunk(true); + return; + } + } + } + + broadcastChunk(true); +} + +void NodeQueryEngine::run(ServerHandler *server,VNode* root) +{ + runRecursively(root); +} + + +void NodeQueryEngine::runRecursively(VNode *node) +{ + if(stopIt_) + return; + + //Execute the node part + if(parser_->execute(node)) + { + //Then execute the attribute part + if(!attrParser_.isEmpty()) + { + QMap::const_iterator it = attrParser_.constBegin(); + while (it != attrParser_.constEnd()) + { + //Process a given attribute type + const std::vector& av=node->attr(); + bool hasType=false; + for(size_t i=0; i < av.size(); i++) + { + if(av[i]->type() == it.key()) + { + hasType=true; + if(it.value()->execute(av[i])) + { + broadcastFind(node,av[i]->data(true)); + scanCnt_++; + } + } + //The the attribute vector elements are grouped by type. + //So we leave the loop when we reach the next type group + else if(hasType) + { + break; + } + } + ++it; + } + + + } + else + { + broadcastFind(node); + scanCnt_++; + } + } + + for(int i=0; i < node->numOfChildren(); i++) + { + runRecursively(node->childAt(i)); + if(stopIt_) + return; + } +} + +void NodeQueryEngine::broadcastFind(VNode* node,QStringList attr) +{ + Q_ASSERT(node); + + if(!attr.isEmpty()) + { + NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node,attr)); + res_ << d; + } + else + { + NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node)); + res_ << d; + } + + broadcastChunk(false); + + cnt_++; + + if(cnt_ >= maxNum_) + { + broadcastChunk(true); + stopIt_=true; + maxReached_=true; + } +} + +void NodeQueryEngine::broadcastFind(VNode* node) +{ + Q_ASSERT(node); + + NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node)); + res_ << d; + + broadcastChunk(false); + + cnt_++; + + if(cnt_ >= maxNum_) + { + broadcastChunk(true); + stopIt_=true; + maxReached_=true; + } +} + + +void NodeQueryEngine::broadcastChunk(bool force) +{ + bool doIt=false; + if(!force) + { + if(res_.count() >= chunkSize_) + { + doIt=true; + } + } + else if(!res_.isEmpty()) + { + doIt=true; + } + + if(doIt) + { + Q_EMIT found(res_); + res_.clear(); + } +} + +void NodeQueryEngine::slotFinished() +{ + //Notify the servers that the search finished + Q_FOREACH(ServerHandler* s,servers_) + { + s->searchFinished(); + } +} + +void NodeQueryEngine::slotFailed() +{ + +} + +NodeFilterEngine::NodeFilterEngine(NodeFilter* owner) : + query_(new NodeQuery("tmp")), + parser_(NULL), + server_(NULL), + owner_(owner), + rootNode_(0) +{ +} + +NodeFilterEngine::~NodeFilterEngine() +{ + delete query_; + + if(parser_) + delete parser_; +} + +void NodeFilterEngine::setQuery(NodeQuery* query) +{ + query_->swap(query); + + if(parser_) + delete parser_; + + parser_=NodeExpressionParser::instance()->parseWholeExpression(query_->query().toStdString()); + if(parser_ == NULL) + { + UiLog().err() << "Error, unable to parse enabled condition: " << query_->query().toStdString(); + UserMessage::message(UserMessage::ERROR, true,"Error, unable to parse enabled condition: " + query_->query().toStdString()); + } +} + + +bool NodeFilterEngine::runQuery(ServerHandler* server) +{ + rootNode_=0; + + if(!query_) + return false; + + server_=server; + if(!server_) + return false; + + if(!parser_) + return false; + + if(!query_->rootNode().empty() && + (query_->servers().count() == 1 && !query_->servers()[0].simplified().isEmpty())) + { + rootNode_=server_->vRoot()->find(query_->rootNode()); + if(!rootNode_) + { + UiLog().err() << " the specified root node does not exist: " << query_->rootNode(); +#if 0 + UserMessage::message(UserMessage::ERROR,true, + "Node filter failed! The specified root node does not exist: " + query_->rootNode() + + "
      Please redefine your filter!"); +#endif + return false; + } + } + + if(rootNode_) + runRecursively(rootNode_); + else + runRecursively(server_->vRoot()); + + return true; +} + +void NodeFilterEngine::runRecursively(VNode *node) +{ + if(!node->isServer() && + (node == owner_->forceShowNode() || parser_->execute(node))) + { + owner_->match_.push_back(node); + } + + for(int i=0; i < node->numOfChildren(); i++) + { + runRecursively(node->childAt(i)); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEngine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEngine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryEngine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryEngine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,99 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERYENGINE_HPP_ +#define VIEWER_SRC_NODEQUERYENGINE_HPP_ + +#include +#include + +#include +#include +#include + +#include "NodeQueryResultTmp.hpp" + +class BaseNodeCondition; +class ServerHandler; +class VNode; +class NodeFilter; +class NodeQuery; +class NodeQueryOptions; +class VAttribute; +class VAttributeType; + +class NodeQueryEngine : public QThread +{ + +Q_OBJECT + +public: + explicit NodeQueryEngine(QObject* parent=0); + ~NodeQueryEngine(); + + bool runQuery(NodeQuery* query,QStringList allServers); + void stopQuery(); + int scannedCount() const {return scanCnt_;} + bool wasStopped() const {return stopIt_;} + bool wasMaxReached() const {return maxReached_;} + +protected Q_SLOTS: + void slotFinished(); + void slotFailed(); + +Q_SIGNALS: + void found(NodeQueryResultTmp_ptr); + void found(QList); + +protected: + void run(); + +private: + void run(ServerHandler*,VNode*); + void runRecursively(VNode *node); + void broadcastFind(VNode*); + void broadcastFind(VNode*,QStringList); + void broadcastChunk(bool); + + NodeQuery* query_; + BaseNodeCondition* parser_; + QMap attrParser_; + std::vector servers_; + int cnt_; + int scanCnt_; + int maxNum_; + int chunkSize_; + QList res_; + bool stopIt_; + bool maxReached_; + VNode* rootNode_; +}; + +class NodeFilterEngine +{ +public: + explicit NodeFilterEngine(NodeFilter*); + ~NodeFilterEngine(); + + bool runQuery(ServerHandler*); + void setQuery(NodeQuery*); + +private: + void runRecursively(VNode *node); + + NodeQuery* query_; + BaseNodeCondition* parser_; + ServerHandler* server_; + NodeFilter *owner_; + VNode* rootNode_; +}; + + +#endif /* VIEWER_SRC_NODEQUERYENGINE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,115 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include + +#include "NodeQueryHandler.hpp" + +#include "DirectoryHandler.hpp" +#include "File.hpp" +#include "NodeQuery.hpp" + +NodeQueryHandler* NodeQueryHandler::instance_=0; + +NodeQueryHandler::NodeQueryHandler() : suffix_("query") +{ +} + +NodeQueryHandler* NodeQueryHandler::instance() +{ + if(!instance_) + { + instance_=new NodeQueryHandler(); + } + + return instance_; +} + +NodeQuery* NodeQueryHandler::add(const std::string& name) +{ + NodeQuery *item=new NodeQuery(name); + items_.push_back(item); + return item; +} + +NodeQuery* NodeQueryHandler::add(const std::string& name,const std::string& query) +{ + NodeQuery *item=new NodeQuery(name); + items_.push_back(item); + return item; +} + +void NodeQueryHandler::add(NodeQuery* item,bool saveToFile) +{ + items_.push_back(item); + if(saveToFile) + save(item); +} + + +void NodeQueryHandler::remove(const std::string&) +{ +} + +void NodeQueryHandler::remove(NodeQuery*) +{ + +} + +NodeQuery* NodeQueryHandler::find(const std::string& name) const +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name) + return *it; + } + return NULL; +} + +void NodeQueryHandler::save() +{ +} + +void NodeQueryHandler::save(NodeQuery *item) +{ + std::string f=DirectoryHandler::concatenate(dirPath_,item->name() + "." + suffix_); + VSettings vs(f); + item->save(&vs); + vs.write(); + +} + +void NodeQueryHandler::init(const std::string& dirPath) +{ + dirPath_=dirPath; + DirectoryHandler::createDir(dirPath_); + + std::vector res; + std::string pattern=".*\\." + suffix_ + "$"; + DirectoryHandler::findFiles(dirPath_,pattern,res); + + for(std::vector::const_iterator it=res.begin(); it != res.end(); ++it) + { + std::string fName=DirectoryHandler::concatenate(dirPath_,*it); + VSettings vs(fName); + vs.read(); + + std::size_t pos=(*it).find("." + suffix_); + assert(pos != std::string::npos); + + std::string name=(*it).substr(0,pos); + NodeQuery* item=add(name); + item->load(&vs); + } +} + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,46 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_NODEQUERYHANDLER_HPP_ +#define VIEWER_SRC_NODEQUERYHANDLER_HPP_ + +#include +#include + +class NodeQuery; + +class NodeQueryHandler +{ +public: + NodeQueryHandler(); + + NodeQuery* add(const std::string& name); + NodeQuery* add(const std::string& name,const std::string& query); + void add(NodeQuery* item,bool save); + void remove(const std::string& name); + void remove(NodeQuery*); + NodeQuery* find(const std::string& name) const; + + void save(); + void save(NodeQuery*); + void init(const std::string& dirPath); + const std::vector& items() const {return items_;} + + static NodeQueryHandler* instance(); + +protected: + static NodeQueryHandler* instance_; + + std::string dirPath_; + const std::string suffix_; + std::vector items_; +}; + + +#endif /* VIEWER_SRC_NODEQUERYHANDLER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuery.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuery.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuery.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuery.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,122 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERY_HPP_ +#define VIEWER_SRC_NODEQUERY_HPP_ + +#include +#include + +#include +#include + +#include "StringMatchMode.hpp" +#include "VSettings.hpp" + +class NodeQuery; +class NodeQueryOption; +class NodeQueryListOption; +class VAttributeType; + +class NodeQueryAttrGroup +{ +//friend class NodeQuery; + +public: + NodeQueryAttrGroup(QString name,QList types,QList options) : + name_(name), types_(types), options_(options) {} + + QString name() const {return name_;} + virtual bool hasType(VAttributeType* t) const {return types_.contains(t);} + QList options() const {return options_;} + QString query() const; + +protected: + QString name_; + QList types_; + QList options_; +}; + +class NodeQueryVarAttrGroup : public NodeQueryAttrGroup +{ +public: + NodeQueryVarAttrGroup(QString name,QList types,QList options) : + NodeQueryAttrGroup(name,types,options) {} + + bool hasType(VAttributeType*) const; +}; + +class NodeQuery +{ +friend class NodeQueryOption; + +public: + NodeQuery(const std::string& name,bool ignoreMaxNum=false); + ~NodeQuery(); + NodeQuery* clone(); + NodeQuery* clone(const std::string& name); + + void swap(const NodeQuery*); + + void setName(const std::string& name); + const std::string& name() const {return name_;} + + QString query() const; + QString sqlQuery() const {return sqlQuery_;} + QString nodeQueryPart() const; + bool hasBasicNodeQueryPart() const; + bool hasPeriodQueryPart() const; + QString attrQueryPart() const; + QString attrQueryPart(VAttributeType*) const; + bool hasAttribute(VAttributeType*) const; + QStringList attrSelection() const; + NodeQueryListOption* stateOption() const; + + void setRootNode(const std::string& rootNode) {rootNode_=rootNode;} + const std::string& rootNode() const {return rootNode_;} + void setServers(QStringList servers) {servers_=servers;} + QStringList servers() const {return servers_;} + bool hasServer(const std::string& name) const; + + void buildQueryString(); + + int maxNum() const {return maxNum_;} + void setMaxNum(int m) {maxNum_=m;} + bool ignoreMaxNum() const {return ignoreMaxNum_;} + + void setCaseSensitive(bool b) {caseSensitive_=b;} + bool caseSensitive() const {return caseSensitive_;} + + NodeQueryOption* option(QString name) const; + QMap attrGroup() {return attrGroup_;} + + void load(VSettings*); + void save(VSettings*); + +protected: + void checkDir(); + + std::string name_; + bool advanced_; + std::string rootNode_; + QStringList servers_; + bool allServers_; + QMap extQuery_; + QString sqlQuery_; + bool caseSensitive_; + int maxNum_; + bool ignoreMaxNum_; + QMap options_; + QMap attrGroup_; + static bool defaultCaseSensitive_; + static int defaultMaxNum_; +}; + +#endif /* VIEWER_SRC_NODEQUERY_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOption.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOption.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOption.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOption.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,543 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryOption.hpp" + +#include +#include + +#include + +#include "NodeQuery.hpp" +#include "UiLog.hpp" +#include "VAttributeType.hpp" +#include "VConfig.hpp" +#include "VProperty.hpp" +#include "VSettings.hpp" + +StringMatchMode::Mode NodeQueryStringOption::defaultMatchMode_=StringMatchMode::ContainsMatch; +bool NodeQueryStringOption::defaultCaseSensitive_=false; + +#define _UI_NODEQUERY_DEBUG + +class NodeQueryOptionFactory; +static std::map* makers = 0; + +//========================================= +// Factory +//========================================= + +class NodeQueryOptionFactory +{ +public: + explicit NodeQueryOptionFactory(const std::string& t); + virtual ~NodeQueryOptionFactory() {} + + virtual NodeQueryOption* make(VProperty* p) = 0; + static NodeQueryOption* create(VProperty* p); + +private: + explicit NodeQueryOptionFactory(const NodeQueryOptionFactory&); + NodeQueryOptionFactory& operator=(const NodeQueryOptionFactory&); +}; + +template +class NodeQueryOptionMaker : public NodeQueryOptionFactory +{ + NodeQueryOption* make(VProperty* p) { return new T(p); } +public: + explicit NodeQueryOptionMaker(const std::string& t) : NodeQueryOptionFactory(t) {} +}; + +NodeQueryOptionFactory::NodeQueryOptionFactory(const std::string& type) +{ + if(makers == 0) + makers = new std::map; + + (*makers)[type] = this; +} + +NodeQueryOption* NodeQueryOptionFactory::create(VProperty *p) +{ + if(!p) + return 0; + + std::string type=p->param("type").toStdString(); + std::map::iterator j = makers->find(type); + if(j != makers->end()) + return (*j).second->make(p); + + return 0; +} + +//=============================================== +// +// NodeQueryOption +// +//=============================================== + +NodeQueryOption::NodeQueryOption(VProperty* p) : ignoreIfAny_(false) +{ + type_=p->param("type"); + name_=p->name(); + label_=p->param("label"); + if(label_.isEmpty()) + label_=name_; + + ignoreIfAny_=(p->param("ignoreIfAny") == "true")?true:false; +} + +void NodeQueryOption::build(NodeQuery* query) +{ +#ifdef _UI_NODEQUERY_DEBUG + UI_FUNCTION_LOG +#endif + + //Node query part + VProperty* prop=VConfig::instance()->find("node_query"); + Q_ASSERT(prop); +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << " load node_query"; +#endif + for(int i=0; i < prop->children().size(); i++) + { + VProperty *p=prop->children().at(i); + QString type=p->param("type"); +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << " name=" << p->strName() << " type=" << type.toStdString(); +#endif + NodeQueryOption* op=NodeQueryOptionFactory::create(p); + Q_ASSERT(op); + query->options_[op->name()]=op; + } + + //Attr query part + prop=VConfig::instance()->find("attribute_query"); + Q_ASSERT(prop); +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << " load attribute_query"; +#endif + for(int i=0; i < prop->children().size(); i++) + { + VProperty *p=prop->children().at(i); + if(p->name() == "attribute") + { + query->options_[p->name()]=NodeQueryOptionFactory::create(p); + } + else + { +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << " name=" << p->strName(); +#endif + //Q_ASSERT(def_["attribute"]->values().contains(p->name())); + + QList typeLst; + QList opLst; + + QString types=p->param("types"); + if(types.isEmpty()) + { + VAttributeType *t=VAttributeType::find(p->name().toStdString()); + Q_ASSERT(t); + typeLst << t; + } + else + { + Q_FOREACH(QString s,types.split("|")) + { + VAttributeType *t=VAttributeType::find(s.toStdString()); + Q_ASSERT(t); + typeLst << t; + } + } + + Q_ASSERT(!typeLst.empty()); + + Q_FOREACH(VProperty* ch,p->children()) + { +#ifdef _UI_NODEQUERY_DEBUG + UiLog().dbg() << " option: name=" << ch->strName() << " type=" << ch->param("type").toStdString(); +#endif + NodeQueryOption *op=NodeQueryOptionFactory::create(ch); + query->options_[ch->name()]=op; + opLst << op; + Q_ASSERT(op); + } + if(p->name() == "variable") + query->attrGroup_[p->name()] = new NodeQueryVarAttrGroup(p->name(),typeLst,opLst); + else + query->attrGroup_[p->name()] = new NodeQueryAttrGroup(p->name(),typeLst,opLst); + } + } +} + +//=============================================== +// +// NodeQueryStringOption +// +//=============================================== + +NodeQueryStringOption::NodeQueryStringOption(VProperty* p) : + NodeQueryOption(p), + matchMode_(defaultMatchMode_), + caseSensitive_(defaultCaseSensitive_) +{ +} + +QString NodeQueryStringOption::query() const +{ + QString s; + if(!value_.isEmpty()) + { + s= name_ + " " + matchOperator() + " \'" + value_ + "\'"; + } + return s; +} + +void NodeQueryStringOption::swap(const NodeQueryOption* option) +{ + const NodeQueryStringOption* op=static_cast(option); + Q_ASSERT(op); + + value_=op->value(); + matchMode_=op->matchMode(); + caseSensitive_=op->caseSensitive(); +} + +void NodeQueryStringOption::save(VSettings* vs) +{ + if(value_.isEmpty() && matchMode_.mode() == defaultMatchMode_ && + caseSensitive_ == defaultCaseSensitive_) + return; + + vs->beginGroup(name_.toStdString()); + vs->put("value",value_.toStdString()); + vs->put("matchMode",matchMode_.toInt()); + vs->putAsBool("caseSensistive",caseSensitive_); + vs->endGroup(); +} + +void NodeQueryStringOption::load(VSettings* vs) +{ + if(!vs->contains(name().toStdString())) + return; + + vs->beginGroup(name_.toStdString()); + value_=QString::fromStdString(vs->get("value",value_.toStdString())); + matchMode_.setMode(static_cast(vs->get("matchMode",matchMode_.toInt()))); + caseSensitive_=vs->getAsBool("caseSensistive",caseSensitive_); + vs->endGroup(); +} + +//=============================================== +// +// NodeQueryListOption +// +//=============================================== + +NodeQueryListOption::NodeQueryListOption(VProperty* p) : + NodeQueryOption(p) +{ + values_=p->param("values").split("|"); + valueLabels_=p->param("labels").split("|"); + if(valueLabels_.count() == 1 && valueLabels_[0].isEmpty()) + valueLabels_=values_; + + Q_ASSERT(valueLabels_.count() == values_.count()); +} + +QString NodeQueryListOption::query(QString op) const +{ + QString s; + if(!selection_.isEmpty()) + { + s=selection_.join(op); + } + return s; +} + +void NodeQueryListOption::swap(const NodeQueryOption* option) +{ + const NodeQueryListOption* op=static_cast(option); + Q_ASSERT(op); + selection_=op->selection(); +} + +void NodeQueryListOption::save(VSettings* vs) +{ + if(selection_.isEmpty()) + return; + + std::vector v; + Q_FOREACH(QString s, selection_) + v.push_back(s.toStdString()); + + vs->put(name_.toStdString(),v); +} + +void NodeQueryListOption::load(VSettings* vs) +{ + if(!vs->contains(name_.toStdString())) + return; + + std::vector v; + vs->get(name_.toStdString(),v); + + selection_.clear(); + for(std::vector::const_iterator it=v.begin(); it != v.end(); ++it) + selection_ << QString::fromStdString(*it); +} + +//=============================================== +// +// NodeQueryComboOption +// +//=============================================== + +NodeQueryComboOption::NodeQueryComboOption(VProperty* p) : + NodeQueryOption(p) +{ + values_=p->param("values").split("|"); + valueLabels_=p->param("labels").split("|"); + if(valueLabels_.count() == 1 && valueLabels_[0].isEmpty()) + valueLabels_=values_; + + Q_ASSERT(values_.count() >0); + Q_ASSERT(valueLabels_.count() == values_.count()); + + value_=p->defaultValue().toString(); + if(!values_.contains(value_)) + value_=values_[0]; +} + +QString NodeQueryComboOption::query() const +{ + QString s; + if(ignoreIfAny_ && value_ == "any") + return s; + + if(!value_.isEmpty()) + { + s= name_ + " = " + " \'" + value_ + "\'"; + } + return s; +} + +void NodeQueryComboOption::setValue(QString val) +{ + value_=val; +} + +void NodeQueryComboOption::swap(const NodeQueryOption* option) +{ + const NodeQueryComboOption* op=static_cast(option); + Q_ASSERT(op); + value_=op->value(); +} + +void NodeQueryComboOption::save(VSettings* vs) +{ + vs->put(name_.toStdString(),value().toStdString()); +} + +void NodeQueryComboOption::load(VSettings* vs) +{ + +} + +//=============================================== +// +// NodeQueryPeriodOption +// +//=============================================== + +NodeQueryPeriodOption::NodeQueryPeriodOption(VProperty* p) : + NodeQueryOption(p), + mode_(NoMode), + lastPeriod_(-1) +{ +} + +void NodeQueryPeriodOption::clear() +{ + mode_=NoMode; + lastPeriod_=-1; + lastPeriodUnits_.clear(); + fromDate_=QDateTime(); + toDate_=QDateTime(); +} + +void NodeQueryPeriodOption::setLastPeriod(int interval,QString intervalUnits) +{ + mode_=LastPeriodMode; + lastPeriod_=interval; + lastPeriodUnits_=intervalUnits; + fromDate_=QDateTime(); + toDate_=QDateTime(); +} + +void NodeQueryPeriodOption::setPeriod(QDateTime fromDate,QDateTime toDate) +{ + mode_=FixedPeriodMode; + fromDate_=fromDate; + toDate_=toDate; + lastPeriod_=-1; + lastPeriodUnits_.clear(); +} + +QString NodeQueryPeriodOption::query() const +{ + QString s; + if(mode_ == LastPeriodMode) + { + if(lastPeriod_ >=0 && lastPeriodUnits_ >=0) + { + QDateTime prev=QDateTime::currentDateTime(); + if(lastPeriodUnits_ == "minute") + prev=prev.addSecs(-60*lastPeriod_); + else if(lastPeriodUnits_ == "hour") + prev=prev.addSecs(-3600*lastPeriod_); + else if(lastPeriodUnits_ == "day") + prev=prev.addDays(-lastPeriod_); + else if(lastPeriodUnits_ == "week") + prev=prev.addDays(-7*lastPeriod_); + else if(lastPeriodUnits_ == "month") + prev=prev.addMonths(-lastPeriod_); + else if(lastPeriodUnits_ == "year") + prev=prev.addYears(-lastPeriod_); + else + return QString(); + + s=name_ + " date::>= " + prev.toString(Qt::ISODate); + } + + } + else if(mode_ == FixedPeriodMode) + { + if(fromDate_.isValid() && toDate_.isValid()) + { + s=name_ + " date::>= " + fromDate_.toString(Qt::ISODate) + " and " + + name_ + " date::<= " + toDate_.toString(Qt::ISODate); + } + } + return s; +} + +QString NodeQueryPeriodOption::sqlQuery() const +{ + QString s; + if(mode_ == LastPeriodMode) + { + if(lastPeriod_ >=0 && lastPeriodUnits_ >=0) + { + s=name_ +" >= now() -interval " + QString::number(lastPeriod_) + " " + lastPeriodUnits_; + } + + } + else if(mode_ == FixedPeriodMode) + { + if(fromDate_.isValid() && toDate_.isValid()) + { + s=name_ + " between " + fromDate_.toString(Qt::ISODate) + + " and " + toDate_.toString(Qt::ISODate); + } + } + return s; +} + +void NodeQueryPeriodOption::swap(const NodeQueryOption* option) +{ + const NodeQueryPeriodOption* op=static_cast(option); + Q_ASSERT(op); + + if(op->mode_ == LastPeriodMode) + { + setLastPeriod(op->lastPeriod_,op->lastPeriodUnits_); + } + else if(op->mode_ == FixedPeriodMode) + { + setPeriod(op->fromDate(),op->toDate()); + } + else + mode_=NoMode; +} + + +void NodeQueryPeriodOption::save(VSettings* vs) +{ + if(mode_ == NoMode) + return; + + vs->beginGroup(name_.toStdString()); + if(mode_ == LastPeriodMode) + { + vs->put("mode","last"); + vs->put("value",lastPeriod_); + vs->put("units",lastPeriodUnits_.toStdString()); + } + else if(mode_ == FixedPeriodMode) + { + vs->put("mode","fixed"); + vs->put("from",fromDate_.toString(Qt::ISODate).toStdString()); + vs->put("to",toDate_.toString(Qt::ISODate).toStdString()); + } + vs->endGroup(); +} + +void NodeQueryPeriodOption::load(VSettings* vs) +{ + if(!vs->contains(name().toStdString())) + return; + + vs->beginGroup(name_.toStdString()); + QString mode=QString::fromStdString(vs->get("mode",std::string())); + + if(mode == "last") + { + mode_=LastPeriodMode; + lastPeriod_=vs->get("value",lastPeriod_); + lastPeriodUnits_=QString::fromStdString(vs->get("units",lastPeriodUnits_.toStdString())); + + //Check period length + if(lastPeriod_ <= 0) + clear(); + + //Check period units + if(lastPeriodUnits_ != "minute" && lastPeriodUnits_ != "hour" && + lastPeriodUnits_ != "day" && lastPeriodUnits_ != "week" && + lastPeriodUnits_ != "month" && lastPeriodUnits_ != "year") + clear(); + + } + else if(mode == "fixed") + { + mode_=FixedPeriodMode; + QString from=QString::fromStdString(vs->get("from",fromDate_.toString(Qt::ISODate).toStdString())); + QString to=QString::fromStdString(vs->get("to",fromDate_.toString(Qt::ISODate).toStdString())); + + fromDate_=QDateTime::fromString(from,Qt::ISODate); + toDate_=QDateTime::fromString(from,Qt::ISODate); + + //Check if dates are valis + if(!fromDate_.isValid() || !fromDate_.isValid()) + clear(); + } + else + { + clear(); + } + + vs->endGroup(); +} + +static NodeQueryOptionMaker maker1("string"); +static NodeQueryOptionMaker maker2("list"); +static NodeQueryOptionMaker maker3("combo"); +static NodeQueryOptionMaker maker4("period"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOptionEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOptionEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOptionEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOptionEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,481 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryOptionEdit.hpp" + +#include "ComboMulti.hpp" +#include "CustomListWidget.hpp" +#include "NodeQuery.hpp" +#include "NodeQueryOption.hpp" +#include "StringMatchCombo.hpp" +#include "ViewerUtil.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +NodeQueryOptionEdit::NodeQueryOptionEdit(QString optionId,QGridLayout* grid,QWidget* parent) : + QObject(parent), + optionId_(optionId), + initIsOn_(false), + parent_(parent), + grid_(grid) +{ + connect(this,SIGNAL(changed()), + parent_,SLOT(slotOptionEditChanged())); +} + +void NodeQueryOptionEdit::init(NodeQuery* query) +{ + init(query->option(optionId_)); +} + + +NodeQueryStringOptionEdit::NodeQueryStringOptionEdit(NodeQueryOption* option,QGridLayout* grid, + QWidget* parent,bool sameRow) : + NodeQueryOptionEdit(option->name(),grid,parent), + label_(0), + matchCb_(0), + le_(0), + option_(0) +{ + label_=new QLabel(option->label() + ":",parent_); + matchCb_=new StringMatchCombo(parent_); + matchCb_->setProperty("options","1"); + + le_=new QLineEdit(parent_); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + le_->setClearButtonEnabled(true); + //QWidgetAction *wa=new QWidgetAction(this); + //StringMatchTb* tb=new StringMatchTb(parent_); + //wa->setDefaultWidget(tb); + //le_->addAction(wa,QLineEdit::LeadingPosition); +#endif +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + le_->setPlaceholderText(tr("ANY")); +#endif + + int row=grid_->rowCount(); + if(!sameRow) + { + grid_->addWidget(label_,row,0); + grid_->addWidget(matchCb_,row,1); + grid_->addWidget(le_,row,2,1,1); + } + else + { + row=row-1; + Q_ASSERT(row >= 0); + grid_->addWidget(label_,row,3); + grid_->addWidget(matchCb_,row,4); + grid_->addWidget(le_,row,5,1,-1); + } + + connect(le_,SIGNAL(textChanged(QString)), + this,SLOT(slotEdited(QString))); + + connect(matchCb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotMatchChanged(int))); + + init(option); + Q_ASSERT(option_); +} + +void NodeQueryStringOptionEdit::init(NodeQueryOption* option) +{ + initIsOn_=true; + option_=static_cast(option); + Q_ASSERT(option_); + Q_ASSERT(optionId_ == option_->name()); + le_->setText(option_->value()); + matchCb_->setMatchMode(option_->matchMode()); + initIsOn_=false; +} + +void NodeQueryStringOptionEdit::slotEdited(QString val) +{ + if(initIsOn_) + return; + + if(option_) + { + option_->setValue(val); + Q_EMIT changed(); + } +} + +void NodeQueryStringOptionEdit::slotMatchChanged(int val) +{ + if(initIsOn_) + return; + + if(option_) + { + option_->setMatchMode(matchCb_->matchMode(val)); + Q_EMIT changed(); + } +} + +void NodeQueryStringOptionEdit::clear() +{ + le_->clear(); +} + +void NodeQueryStringOptionEdit::setVisible(bool st) +{ + label_->setVisible(st); + matchCb_->setVisible(st); + le_->setVisible(st); +} + +//=================================================================== +// +// +//================================================================== + +NodeQueryListOptionEdit::NodeQueryListOptionEdit(NodeQueryOption *option,CustomListWidget* list, + QToolButton* tb,QWidget* parent) : + NodeQueryOptionEdit(option->name(),0,parent), + list_(list), + resetTb_(tb), + option_(0) +{ + option_=static_cast(option); + Q_ASSERT(option_); + + connect(list_,SIGNAL(selectionChanged()), + this,SLOT(slotListChanged())); + + connect(resetTb_,SIGNAL(clicked()), + list_,SLOT(clearSelection())); + + list_->addItems(option_->values(),false); + resetTb_->setEnabled(list_->hasSelection()); + + init(option_); +} + +void NodeQueryListOptionEdit::init(NodeQueryOption* option) +{ + initIsOn_=true; + option_=static_cast(option); + Q_ASSERT(option_); + Q_ASSERT(option_->name() == optionId_); + list_->setSelection(option_->selection()); + initIsOn_=false; +} + +void NodeQueryListOptionEdit::clear() +{ + list_->clearSelection(); +} + +void NodeQueryListOptionEdit::slotListChanged() +{ + resetTb_->setEnabled(list_->hasSelection()); + if(initIsOn_) + return; + + if(option_) + { + option_->setSelection(list_->selection()); + Q_EMIT changed(); + } +} + +//=================================================================== +// +// NodeQueryComboOptionEdit +// +//================================================================== + +NodeQueryComboOptionEdit::NodeQueryComboOptionEdit(NodeQueryOption *option,QGridLayout* grid, QWidget* parent) : + NodeQueryOptionEdit(option->name(),grid,parent), + cb_(0), + option_(0) +{ + option_=static_cast(option); + Q_ASSERT(option_); + + label_=new QLabel(option->label() + ":",parent_); + cb_=new QComboBox(parent_); + + int row=grid_->rowCount(); + grid_->addWidget(label_,row,0); + grid_->addWidget(cb_,row,1,1,-1); + + connect(cb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotCbChanged(int))); + + QStringList vals=option_->values(); + QStringList labels=option_->valueLabels(); + for(int i=0; i < vals.count(); i++) + { + cb_->addItem(labels[i],vals[i]); + } + + init(option_); +} + +void NodeQueryComboOptionEdit::init(NodeQueryOption* option) +{ + initIsOn_=true; + option_=static_cast(option); + Q_ASSERT(option_); + Q_ASSERT(option_->name() == optionId_); + + for(int i=0; i < cb_->count(); i++) + { + if(cb_->itemData(i).toString() == option_->value()) + { + cb_->setCurrentIndex(i); + break; + } + } + initIsOn_=false; +} + +void NodeQueryComboOptionEdit::slotCbChanged(int idx) +{ + if(initIsOn_) + return; + + if(option_) + { + option_->setValue(cb_->itemData(idx).toString()); + Q_EMIT changed(); + } +} + +void NodeQueryComboOptionEdit::setVisible(bool st) +{ + label_->setVisible(st); + cb_->setVisible(st); +} + +//=================================================================== +// +// NodeQueryPeriodOptionEdit +// +//================================================================== + +NodeQueryPeriodOptionEdit::NodeQueryPeriodOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget *parent) : + NodeQueryOptionEdit(option->name(),grid,parent), + option_(0) +{ + int row=grid_->rowCount(); + + label_=new QLabel(option->label() + ": ",parent_); + + modeCb_=new QComboBox(parent); + modeCb_->addItem("any time","any"); + modeCb_->addItem("in the last","last"); + modeCb_->addItem("in period","period"); + + connect(modeCb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(modeChanged(int))); + + holder_=new QWidget(parent); + QHBoxLayout* hb=new QHBoxLayout(holder_); + hb->setContentsMargins(0,0,0,0); + + //last period + lastValueSpin_=new QSpinBox(holder_); + lastValueSpin_->setRange(1,100000); + hb->addWidget(lastValueSpin_); + + lastUnitsCb_=new QComboBox(holder_); + lastUnitsCb_->addItem(tr("minutes"),"minute"); + lastUnitsCb_->addItem(tr("hours"),"hour"); + lastUnitsCb_->addItem(tr("days"),"day"); + lastUnitsCb_->addItem(tr("weeks"),"week"); + lastUnitsCb_->addItem(tr("months"),"month"); + lastUnitsCb_->addItem(tr("years"),"year"); + hb->addWidget(lastUnitsCb_); + + connect(lastValueSpin_,SIGNAL(valueChanged(int)), + this,SLOT(lastValueChanged(int))); + + connect(lastUnitsCb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(lastUnitsChanged(int))); + + //Period: from-to + periodFromDe_=new QDateTimeEdit(holder_); + periodFromDe_->setCalendarPopup(true); + periodFromDe_->setDisplayFormat("yyyy-MM-dd hh:mm"); + periodFromDe_->setDate(QDate::currentDate()); + periodFromDe_->setMaximumDate(QDate::currentDate()); + hb->addWidget(periodFromDe_); + + periodToDe_=new QDateTimeEdit(holder_); + periodToDe_->setCalendarPopup(true); + periodToDe_->setDisplayFormat("yyyy-MM-dd hh:mm"); + periodToDe_->setDate(QDate::currentDate()); + periodToDe_->setMaximumDate(QDate::currentDate()); + periodToDe_->setTime(QTime(23,59)); + hb->addWidget(periodToDe_); + + connect(periodFromDe_,SIGNAL(dateTimeChanged(QDateTime)), + this,SLOT(slotFromChanged(QDateTime))); + + connect(periodToDe_,SIGNAL(dateTimeChanged(QDateTime)), + this,SLOT(slotToChanged(QDateTime))); + + //Add to grid + grid_->addWidget(label_,row,0); + grid_->addWidget(modeCb_,row,1); + grid_->addWidget(holder_,row,2); + + //Init + modeCb_->setCurrentIndex(0); + lastUnitsCb_->setCurrentIndex(1); //minutes + + init(option); + Q_ASSERT(option_); + + //we need to call it to have a proper init!! + modeChanged(0); +} + +void NodeQueryPeriodOptionEdit::init(NodeQueryOption* option) +{ + initIsOn_=true; + + option_=static_cast(option); + Q_ASSERT(option_); + Q_ASSERT(optionId_ == option_->name()); + if(option_->mode() == NodeQueryPeriodOption::LastPeriodMode) + { + modeCb_->setCurrentIndex(1); + lastValueSpin_->setValue(option_->lastPeriod()); + ViewerUtil::initComboBoxByData(option_->lastPeriodUnits(),lastUnitsCb_); + } + else if(option_->mode() == NodeQueryPeriodOption::FixedPeriodMode) + { + modeCb_->setCurrentIndex(2); + periodFromDe_->setDateTime(option_->fromDate()); + periodToDe_->setDateTime(option_->toDate()); + } + else + { + modeCb_->setCurrentIndex(0); + } + + initIsOn_=false; +} + +void NodeQueryPeriodOptionEdit::setVisible(bool) +{ +} + +void NodeQueryPeriodOptionEdit::modeChanged(int) +{ + int idx=modeCb_->currentIndex(); + if(idx < 0) + { + holder_->hide(); + updateOptions(); + return; + } + + QString mode=modeCb_->itemData(idx).toString(); + if(mode == "last") + { + holder_->show(); + lastValueSpin_->show(); + lastUnitsCb_->show(); + periodFromDe_->hide(); + periodToDe_->hide(); + } + else if(mode == "period") + { + holder_->show(); + lastValueSpin_->hide(); + lastUnitsCb_->hide(); + periodFromDe_->show(); + periodToDe_->show(); + } + else + { + holder_->hide(); + } + + updateOptions(); +} + +void NodeQueryPeriodOptionEdit::lastValueChanged(int) +{ + updateOptions(); +} + +void NodeQueryPeriodOptionEdit::lastUnitsChanged(int) +{ + updateOptions(); +} + +void NodeQueryPeriodOptionEdit::slotFromChanged(QDateTime) +{ + updateOptions(); +} + +void NodeQueryPeriodOptionEdit::slotToChanged(QDateTime) +{ + updateOptions(); +} + +void NodeQueryPeriodOptionEdit::updateOptions() +{ + if(initIsOn_) + return; + + if(option_) + { + int modeIdx=modeCb_->currentIndex(); + if(modeIdx < 0) + { + return; + } + QString mode=modeCb_->itemData(modeIdx).toString(); + + //last period + if(mode == "last") + { + int val=lastValueSpin_->value(); + int idx=lastUnitsCb_->currentIndex(); + QString units; + if(idx > -1) + units=lastUnitsCb_->itemData(idx).toString(); + else + val=-1; + + option_->setLastPeriod(val,units); + } + + //fixed period + else if(mode == "period") + { + option_->setPeriod(periodFromDe_->dateTime(),periodToDe_->dateTime()); + } + else + { + option_->clear(); + } + + Q_EMIT changed(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOptionEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOptionEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOptionEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOptionEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,150 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef NODEQUERYOPTIONEDIT_HPP +#define NODEQUERYOPTIONEDIT_HPP + +#include +#include + +class CustomListWidget; +class NodeQuery; +class NodeQueryDef; +class NodeQueryListModel; +class NodeQueryOption; +class NodeQueryStringOption; +class NodeQueryListOption; +class NodeQueryComboOption; +class StringMatchCombo; +class NodeQueryPeriodOption; + +class QComboBox; +class QDateTimeEdit; +class QLabel; +class QGridLayout; +class QLineEdit; +class QSpinBox; +class QToolButton; +class QWidget; + +class NodeQueryOptionEdit : public QObject +{ +Q_OBJECT +public: + NodeQueryOptionEdit(QString optionId,QGridLayout* grid,QWidget *parent); + + void init(NodeQuery*); + QString optionId() const {return optionId_;} + virtual void setVisible(bool)=0; + virtual void clear() {} + +Q_SIGNALS: + void changed(); + +protected: + virtual void init(NodeQueryOption*)=0; + + QString optionId_; + bool initIsOn_; + QWidget* parent_; + QGridLayout* grid_; +}; + +class NodeQueryStringOptionEdit : public NodeQueryOptionEdit +{ +Q_OBJECT +public: + NodeQueryStringOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget* parent, bool sameRow); + void setVisible(bool); + void clear(); + +protected Q_SLOTS: + void slotEdited(QString val); + void slotMatchChanged(int val); + +protected: + void init(NodeQueryOption*); + + QLabel* label_; + StringMatchCombo* matchCb_; + QLineEdit *le_; + NodeQueryStringOption* option_; +}; + +class NodeQueryListOptionEdit : public NodeQueryOptionEdit +{ +Q_OBJECT +public: + NodeQueryListOptionEdit(NodeQueryOption* option,CustomListWidget* cl,QToolButton*,QWidget*); + void setVisible(bool) {} + void clear(); + +protected Q_SLOTS: + void slotListChanged(); + +protected: + void init(NodeQueryOption*); + + CustomListWidget* list_; + QToolButton *resetTb_; + NodeQueryListOption* option_; +}; + +class NodeQueryComboOptionEdit : public NodeQueryOptionEdit +{ +Q_OBJECT +public: + NodeQueryComboOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget*); + void setVisible(bool); + +protected Q_SLOTS: + void slotCbChanged(int); + +protected: + void init(NodeQueryOption*); + + QLabel* label_; + QComboBox* cb_; + NodeQueryComboOption* option_; +}; + +class NodeQueryPeriodOptionEdit : public NodeQueryOptionEdit +{ +Q_OBJECT +public: + NodeQueryPeriodOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget *parent); + void setVisible(bool); + +protected: + void init(NodeQueryOption*); + +protected Q_SLOTS: + void updateOptions(); + void modeChanged(int); + void lastValueChanged(int); + void lastUnitsChanged(int); + void slotFromChanged(QDateTime); + void slotToChanged(QDateTime); + +private: + NodeQueryPeriodOption* option_; + QLabel* label_; + QComboBox* modeCb_; + QSpinBox* lastValueSpin_; + QComboBox* lastUnitsCb_; + QDateTimeEdit* periodFromDe_; + QDateTimeEdit* periodToDe_; + QWidget* holder_; +}; + + + +#endif // NODEQUERYOPTIONEDIT_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOption.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOption.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryOption.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryOption.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,178 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef NODEQUERYOPTION_HPP +#define NODEQUERYOPTION_HPP + +#include +#include + +#include "StringMatchMode.hpp" + +class NodeQuery; +class NodeQueryAttributeTerm; +class VAttributeType; +class VProperty; +class VSettings; +class NodeQueryStringOption; +class NodeQueryListOption; +class NodeQueryComboOption; + +class NodeQueryOption +{ +public: + NodeQueryOption(VProperty*); + virtual ~NodeQueryOption() {} + + QString type() const {return type_;} + QString name() const {return name_;} + QString label() const {return label_;} + virtual QString valueAsString() const=0; + + virtual void swap(const NodeQueryOption*)=0; + virtual QString query() const {return QString();} + virtual QString sqlQuery() const {return query();} + virtual QString query(QString op) const {return QString();} + virtual void load(VSettings*)=0; + virtual void save(VSettings*)=0; + + virtual NodeQueryStringOption* isString() const {return NULL;} + virtual NodeQueryListOption* isList() const {return NULL;} + virtual NodeQueryComboOption* isCombo() const {return NULL;} + + static void build(NodeQuery*); + +protected: + QString type_; + QString name_; + QString label_; + bool ignoreIfAny_; + + //QStringList values_; + //QStringList valueLabels_; +}; + +class NodeQueryStringOption : public NodeQueryOption +{ +public: + NodeQueryStringOption(VProperty*); + + void swap(const NodeQueryOption*); + + QString value() const {return value_;} + QString valueAsString() const {return value();} + const StringMatchMode& matchMode() const {return matchMode_;} + QString matchOperator() const {return QString::fromStdString(matchMode_.matchOperator());} + bool caseSensitive() const {return caseSensitive_;} + + void setValue(QString s) {value_=s;} + void setMatchMode(StringMatchMode::Mode m) {matchMode_.setMode(m);} + void setMatchMode(const StringMatchMode& m) {matchMode_=m;} + void setCaseSensitive(bool b) {caseSensitive_=b;} + + QString query() const; + void load(VSettings*); + void save(VSettings*); + + NodeQueryStringOption* isString() const {return const_cast(this);} + +protected: + QString value_; + StringMatchMode matchMode_; + bool caseSensitive_; + + static StringMatchMode::Mode defaultMatchMode_; + static bool defaultCaseSensitive_; +}; + +class NodeQueryListOption : public NodeQueryOption +{ +public: + NodeQueryListOption(VProperty*); + + void swap(const NodeQueryOption*); + + QString query(QString op) const; + void load(VSettings*); + void save(VSettings*); + + QString valueAsString() const {return QString();} + QStringList values() const {return values_;} + QStringList valueLabels() const {return valueLabels_;} + void setSelection(QStringList lst) {selection_=lst;} + QStringList selection() const {return selection_;} + + NodeQueryListOption* isList() const {return const_cast(this);} + +protected: + QStringList selection_; + QStringList values_; + QStringList valueLabels_; +}; + +class NodeQueryComboOption : public NodeQueryOption +{ +public: + NodeQueryComboOption(VProperty*); + + void swap(const NodeQueryOption*); + + QString query() const; + void load(VSettings*); + void save(VSettings*); + + QStringList values() const {return values_;} + QStringList valueLabels() const {return valueLabels_;} + void setValue(QString); + QString value() const {return value_;} + QString valueAsString() const {return value();} + + NodeQueryComboOption* isCombo() const {return const_cast(this);} + +protected: + QString value_; + QStringList values_; + QStringList valueLabels_; +}; + +class NodeQueryPeriodOption : public NodeQueryOption +{ +public: + NodeQueryPeriodOption(VProperty*); + + void swap(const NodeQueryOption*); + + QString query() const; + QString sqlQuery() const; + void load(VSettings*); + void save(VSettings*); + + void clear(); + void setLastPeriod(int lastPeriod,QString lastPeriodUnits); + void setPeriod(QDateTime,QDateTime); + + enum Mode {LastPeriodMode,FixedPeriodMode,NoMode}; + Mode mode() const {return mode_;} + int lastPeriod() const {return lastPeriod_;} + QString lastPeriodUnits() const {return lastPeriodUnits_;} + QDateTime fromDate() const {return fromDate_;} + QDateTime toDate() const {return toDate_;} + QString valueAsString() const {return QString();} + +protected: + Mode mode_; + QDateTime fromDate_; + QDateTime toDate_; + int lastPeriod_; + QString lastPeriodUnits_; +}; + +#endif // NODEQUERYOPTION_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResult.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResult.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResult.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResult.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,444 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryResult.hpp" + +#include + +#include "ServerHandler.hpp" +#include "VNode.hpp" + +//======================================================== +// +// NodeQueryResultItem +// +//======================================================== + +NodeQueryResultItem::NodeQueryResultItem(VNode* node) : + node_(node), + server_(NULL) +{ + if(node_) + server_=node_->server(); +} + +NodeQueryResultItem::NodeQueryResultItem(NodeQueryResultTmp_ptr d) +{ + node_=d->node_; + attr_=d->attr_; + + if(node_) + server_=node_->server(); +} + +void NodeQueryResultItem::invalidateNode() +{ + assert(node_); + path_=node_->absNodePath(); + node_=NULL; +} + +bool NodeQueryResultItem::updateNode() +{ + if(node_) + return (node_->server() != NULL); + + else + { + node_=server_->vRoot()->find(path_); + path_.clear(); + return (node_ != NULL); + } + + return false; +} + +QString NodeQueryResultItem::serverStr() const +{ + return (server_)?QString::fromStdString(server_->name()):QString(); +} + +QString NodeQueryResultItem::pathStr() const +{ + if(node_) + return QString::fromStdString(node_->absNodePath()); + + return QString::fromStdString(path_); +} + +QString NodeQueryResultItem::typeStr() const +{ + if(node_) + return QString::fromStdString(node_->nodeType()); + + return QString("???"); +} + +QString NodeQueryResultItem::stateStr() const +{ + if(node_) + return node_->stateName(); + return QString("???"); +} + +QColor NodeQueryResultItem::stateColour() const +{ + if(node_) + return node_->stateColour(); + return QColor(Qt::transparent); +} + +QString NodeQueryResultItem::stateChangeTimeAsString() const +{ + QString s; + if(node_) + node_->statusChangeTime(s); + + return s; +} + +unsigned int NodeQueryResultItem::stateChangeTime() const +{ + if(node_) + return node_->statusChangeTime(); + + return 0; +} + +QStringList NodeQueryResultItem::attr() const +{ + return attr_; +} + +void NodeQueryResultBlock::add(VNode* node,int pos) +{ + if(pos_==-1) + pos_=pos; + + cnt_++; + + if(nodes_[node].pos_==-1) + { + nodes_[node].pos_=pos; + } + nodes_[node].cnt_++; +} + +void NodeQueryResultBlock::clear() +{ + pos_=-1; + cnt_=0; + nodes_.clear(); +} + +bool NodeQueryResultBlock::find(const VNode* nc,int &pos, int &cnt) +{ + QHash::const_iterator it = nodes_.find(const_cast(nc)); + if(it != nodes_.end()) + { + pos=it.value().pos_; + cnt=it.value().cnt_; + return true; + } + + return false; +} + +//======================================================== +// +// NodeQueryResult +// +//======================================================== + +NodeQueryResult::NodeQueryResult(QObject *parent) : + QObject(parent) +{ +} + +NodeQueryResult::~NodeQueryResult() +{ + clear(); +} + +NodeQueryResultItem* NodeQueryResult::itemAt(int i) +{ + if(i >= 0 && i < static_cast(data_.size())) + return data_.at(i); + + return NULL; +} + +void NodeQueryResult::add(NodeQueryResultTmp_ptr item) +{ + ServerHandler *s=item->node_->server(); + if(!s) + return; + + attach(s); + + Q_EMIT beginAppendRow(); + + data_.push_back(new NodeQueryResultItem(item)); + blocks_[s].add(item->node_,data_.size()-1); + + Q_EMIT endAppendRow(); +} + +void NodeQueryResult::add(QList items) +{ + if(items.count() == 0) + return; + + Q_EMIT beginAppendRows(items.count()); + + for(int i=0; i < items.count(); i++) + { + VNode *node=items[i]->node_; + ServerHandler *s=node->server(); + attach(s); + data_.push_back(new NodeQueryResultItem(items[i])); + blocks_[s].add(node,data_.size()-1); + } + + Q_EMIT endAppendRows(items.count()); +} + +void NodeQueryResult::add(std::vector items) +{ + if(items.size() == 0) + return; + + //Count the needed items + int num=0; + for(unsigned int i=0; i < items.size(); i++) + { + assert(items.at(i) && items.at(i).get()); + if(items.at(i)->isServer() || items.at(i)->isNode()) + { + num++; + } + } + + Q_EMIT beginAppendRows(items.size()); + + for(unsigned int i=0; i < items.size(); i++) + { + if(items.at(i)->isServer() || items.at(i)->isNode()) + { + VNode *node=items.at(i)->node(); + ServerHandler *s=items.at(i)->server(); + attach(s); + data_.push_back(new NodeQueryResultItem(node)); + blocks_[s].add(node,data_.size()-1); + } + } + + Q_EMIT endAppendRows(items.size()); +} +void NodeQueryResult::clear() +{ + Q_EMIT beginReset(); + + for(std::map::const_iterator it=blocks_.begin(); it != blocks_.end(); it++) + { + it->first->removeServerObserver(this); + it->first->removeNodeObserver(this); + } + blocks_.clear(); + + for(std::vector::const_iterator it=data_.begin(); it != data_.end(); it++) + { + delete *it; + } + + data_.clear(); + + Q_EMIT endReset(); +} + +void NodeQueryResult::clear(ServerHandler* server) +{ + std::vector prev=data_; + data_.clear(); + + //Adjust the servers + detach(server); + + for(std::map::iterator it=blocks_.begin(); it != blocks_.end(); it++) + { + it->second.clear(); + } + + //Adjust data + for(std::vector::const_iterator it=prev.begin(); it != prev.end(); ++it) + { + ServerHandler *s=(*it)->node_->server(); + if(s == server) + { + delete *it; + } + else + { + data_.push_back(*it); + blocks_[s].add((*it)->node_,data_.size()-1); + } + } +} + +void NodeQueryResult::serverClear(ServerHandler* server) +{ + for(std::vector::const_iterator it=data_.begin(); it != data_.end(); ++it) + { + if(server == (*it)->server_) + { + (*it)->invalidateNode(); + } + } +} + +void NodeQueryResult::serverScan(ServerHandler* server) +{ + std::vector prev=data_; + data_.clear(); + + for(std::map::iterator it=blocks_.begin(); it != blocks_.end(); it++) + { + it->second.clear(); + } + + for(std::vector::const_iterator it=prev.begin(); it != prev.end(); ++it) + { + if(server == (*it)->server_) + { + if((*it)->updateNode()) + { + data_.push_back(*it); + blocks_[server].add((*it)->node_,data_.size()-1); + } + else + delete *it; + } + else + { + data_.push_back(*it); + blocks_[server].add((*it)->node_,data_.size()-1); + } + } + + if(blocks_[server].cnt_ == 0) + detach(server); +} + + +void NodeQueryResult::attach(ServerHandler *s) +{ + if(blocks_.find(s) == blocks_.end()) + { + s->addServerObserver(this); + s->addNodeObserver(this); + } +} + +void NodeQueryResult::detach(ServerHandler* s) +{ + std::map::iterator it=blocks_.find(s); + if(it != blocks_.end()) + { + blocks_.erase(it); + + s->removeServerObserver(this); + s->removeNodeObserver(this); + } +} + +/*void NodeQueryResult::detach(VNode *node) +{ + std::map::iterator it=serverCnt_.find(node->server()); + if(it != serverCnt_.end()) + { + it->second--; + if(it->second == 0) + { + detach(node->server()); + } + } +}*/ + +void NodeQueryResult::notifyServerDelete(ServerHandler* server) +{ + Q_EMIT beginReset(); + clear(server); + Q_EMIT endReset(); +} + +void NodeQueryResult::notifyBeginServerClear(ServerHandler* server) +{ + serverClear(server); +} + +void NodeQueryResult::notifyEndServerClear(ServerHandler* server) +{ +} + +void NodeQueryResult::notifyBeginServerScan(ServerHandler* server,const VServerChange&) +{ + Q_EMIT beginReset(); +} + +void NodeQueryResult::notifyEndServerScan(ServerHandler* server) +{ + serverScan(server); + Q_EMIT endReset(); +} + + +void NodeQueryResult::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange& vn) +{ + bool changed=false; + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + //Changes in the nodes + if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::SUSPENDED) + { + changed=true; + break; + } + } + + if(changed) + { + int pos=-1; + int cnt=0; + if(range(node,pos,cnt)) + Q_EMIT stateChanged(node,pos,cnt); + } +} + +void NodeQueryResult::notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) +{ + +} + +//----------------------------------------------- +// Mapping +//----------------------------------------------- + +bool NodeQueryResult::range(const VNode* node,int &pos,int &cnt) +{ + ServerHandler *server=node->server(); + std::map::iterator it=blocks_.find(server); + if(it != blocks_.end()) + { + if(it->second.find(node,pos,cnt)) + return true; + } + return false; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResult.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResult.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResult.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResult.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,138 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERYRESULT_HPP_ +#define VIEWER_SRC_NODEQUERYRESULT_HPP_ + +#include +#include +#include +#include + +#include "NodeObserver.hpp" +#include "NodeQueryResultTmp.hpp" +#include "ServerObserver.hpp" +#include "VInfo.hpp" + +class ServerHandler; +class VNode; + +class NodeQueryResultItem +{ + friend class NodeQueryEngine; + friend class NodeQueryResult; + friend class NodeQueryResultModel; + friend class TriggerGraphModel; + +public: + NodeQueryResultItem() : node_(NULL), server_(NULL) {} + NodeQueryResultItem(VNode* node); + NodeQueryResultItem(NodeQueryResultTmp_ptr); + + void invalidateNode(); + bool updateNode(); + + QString serverStr() const; + QString pathStr() const; + QString typeStr() const; + QString stateStr() const; + QColor stateColour() const; + QString stateChangeTimeAsString() const; + unsigned int stateChangeTime() const; + QStringList attr() const; + bool hasAttribute() const {return attr_.count() > 0;} + +protected: + VNode* node_; + ServerHandler* server_; + QStringList attr_; + std::string path_; +}; + +struct Pos +{ + Pos() : pos_(-1), cnt_(0) {} + int pos_; + int cnt_; +}; + +struct NodeQueryResultBlock : public Pos +{ + NodeQueryResultBlock() : server_(0) {} + void add(VNode*,int); + void clear(); + bool find(const VNode* node,int &pos, int &cnt); + + ServerHandler* server_; + QHash nodes_; +}; + + +class NodeQueryResult : public QObject, public ServerObserver, public NodeObserver +{ + Q_OBJECT + +public: + explicit NodeQueryResult(QObject* parent=0); + ~NodeQueryResult(); + + int size() const {return data_.size();} + NodeQueryResultItem* itemAt(int i); + void clear(); + + //From ServerObserver + void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + void notifyEndServerClear(ServerHandler* server); + void notifyBeginServerScan(ServerHandler* server,const VServerChange&); + void notifyEndServerScan(ServerHandler* server); + + //From NodeObserver + void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); + void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&); + void add(std::vector); + + +public Q_SLOTS: + void add(NodeQueryResultTmp_ptr); + void add(QList items); + +Q_SIGNALS: + void beginAppendRow(); + void endAppendRow(); + void beginAppendRows(int); + void endAppendRows(int); + void beginRemoveRow(int); + void endRemoveRow(int); + void beginRemoveRows(int,int); + void endRemoveRows(int,int); + void beginReset(); + void endReset(); + void stateChanged(const VNode*,int,int); + +protected: + void clearData(bool hideOnly); + void clear(ServerHandler*); + void serverClear(ServerHandler*); + void serverScan(ServerHandler*); + void attach(ServerHandler*); + void detach(ServerHandler*); + bool range(const VNode*,int&,int&); + //void detach(VNode*); + + std::vector data_; + //std::map serverCnt_; + std::map blocks_; +}; + + + +#endif /* VIEWER_SRC_NODEQUERYRESULT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,360 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryResultModel.hpp" + +#include "ModelColumn.hpp" +#include "NodeQueryResult.hpp" +#include "ServerHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +#include + +NodeQueryResultModel::NodeQueryResultModel(QObject *parent) : + QAbstractItemModel(parent), + columns_(0) +{ + columns_=ModelColumn::def("query_columns"); + Q_ASSERT(columns_); + + //Check the mapping between the enum and column ids + Q_ASSERT(columns_->id(ServerColumn) == "server"); + Q_ASSERT(columns_->id(PathColumn) == "path"); + Q_ASSERT(columns_->id(StatusColumn) == "status"); + Q_ASSERT(columns_->id(TypeColumn) == "type"); + Q_ASSERT(columns_->id(StatusChangeColumn) == "statusChange"); + Q_ASSERT(columns_->id(AttributeColumn) == "attribute"); + + data_=new NodeQueryResult(this); + + connect(data_,SIGNAL(beginAppendRow()), + this,SLOT(slotBeginAppendRow())); + + connect(data_,SIGNAL(endAppendRow()), + this,SLOT(slotEndAppendRow())); + + connect(data_,SIGNAL(beginAppendRows(int)), + this,SLOT(slotBeginAppendRows(int))); + + connect(data_,SIGNAL(endAppendRows(int)), + this,SLOT(slotEndAppendRows(int))); + + connect(data_,SIGNAL(beginRemoveRow(int)), + this,SLOT(slotBeginRemoveRow(int))); + + connect(data_,SIGNAL(endRemoveRow(int)), + this,SLOT(slotEndRemoveRow(int))); + + connect(data_,SIGNAL(beginRemoveRows(int,int)), + this,SLOT(slotBeginRemoveRows(int,int))); + + connect(data_,SIGNAL(endRemoveRows(int,int)), + this,SLOT(slotEndRemoveRows(int,int))); + + connect(data_,SIGNAL(beginReset()), + this,SLOT(slotBeginReset())); + + connect(data_,SIGNAL(endReset()), + this,SLOT(slotEndReset())); + + connect(data_,SIGNAL(stateChanged(const VNode*,int,int)), + this,SLOT(slotStateChanged(const VNode*,int,int))); + +} + +NodeQueryResultModel::~NodeQueryResultModel() +{ +} + + +void NodeQueryResultModel::clearData() +{ + data_->clear(); +} + +bool NodeQueryResultModel::hasData() const +{ + return data_->size() > 0; +} + +int NodeQueryResultModel::columnCount( const QModelIndex& /*parent */) const +{ + return columns_->count(); +} + +int NodeQueryResultModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + //Parent is the root: + if(!parent.isValid()) + { + return data_->size(); + } + + return 0; +} + +Qt::ItemFlags NodeQueryResultModel::flags ( const QModelIndex & index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QVariant NodeQueryResultModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid() || !hasData() || + (role != Qt::DisplayRole && role != Qt::BackgroundRole && + role != Qt::TextAlignmentRole && role != SortRole )) + { + return QVariant(); + } + + int row=index.row(); + if(row < 0 || row >= data_->size()) + return QVariant(); + + ColumnType id=static_cast(index.column()); + NodeQueryResultItem* d=data_->itemAt(row); + + if(role == Qt::DisplayRole) + { + if(id == PathColumn) + return d->pathStr(); + else if(id == ServerColumn) + return d->serverStr(); + else if(id == TypeColumn) + return d->typeStr(); + else if(id == StatusColumn) + return d->stateStr(); + else if(id == StatusChangeColumn) + return d->stateChangeTimeAsString(); + else if(id == AttributeColumn) + return d->attr(); + + return QVariant(); + } + else if(role == Qt::BackgroundRole) + { + if(id == StatusColumn) + return d->stateColour(); + + return QVariant(); + } + else if(role == Qt::TextAlignmentRole) + { + if(id == StatusColumn || id == TypeColumn || id == StatusChangeColumn) + return Qt::AlignCenter; + + return QVariant(); + } + + else if(role == SortRole) + { + if(id == PathColumn) + return d->pathStr(); + else if(id == ServerColumn) + return d->serverStr(); + else if(id == TypeColumn) + return d->typeStr(); + else if(id == StatusColumn) + return d->stateStr(); + else if(id == StatusChangeColumn) + return d->stateChangeTime(); + else if(id == AttributeColumn) + return d->attr(); + + return QVariant(); + } + + return QVariant(); +} + +QVariant NodeQueryResultModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal) + return QAbstractItemModel::headerData( section, orient, role ); + + ColumnType id=static_cast(section); + + if(role == Qt::DisplayRole) + return columns_->label(section); + else if(role == Qt::UserRole) + return columns_->id(section); + else if(role == Qt::ToolTipRole) + return columns_->tooltip(section); + else if(role == Qt::TextAlignmentRole) + { + if(id == StatusColumn || id == TypeColumn || id == StatusChangeColumn) + return Qt::AlignCenter; + } + + return QVariant(); +} + +QModelIndex NodeQueryResultModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex NodeQueryResultModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + +VInfo_ptr NodeQueryResultModel::nodeInfo(const QModelIndex& index) +{ + //For invalid index no info is created. + if(!index.isValid()) + { + VInfo_ptr res; + return res; + } + + if(index.row() >=0 && index.row() <= data_->size()) + { + NodeQueryResultItem* d=data_->itemAt(index.row()); + + if(ServerHandler *s=d->server_) + { + if(d->node_) + { + //server + if(d->node_->isServer()) + { + return VInfoServer::create(s); + } + //node + else if(!d->hasAttribute()) + { + return VInfoNode::create(d->node_); + } + //attribute + else + { + if(VAttribute* a=d->node_->findAttribute(d->attr_)) + return VInfoAttribute::create(a); + else + return VInfoNode::create(d->node_); + } + } + } + } + + VInfo_ptr res; + return res; +} + +QModelIndex NodeQueryResultModel::infoToIndex(VInfo_ptr info) +{ + /*if(info && info.get()) + { + if(info->isServer()) + { + if(ServerHandler *s=info->server()) + { + return serverToIndex(s); + } + } + else if(VNode* n=info->node()) + { + return nodeToIndex(n); + } + }*/ + + return QModelIndex(); +} + +void NodeQueryResultModel::slotBeginAppendRow() +{ + int num=data_->size(); + Q_EMIT beginInsertRows(QModelIndex(),num,num); + + + Q_EMIT endInsertRows(); +} + +void NodeQueryResultModel::slotEndAppendRow() +{ + Q_EMIT endInsertRows(); +} + +void NodeQueryResultModel::slotBeginAppendRows(int n) +{ + if(n <= 0) + return; + + int num=data_->size(); + Q_EMIT beginInsertRows(QModelIndex(),num,num+n-1); +} + +void NodeQueryResultModel::slotEndAppendRows(int n) +{ + if(n <= 0) + return; + Q_EMIT endInsertRows(); +} + +void NodeQueryResultModel::slotBeginRemoveRow(int row) +{ + beginRemoveRows(QModelIndex(),row,row); +} + +void NodeQueryResultModel::slotEndRemoveRow(int row) +{ + endRemoveRows(); +} + +void NodeQueryResultModel::slotBeginRemoveRows(int rowStart,int rowEnd) +{ + beginRemoveRows(QModelIndex(),rowStart,rowEnd); +} + +void NodeQueryResultModel::slotEndRemoveRows(int,int) +{ + endRemoveRows(); +} + +void NodeQueryResultModel::slotBeginReset() +{ + beginResetModel(); +} + +void NodeQueryResultModel::slotEndReset() +{ + endResetModel(); +} + +void NodeQueryResultModel::slotStateChanged(const VNode*,int pos,int cnt) +{ + int col=columns_->indexOf("status"); + + if(col != -1) + { + QModelIndex fromIdx=index(pos,col); + QModelIndex toIdx=index(pos+cnt-1,col); + + Q_EMIT dataChanged(fromIdx,toIdx); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,75 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ +#define VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ + +#include +#include +//#include + +#include "VInfo.hpp" + +class ModelColumn; +class NodeQueryResult; + +class NodeQueryResultModel : public QAbstractItemModel +{ +Q_OBJECT + +public: + enum CustomItemRole {SortRole = Qt::UserRole + 1}; + + explicit NodeQueryResultModel(QObject *parent=0); + ~NodeQueryResultModel(); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + NodeQueryResult *data() const {return data_;} + void clearData(); + bool hasData() const; + //void addDataStart(); + //void addDataEnd(); + + VInfo_ptr nodeInfo(const QModelIndex&); + QModelIndex infoToIndex(VInfo_ptr); + + //To speed up identifying a column. The mapping here must match the definition of + //"query_columns" in ecflowview_view_conf.json !!! + enum ColumnType {ServerColumn=0,PathColumn=1,StatusColumn=2,TypeColumn=3, + StatusChangeColumn=4,AttributeColumn=5}; + +public Q_SLOTS: + void slotBeginAppendRow(); + void slotEndAppendRow(); + void slotBeginAppendRows(int); + void slotEndAppendRows(int); + void slotBeginRemoveRow(int); + void slotEndRemoveRow(int); + void slotBeginRemoveRows(int,int); + void slotEndRemoveRows(int,int); + void slotBeginReset(); + void slotEndReset(); + void slotStateChanged(const VNode*,int,int); + +protected: + NodeQueryResult *data_; + ModelColumn* columns_; +}; + +#endif /* VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultTmp.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultTmp.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultTmp.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultTmp.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ +#define VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ + +#include +#include +#include + +class VNode; + +class NodeQueryResultTmp; +typedef boost::shared_ptr NodeQueryResultTmp_ptr; + +struct NodeQueryResultTmp +{ + NodeQueryResultTmp() : node_(NULL) {} + NodeQueryResultTmp(VNode *node) : node_(node) {} + NodeQueryResultTmp(VNode *node,QStringList attr) : node_(node), attr_(attr) {} + + VNode* node_; + QStringList attr_; +}; + +#endif /* VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,276 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryResultView.hpp" + +#include +#include +#include +#include +#include +#include + +#include "ActionHandler.hpp" +#include "NodeQueryResultModel.hpp" +#include "NodeQueryViewDelegate.hpp" +#include "UserMessage.hpp" +#include "VNode.hpp" + +NodeQueryResultView::NodeQueryResultView(QWidget* parent) : + QTreeView(parent), + model_(NULL), + sortModel_(NULL), + needItemsLayout_(false) +{ + //setProperty("style","nodeView"); + setProperty("view","query"); + + actionHandler_=new ActionHandler(this,this); + + sortModel_=new QSortFilterProxyModel(this); + //sortModel_->setDynamicSortFilter(true); + setModel(sortModel_); + + //Create delegate to the view + delegate_=new NodeQueryViewDelegate(this); + setItemDelegate(delegate_); + + connect(delegate_,SIGNAL(sizeHintChangedGlobal()), + this,SLOT(slotSizeHintChangedGlobal())); + + //setRootIsDecorated(false); + setAllColumnsShowFocus(true); + setUniformRowHeights(true); + setMouseTracking(true); + setRootIsDecorated(false); + setSortingEnabled(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + + //!!!!We need to do it because: + //The background colour between the view's left border and the nodes cannot be + //controlled by delegates or stylesheets. It always takes the QPalette::Highlight + //colour from the palette. Here we set this to transparent so that Qt could leave + //this area empty and we will fill it appropriately in our delegate. + QPalette pal=palette(); + pal.setColor(QPalette::Highlight,QColor(128,128,128,0));//Qt::transparent); + setPalette(pal); + + //Context menu + enableContextMenu(true); + + //Selection + connect(this,SIGNAL(clicked(const QModelIndex&)), + this,SLOT(slotSelectItem(const QModelIndex&))); + + connect(this,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClickItem(const QModelIndex&))); + +} + +NodeQueryResultView::~NodeQueryResultView() +{ + delete actionHandler_; +} + + +void NodeQueryResultView::enableContextMenu(bool enable) +{ + if (enable) + { + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + } + else + { + setContextMenuPolicy(Qt::NoContextMenu); + + disconnect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + } +} + + +void NodeQueryResultView::setSourceModel(NodeQueryResultModel *model) +{ + model_= model; + sortModel_->setSourceModel(model_); + sortModel_->setSortRole(NodeQueryResultModel::SortRole); +} + + + +//Collects the selected list of indexes +QModelIndexList NodeQueryResultView::selectedList() +{ + QModelIndexList lst; + Q_FOREACH(QModelIndex idx,selectedIndexes()) + { + if(idx.column() == 0) + lst << idx; + } + return lst; +} + +// this is called even if the user clicks outside of the node list to deselect all +void NodeQueryResultView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QTreeView::selectionChanged(selected, deselected); + Q_EMIT selectionChanged(); +} + +void NodeQueryResultView::slotSelectItem(const QModelIndex&) +{ + QModelIndexList lst=selectedIndexes(); + if(lst.count() > 0) + { + VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(lst.front())); + if(info) + { + Q_EMIT selectionChanged(info); + } + } +} + +VInfo_ptr NodeQueryResultView::currentSelection() +{ + QModelIndexList lst=selectedIndexes(); + if(lst.count() > 0) + { + return model_->nodeInfo(sortModel_->mapToSource(lst.front())); + } + return VInfo_ptr(); +} + +void NodeQueryResultView::currentSelection(VInfo_ptr info) +{ + /*QModelIndex idx=model_->infoToIndex(info); + if(idx.isValid()) + { + setCurrentIndex(idx); + Q_EMIT selectionChanged(info); + }*/ +} + +void NodeQueryResultView::slotSetCurrent(VInfo_ptr info) +{ + /*QModelIndex idx=model_->infoToIndex(info); + if(idx.isValid()) + { + setCurrentIndex(idx); + Q_EMIT selectionChanged(info); + }*/ +} + +void NodeQueryResultView::selectFirstServer() +{ + QModelIndex idx=sortModel_->index(0,0); + if(idx.isValid()) + { + setCurrentIndex(idx); + VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(idx)); + Q_EMIT selectionChanged(info); + } +} + + +void NodeQueryResultView::getListOfSelectedNodes(std::vector &nodeList) +{ + QModelIndexList indexList=selectedList(); + + nodeList.clear(); + for(int i=0; i < indexList.count(); i++) + { + VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(indexList[i])); + if(info && !info->isEmpty()) + nodeList.push_back(info); + } +} + + +void NodeQueryResultView::slotDoubleClickItem(const QModelIndex&) +{ +} + +void NodeQueryResultView::slotContextMenu(const QPoint &position) +{ + QModelIndexList lst=selectedList(); + //QModelIndex index=indexAt(position); + QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); + + handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); +} + + +void NodeQueryResultView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) +{ + //Node actions + if(indexClicked.isValid()) //indexLst[0].isValid() && indexLst[0].column() == 0) + { + std::vector nodeLst; + for(int i=0; i < indexLst.count(); i++) + { + VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(indexLst[i])); + if(info && !info->isEmpty()) + nodeLst.push_back(info); + } + + actionHandler_->contextMenu(nodeLst,globalPos); + } + + //Desktop actions + else + { + } +} + +void NodeQueryResultView::slotViewCommand(std::vector nodeLst,QString cmd) +{ + + if(nodeLst.size() == 0) + return; + + /*if(cmd == "set_as_root") + { + model_->setRootNode(nodeLst.at(0)->node()); + expandAll(); + }*/ +} + +void NodeQueryResultView::reload() +{ + //model_->reload(); + //expandAll(); +} + +void NodeQueryResultView::rerender() +{ + if(needItemsLayout_) + { + doItemsLayout(); + needItemsLayout_=false; + } + else + { + viewport()->update(); + } +} + +void NodeQueryResultView::slotRerender() +{ + rerender(); +} + + +void NodeQueryResultView::slotSizeHintChangedGlobal() +{ + needItemsLayout_=true; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryResultView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryResultView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,70 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ +#define VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ + +#include + +#include "VInfo.hpp" + +class ActionHandler; +class NodeQueryResultModel; +class NodeQueryViewDelegate; + +class QSortFilterProxyModel; + +class NodeQueryResultView : public QTreeView +{ +Q_OBJECT + +public: + explicit NodeQueryResultView(QWidget *parent=0); + ~NodeQueryResultView(); + + void reload(); + void rerender(); + VInfo_ptr currentSelection(); + void currentSelection(VInfo_ptr n); + void selectFirstServer(); + void setSourceModel(NodeQueryResultModel* model); + void enableContextMenu(bool enable); + void getListOfSelectedNodes(std::vector &nodeList); + + //void readSettings(VSettings* vs) {}; + +public Q_SLOTS: + void slotSelectItem(const QModelIndex&); + void slotDoubleClickItem(const QModelIndex&); + void slotContextMenu(const QPoint &position); + void slotViewCommand(std::vector,QString); + void slotSetCurrent(VInfo_ptr); + void slotRerender(); + void slotSizeHintChangedGlobal(); + void selectionChanged (const QItemSelection &selected, const QItemSelection &deselected); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + void selectionChanged(); + void infoPanelCommand(VInfo_ptr,QString); + +protected: + QModelIndexList selectedList(); + void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); + + NodeQueryResultModel* model_; + QSortFilterProxyModel* sortModel_; + ActionHandler* actionHandler_; + bool needItemsLayout_; + NodeQueryViewDelegate* delegate_; +}; + + +#endif /* VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuerySaveDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuerySaveDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQuerySaveDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQuerySaveDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,87 @@ + + + NodeQuerySaveDialog + + + + 0 + 0 + 279 + 77 + + + + Save query as + + + true + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + &Query name: + + + label + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + NodeQuerySaveDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox + rejected() + NodeQuerySaveDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryViewDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryViewDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryViewDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,263 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeQueryViewDelegate.hpp" + +#include +#include +#include +#include +#include + +#include + +#include "AbstractNodeModel.hpp" +#include "Animation.hpp" +#include "IconProvider.hpp" +#include "ModelColumn.hpp" +#include "PropertyMapper.hpp" + +static std::vector propVec; + +//Define node renderer properties +struct QueryNodeDelegateBox : public NodeDelegateBox +{ + QueryNodeDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=3; + rightMargin=0; + topPadding=0; + bottomPadding=0; + leftPadding=2; + rightPadding=1; + } +}; + +//Define attribute renderer properties +struct QueryAttrDelegateBox : public AttrDelegateBox +{ + QueryAttrDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=1; + rightMargin=0; + topPadding=0; + bottomPadding=0; + leftPadding=0; + rightPadding=0; + } +}; + +NodeQueryViewDelegate::NodeQueryViewDelegate(QWidget *parent) +{ + nodeBox_=new QueryNodeDelegateBox; + attrBox_=new QueryAttrDelegateBox; + + nodeBox_->adjust(font_); + attrBox_->adjust(attrFont_); + + borderPen_=QPen(QColor(230,230,230)); + + columns_=ModelColumn::def("query_columns"); + + //Property + if(propVec.empty()) + { + //Base settings + addBaseSettings(propVec); + } + + prop_=new PropertyMapper(propVec,this); + + updateSettings(); +} + +NodeQueryViewDelegate::~NodeQueryViewDelegate() +{ +} + +void NodeQueryViewDelegate::updateSettings() +{ + //Update the settings handled by the base class + updateBaseSettings(); +} + +QSize NodeQueryViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QSize size=QStyledItemDelegate::sizeHint(option,index); + return QSize(size.width(),nodeBox_->sizeHintCache.height()); +} + +void NodeQueryViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ + //Background +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QStyleOptionViewItem vopt(option); +#else + QStyleOptionViewItemV4 vopt(option); +#endif + + initStyleOption(&vopt, index); + + const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); + const QWidget* widget = vopt.widget; + + //Save painter state + painter->save(); + + QString id=columns_->id(index.column()); + + if(id == "path") + { + QString text=index.data(Qt::DisplayRole).toString(); + renderNode(painter,index,vopt,text); + } + else if(id == "status") + { + renderStatus(painter,index,vopt); + } + + //Render attributes + else if(id == "attribute") + { + QVariant va=index.data(Qt::DisplayRole); + if(va.type() == QVariant::StringList) + { + QStringList lst=va.toStringList(); + if(lst.count() > 0) + { + QMap::const_iterator it=attrRenderers_.find(lst.at(0)); + if(it != attrRenderers_.end()) + { + QSize size; + AttributeRendererProc a=it.value(); + (this->*a)(painter,lst,vopt,size); + } + } + } + } + + //rest of the columns + else + { + QString text=index.data(Qt::DisplayRole).toString(); + QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); + painter->setPen(Qt::black); + + int rightPos=textRect.right()+1; + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + /*QVariant alg=index.data(Qt::TextAlignmentRole); + if(alg.isValid()) + painter->drawText(textRect,alg.value(),text); + else*/ + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + if(setClipRect) + { + painter->restore(); + } + + } + + //Render the horizontal border for rows. We only render the top border line. + //With this technique we miss the bottom border line of the last row!!! + //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); + QRect bgRect=option.rect; + painter->setPen(borderPen_); + painter->drawLine(bgRect.topLeft(),bgRect.topRight()); + + painter->restore(); +} + +void NodeQueryViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const +{ + bool selected=option.state & QStyle::State_Selected; + QFontMetrics fm(font_); + + QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); + + //The text rectangle + QRect textRect = itemRect; + + int textWidth=fm.width(text); + textRect.setWidth(textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding); + + int currentRight=textRect.x()+textRect.width(); + + QList pixLst; + QList pixRectLst; + + QVariant va=index.data(AbstractNodeModel::IconRole); + if(va.type() == QVariant::List) + { + QVariantList lst=va.toList(); + if(lst.count() >0) + { + int xp=currentRight+nodeBox_->iconPreGap; + int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; + for(int i=0; i < lst.count(); i++) + { + int id=lst[i].toInt(); + if(id != -1) + { + pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); + pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); + xp+=nodeBox_->iconSize+nodeBox_->iconGap; + } + } + + if(!pixLst.isEmpty()) + { + currentRight=xp-nodeBox_->iconGap; + } + } + } + + //Define clipping + int rightPos=currentRight+1; + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw text + QColor fg=index.data(Qt::ForegroundRole).value(); + painter->setPen(fg); + painter->drawText(textRect,Qt::AlignHCenter | Qt::AlignVCenter,text); + + if(selected) + { + QRect sr=textRect; + sr.setX(option.rect.x()+nodeBox_->leftMargin); + renderSelectionRect(painter,sr); + } + + //Draw icons + for(int i=0; i < pixLst.count(); i++) + { + painter->drawPixmap(pixRectLst[i],pixLst[i]); + } + + if(setClipRect) + { + painter->restore(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryViewDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryViewDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeQueryViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeQueryViewDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,48 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ +#define VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ + +#include +#include +#include +#include +#include + +#include "NodeViewDelegate.hpp" +#include "VProperty.hpp" + +#include + +class ModelColumn; + +class NodeQueryViewDelegate : public NodeViewDelegate +{ +public: + explicit NodeQueryViewDelegate(QWidget *parent=0); + ~NodeQueryViewDelegate(); + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + +protected: + void updateSettings(); + + void renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const; + + ModelColumn* columns_; + QPen borderPen_; + +}; + +#endif /* VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,119 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include +#include + +#include "NodeSearchDialog.hpp" +#include "SessionHandler.hpp" +#include "VConfig.hpp" +#include "WidgetNameProvider.hpp" + +NodeSearchDialog::NodeSearchDialog(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + setAttribute(Qt::WA_DeleteOnClose); + + QString wt=windowTitle(); + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + connect(queryWidget_,SIGNAL(closeClicked()), + this,SLOT(accept())); + + //Read the qt settings + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +NodeSearchDialog::~NodeSearchDialog() +{ +} + +NodeSearchWidget* NodeSearchDialog::queryWidget() const +{ + return queryWidget_; +} + +void NodeSearchDialog::closeEvent(QCloseEvent * event) +{ + queryWidget_->slotStop(); //The search thread might be running!! + event->accept(); + writeSettings(); +} + +void NodeSearchDialog::accept() +{ + writeSettings(); + QDialog::accept(); +} + +void NodeSearchDialog::reject() +{ + writeSettings(); + QDialog::reject(); +} + +void NodeSearchDialog::slotOwnerDelete() +{ + deleteLater(); +} + +//------------------------------------------ +// Settings read/write +//------------------------------------------ + +void NodeSearchDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeSearchDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + queryWidget_->writeSettings(settings); + settings.endGroup(); +} + +void NodeSearchDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeSearchDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(550,540)); + } + + queryWidget_->readSettings(settings); + + /*if(settings.contains("current")) + { + int current=settings.value("current").toInt(); + if(current >=0) + list_->setCurrentRow(current); + }*/ + settings.endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,46 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef NODESEARCHDIALOG_HPP_ +#define NODESEARCHDIALOG_HPP_ + +#include + +#include "ServerFilter.hpp" + +#include "ui_NodeSearchDialog.h" + +class ServerFilter; + +class NodeSearchDialog : public QDialog, protected Ui::NodeSearchDialog +{ + Q_OBJECT + +public: + explicit NodeSearchDialog(QWidget *parent = 0); + ~NodeSearchDialog(); + + NodeSearchWidget* queryWidget() const; + +protected Q_SLOTS: + void accept(); + void reject(); + void slotOwnerDelete(); + +protected: + void closeEvent(QCloseEvent * event); + +private: + void readSettings(); + void writeSettings(); +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ + + + NodeSearchDialog + + + + 0 + 0 + 514 + 544 + + + + Query + + + + 4 + + + + + + + + + NodeSearchWidget + QWidget +
      NodeSearchWidget.hpp
      + 1 +
      +
      + + + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,356 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "NodeSearchWidget.hpp" + +#include "ComboMulti.hpp" +#include "CustomListWidget.hpp" +#include "Highlighter.hpp" +#include "NodeQuery.hpp" +#include "NodeQueryEngine.hpp" +#include "NodeQueryHandler.hpp" +#include "NodeQueryHandler.hpp" +#include "NodeQueryResult.hpp" +#include "NodeQueryResultModel.hpp" +#include "ServerFilter.hpp" +#include "UiLog.hpp" +#include "VNState.hpp" + +#include +#include +#include +#include +#include + +#define _UI_NODESEARCHWIDGET_DEBUG + +//====================================================== +// +// NodeQueryWidget +// +//====================================================== + +NodeSearchWidget::NodeSearchWidget(QWidget *parent) : + QWidget(parent), + query_(NULL), + columnsAdjusted_(false) +{ + setupUi(this); + + //-------------------- + // Show/hide + //-------------------- + + //Show hide def panel + connect(defPanelTb_,SIGNAL(toggled(bool)), + this,SLOT(slotShowDefPanel(bool))); + + editor_->show(); + + //connect(editor_,SIGNAL(queryEnabledChanged(bool)), + // this,SLOT(slotQueryEnabledChanged(bool))); + + //Show hide query + connect(queryPanelTb_,SIGNAL(toggled(bool)), + this,SLOT(slotShowQueryPanel(bool))); + + QFont showf; + showf.setBold(true); + showf.setPointSize(showf.pointSize()-1); + showLabel_->setFont(showf); + showLabel_->setText("Show:"); + + //Find button + findPb_->setProperty("startSearch","1"); + QPalette pal=findPb_->palette(); + QColor col(230,245,253); + pal.setColor(QPalette::Button,col); + findPb_->setPalette(pal); + + connect(findPb_,SIGNAL(clicked()), + this,SLOT(slotFind())); + + connect(stopPb_,SIGNAL(clicked()), + this,SLOT(slotStop())); + + //Clear + connect(clearPb_,SIGNAL(clicked()), + editor_,SLOT(slotClear())); + + //Close button + connect(closePb_,SIGNAL(clicked()), + this,SLOT(slotClose())); + + //-------------------------------- + // Result tree/model + //-------------------------------- + + model_=new NodeQueryResultModel(this); + resTree_->setSourceModel(model_); + + connect(resTree_,SIGNAL(selectionChanged(VInfo_ptr)), + this,SIGNAL(selectionChanged(VInfo_ptr))); + + connect(resTree_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); + + //-------------------------------- + // Query + //-------------------------------- + + query_=new NodeQuery("tmp"); + + //-------------------- + // Query engine + //-------------------- + + engine_=new NodeQueryEngine(this); + + connect(engine_,SIGNAL(found(QList)), + model_->data(), SLOT(add(QList))); + + connect(engine_,SIGNAL(found(NodeQueryResultTmp_ptr)), + model_->data(),SLOT(add(NodeQueryResultTmp_ptr))); + + connect(engine_,SIGNAL(started()), + this,SLOT(slotQueryStarted())); + + connect(engine_,SIGNAL(finished()), + this,SLOT(slotQueryFinished())); + + //------------------- + // Progress + //------------------- + + queryProgress_->hide(); + + stopPb_->setEnabled(false); +} + +NodeSearchWidget::~NodeSearchWidget() +{ + delete query_; +} + +void NodeSearchWidget::setServerFilter(ServerFilter* f) +{ + editor_->setServerFilter(f); +} + +void NodeSearchWidget::setRootNode(VInfo_ptr info) +{ + editor_->setRootNode(info); +} + +void NodeSearchWidget::slotShowDefPanel(bool b) +{ + editor_->showDefPanel(b); +} + +void NodeSearchWidget::slotShowQueryPanel(bool b) +{ + editor_->showQueryPanel(b); +} + +void NodeSearchWidget::slotQueryEnabledChanged(bool queryEnabled) +{ + //if(!engine_->isRunning()) + //{ +#if 0 + UiLog().dbg() << "NodeSearchWidget::slotQueryEnabledChanged -->" << std::string((queryEnabled?"true":"false")); + findPb_->setEnabled(queryEnabled); +#endif + //} +} + +void NodeSearchWidget::slotFind() +{ +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << "NodeSearchWidget::slotFind -->"; +#endif + + //Avoid double clicking + if(!findPb_->isEnabled()) + { + UiLog().dbg() << "<-- NodeSearchWidget::slotFind - search is already running"; + return; + } + +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << " isRunning=" << engine_->isRunning(); +#endif + + adjustColumns(); + + //Clear the results + model_->clearData(); + + assert(!engine_->isRunning()); + assert(findPb_->isEnabled()); + assert(!stopPb_->isEnabled()); + + //We set the button state in advance as if the engine were running + adjustButtonState(true); + + elapsed_.start(); + if(!engine_->runQuery(editor_->query(),editor_->allServers())) + { + elapsed_=QTime(); + + //if we are here we could not start the query and we need to reset the button state + adjustButtonState(); + } +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << "<-- NodeSearchWidget::slotFind"; +#endif +} + +void NodeSearchWidget::slotStop() +{ + //It is a blocking call! + engine_->stopQuery(); + assert(!engine_->isRunning()); + adjustButtonState(); +} + +void NodeSearchWidget::slotClose() +{ + slotStop(); + Q_EMIT closeClicked(); +} + +void NodeSearchWidget::slotQueryStarted() +{ +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << "NodeSearchWidget::slotQueryStarted -->"; +#endif + adjustButtonState(); + + queryProgress_->setRange(0,0); + queryProgress_->show(); + + progressLabel_->setText("Search in progress ..."); +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << "<-- slotQueryStarted"; +#endif +} + +void NodeSearchWidget::slotQueryFinished() +{ +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << "NodeSearchWidget::slotQueryFinished -->"; + UiLog().dbg() << " Search finished. Total node scanned: " << engine_->scannedCount(); +#endif + + adjustButtonState(); + + queryProgress_->hide(); + queryProgress_->setRange(0,1); + queryProgress_->setValue(1); + + QString s="" + QString::number(model_->rowCount()) + " items found in " + + QString::number(elapsed_.elapsed()*0.001,'f',1) + " s"; + + QColor col(90,92,92); + if(engine_->wasMaxReached()) + { + s+=" (stopped due to maxnum reached!)"; + } + else if(engine_->wasStopped()) + { + s+=" (query was interrupted!)"; + } + progressLabel_->setText(s); + elapsed_=QTime(); + +#ifdef _UI_NODESEARCHWIDGET_DEBUG + UiLog().dbg() << " isRunning=" << engine_->isRunning(); + UiLog().dbg() << "<-- NodeSearchWidget::slotQueryFinished"; +#endif +} + + +void NodeSearchWidget::adjustButtonState() +{ + adjustButtonState(engine_->isRunning()); +} + +void NodeSearchWidget::adjustButtonState(bool engineRunning) +{ + if(engineRunning) + { + findPb_->setEnabled(false); + stopPb_->setEnabled(true); + editor_->setEnabled(false); + } + else + { + findPb_->setEnabled(true); + stopPb_->setEnabled(false); + editor_->setEnabled(true); + } + + UiLog().dbg() << "NodeSearchWidget::adjustButtonState -->"; + UiLog().dbg() << " findTb_: " << findPb_->isEnabled(); + UiLog().dbg() << "<-- adjustButtonState"; +} + +void NodeSearchWidget::adjustColumns() +{ + if(!columnsAdjusted_) + { + columnsAdjusted_=true; + + //We preset the column width. Setting it dynamically can be expensive + //for a large number of rows (> 1M) + QFont f; + QFontMetrics fm(f); + resTree_->setColumnWidth(0,fm.width("serverserverserse")); + resTree_->setColumnWidth(1,fm.width("/suite/family1/family2/longtaskname1")); + resTree_->setColumnWidth(2,fm.width("suspendedAA")); + resTree_->setColumnWidth(3,fm.width("familyAA")); + resTree_->setColumnWidth(4,fm.width("2017-Mar-07 15:45:56AA")); + } +} + +void NodeSearchWidget::writeSettings(QSettings &settings) +{ + settings.setValue("defPanel",editor_->isDefPanelVisible()); + settings.setValue("queryPanel",editor_->isQueryPanelVisible()); + QStringList colW; + for(int i=0; i < resTree_->model()->columnCount()-1; i++) + colW << QString::number(resTree_->columnWidth(i)); + + settings.setValue("resColumnWidth",colW); +} + +void NodeSearchWidget::readSettings(const QSettings &settings) +{ + if(settings.contains("defPanel")) + { + defPanelTb_->setChecked(settings.value("defPanel").toBool()); + } + if(settings.contains("queryPanel")) + { + queryPanelTb_->setChecked(settings.value("queryPanel").toBool()); + } + if(settings.contains("resColumnWidth")) + { + QStringList lst=settings.value("resColumnWidth").toStringList(); + for(int i=0; i < lst.count(); i++) + resTree_->setColumnWidth(i,lst[i].toInt()); + + if(lst.count() >= 4) + columnsAdjusted_=true; + } +} + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,71 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_NODESEARCHWIDGET_HPP_ +#define VIEWER_SRC_NODESEARCHWIDGET_HPP_ + +#include +#include +#include +#include +#include + +#include "ServerFilter.hpp" +#include "VInfo.hpp" + +#include "ui_NodeSearchWidget.h" + +class NodeQuery; +class NodeQueryEngine; +class NodeQueryResultModel; + +class NodeSearchWidget : public QWidget, protected Ui::NodeSearchWidget +{ + Q_OBJECT + +public: + explicit NodeSearchWidget(QWidget *parent = 0); + ~NodeSearchWidget(); + + void setServerFilter(ServerFilter*); + void setRootNode(VInfo_ptr); + + void writeSettings(QSettings &settings); + void readSettings(const QSettings &settings); + +public Q_SLOTS: + void slotStop(); + +protected Q_SLOTS: + void slotShowDefPanel(bool); + void slotShowQueryPanel(bool); + void slotFind(); + void slotClose(); + void slotQueryStarted(); + void slotQueryFinished(); + void slotQueryEnabledChanged(bool queryEnabled); + +Q_SIGNALS: + void closeClicked(); + void selectionChanged(VInfo_ptr); + void infoPanelCommand(VInfo_ptr,QString); + +private: + void adjustColumns(); + void adjustButtonState(); + void adjustButtonState(bool); + + NodeQuery* query_; + NodeQueryEngine* engine_; + NodeQueryResultModel* model_; + bool columnsAdjusted_; + QTime elapsed_; +}; + +#endif /* VIEWER_SRC_NODESEARCHWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeSearchWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeSearchWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,213 @@ + + + NodeSearchWidget + + + + 0 + 0 + 1564 + 1113 + + + + Form + + + + + + + + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + 24 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + + + + + Search + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + + + + + Stop + + + + + + + Qt::Horizontal + + + + + + + Clear the node and attribute query options + + + Clear options + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Show: + + + + + + + + 0 + 0 + + + + Editor + + + true + + + false + + + + + + + + 0 + 0 + + + + Query + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Close + + + + + + + + + + NodeQueryResultView + QTreeView +
      NodeQueryResultView.hpp
      +
      + + NodeQueryEditor + QWidget +
      NodeQueryEditor.hpp
      + 1 +
      +
      + + +
      diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewBase.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewBase.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewBase.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewBase.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,15 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "NodeViewBase.hpp" + +NodeViewBase::NodeViewBase(NodeFilterDef* filterDef) : filterDef_(filterDef) +{ + +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewBase.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewBase.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewBase.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewBase.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,45 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef NODEVIEWBASE_HPP_ +#define NODEVIEWBASE_HPP_ + +#include "Viewer.hpp" +#include "VInfo.hpp" + +class QWidget; +class QObject; + +class TableNodeSortModel; +class NodeFilterDef; +class VSettings; +class QModelIndex; + +class NodeViewBase +{ +public: + explicit NodeViewBase(NodeFilterDef*); + virtual ~NodeViewBase(){} + + virtual void reload()=0; + virtual void rerender()=0; + virtual QWidget* realWidget()=0; + virtual QObject* realObject()=0; + virtual VInfo_ptr currentSelection()=0; + virtual void selectFirstServer()=0; + virtual void setCurrentSelection(VInfo_ptr n)=0; + + virtual void readSettings(VSettings* vs)=0; + virtual void writeSettings(VSettings* vs)=0; + +protected: + NodeFilterDef* filterDef_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1419 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "NodeViewDelegate.hpp" + +#include +#include +#include +#include + +#include "AbstractNodeModel.hpp" +#include "PropertyMapper.hpp" +#include "UiLog.hpp" + +int NodeViewDelegate::lighter_=150; + +static std::vector propVec; + +NodeViewDelegate::NodeViewDelegate(QWidget *parent) : + QStyledItemDelegate(parent), + prop_(0), + nodeBox_(0), + attrBox_(0), + useStateGrad_(true), + drawAttrSelectionRect_(false), + limitShape_(RectLimitShape) +{ + hoverPen_=QPen(QColor(201,201,201)); + hoverBrush_=QBrush(QColor(250,250,250,210)); + selectPen_=QPen(QColor(125,162,206)); + selectBrush_=QBrush(QColor(193,220,252,110)); + nodePen_=QPen(QColor(180,180,180)); + nodeSelectPen_=QPen(QColor(0,0,0),2); + + lostConnectBgBrush_=QBrush(QColor(150,150,150,150),Qt::Dense7Pattern); + lostConnectBandBrush_=QBrush(QColor(255,166,0,150)); + + QImageReader imgR(":/viewer/warning.svg"); + if(imgR.canRead()) + { + QFont font; + QFontMetrics fm(font); + int size=fm.height()+2; + imgR.setScaledSize(QSize(size,size)); + QImage img=imgR.read(); + errPix_=QPixmap(QPixmap::fromImage(img)); + } + + grad_.setCoordinateMode(QGradient::ObjectBoundingMode); + grad_.setStart(0,0); + grad_.setFinalStop(0,1); + + eventFillBrush_=QBrush(QColor(0,0,255)); + eventFillBrush_=QBrush(QColor(240,240,240)); + meterFillBrush_=QBrush(QColor(0,0,255)); + meterThresholdBrush_=QBrush(QColor(0,0,255)); + limitFillBrush_=QBrush(QColor(0,255,0)); + limitExtraFillBrush_=QBrush(QColor(0,0,255)); + triggerBgBrush_=QBrush(QColor(230,230,230)); + triggerBorderPen_=QPen(QColor(150,150,150)); + triggerFontPen_=QPen(QColor(0,0,0)); + completeBgBrush_=QBrush(QColor(230,230,230)); + completeBorderPen_=QPen(QColor(150,150,150)); + completeFontPen_=QPen(QColor(0,0,255)); + + attrRenderers_["meter"]=&NodeViewDelegate::renderMeter; + attrRenderers_["label"]=&NodeViewDelegate::renderLabel; + attrRenderers_["event"]=&NodeViewDelegate::renderEvent; + attrRenderers_["var"]=&NodeViewDelegate::renderVar; + attrRenderers_["genvar"]=&NodeViewDelegate::renderGenvar; + attrRenderers_["limit"]=&NodeViewDelegate::renderLimit; + attrRenderers_["limiter"]=&NodeViewDelegate::renderLimiter; + attrRenderers_["trigger"]=&NodeViewDelegate::renderTrigger; + attrRenderers_["time"]=&NodeViewDelegate::renderTime; + attrRenderers_["date"]=&NodeViewDelegate::renderDate; + attrRenderers_["repeat"]=&NodeViewDelegate::renderRepeat; + attrRenderers_["late"]=&NodeViewDelegate::renderLate; +} + +NodeViewDelegate::~NodeViewDelegate() +{ + Q_ASSERT(nodeBox_); + delete nodeBox_; + + Q_ASSERT(attrBox_); + delete attrBox_; + + if(prop_) + delete prop_; +} + +void NodeViewDelegate::notifyChange(VProperty* p) +{ + updateSettings(); +} + +void NodeViewDelegate::addBaseSettings(std::vector& propVec) +{ + propVec.push_back("view.common.node_gradient"); + propVec.push_back("view.attribute.eventFillColour"); + propVec.push_back("view.attribute.meterFillColour"); + propVec.push_back("view.attribute.meterThresholdColour"); + propVec.push_back("view.attribute.limitShape"); + propVec.push_back("view.attribute.limitFillColour"); + propVec.push_back("view.attribute.limitExtraFillColour"); + propVec.push_back("view.attribute.triggerBackground"); + propVec.push_back("view.attribute.triggerBorderColour"); + propVec.push_back("view.attribute.triggerFontColour"); + propVec.push_back("view.attribute.completeBackground"); + propVec.push_back("view.attribute.completeBorderColour"); + propVec.push_back("view.attribute.completeFontColour"); +} + +void NodeViewDelegate::updateBaseSettings() +{ + if(VProperty* p=prop_->find("view.common.node_gradient")) + { + useStateGrad_=p->value().toBool(); + } + if(VProperty* p=prop_->find("view.attribute.eventFillColour")) + { + eventFillBrush_=QBrush(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.meterFillColour")) + { + QLinearGradient gr; + gr.setCoordinateMode(QGradient::ObjectBoundingMode); + gr.setStart(0,0); + gr.setFinalStop(0,1); + QColor c1=p->value().value(); + gr.setColorAt(0,c1); + gr.setColorAt(1,c1.lighter(110)); + meterFillBrush_=QBrush(gr); + } + if(VProperty* p=prop_->find("view.attribute.meterThresholdColour")) + { + QLinearGradient gr; + gr.setCoordinateMode(QGradient::ObjectBoundingMode); + gr.setStart(0,0); + gr.setFinalStop(0,1); + QColor c1=p->value().value(); + gr.setColorAt(0,c1); + gr.setColorAt(1,c1.lighter(110)); + meterThresholdBrush_=QBrush(gr); + } + if(VProperty* p=prop_->find("view.attribute.limitShape")) + { + QString shape=p->value().toString(); + if(shape == "rectangle") + limitShape_=RectLimitShape; + else if(shape == "circle") + limitShape_=CircleLimitShape; + else + limitShape_=CircleLimitShape; + } + if(VProperty* p=prop_->find("view.attribute.limitFillColour")) + { + limitFillBrush_=QBrush(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.limitExtraFillColour")) + { + limitExtraFillBrush_=QBrush(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.triggerBackground")) + { + triggerBgBrush_=QBrush(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.triggerBorderColour")) + { + triggerBorderPen_=QPen(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.triggerFontColour")) + { + triggerFontPen_=QPen(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.completeBackground")) + { + completeBgBrush_=QBrush(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.completeBorderColour")) + { + completeBorderPen_=QPen(p->value().value()); + } + if(VProperty* p=prop_->find("view.attribute.completeFontColour")) + { + completeFontPen_=QPen(p->value().value()); + } + + //limit pixmaps + if(limitShape_ == CircleLimitShape) + { + QFontMetrics fm(attrFont_); + int itemSize=static_cast(static_cast(fm.ascent())*0.8); + + QImage img(itemSize,itemSize,QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::transparent); + QPainter painter(&img); + painter.setRenderHint(QPainter::Antialiasing,true); + + painter.setPen(Qt::NoPen); + painter.setBrush(QColor(70,70,70)); + painter.drawEllipse(1,1,itemSize-1,itemSize-1); + painter.setBrush(limitFillBrush_); + painter.drawEllipse(1,1,itemSize-2,itemSize-2); + limitFillPix_=QPixmap::fromImage(img); + + painter.fillRect(QRect(QPoint(0,0),img.size()),Qt::transparent); + painter.setBrush(QColor(70,70,70)); + painter.drawEllipse(1,1,itemSize-1,itemSize-1); + painter.setBrush(limitExtraFillBrush_); + painter.drawEllipse(1,1,itemSize-2,itemSize-2); + limitExtraFillPix_=QPixmap::fromImage(img); + + painter.fillRect(QRect(QPoint(0,0),img.size()),Qt::transparent); + painter.setBrush(QColor(130,130,130)); + painter.drawEllipse(1,1,itemSize-1,itemSize-1); + painter.setBrush(QColor(240,240,240)); + painter.drawEllipse(1,1,itemSize-2,itemSize-2); + limitEmptyPix_=QPixmap::fromImage(img); + } +} + +void NodeViewDelegate::renderSelectionRect(QPainter* painter,QRect r) const +{ + painter->setPen(nodeSelectPen_); + painter->setBrush(Qt::NoBrush); + painter->drawRect(r); +} + +void NodeViewDelegate::renderStatus(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option) const +{ + int offset=4; + + QFontMetrics fm(font_); + int deltaH=(option.rect.height()-(fm.height()+4))/2; + + //The initial filled rect (we will adjust its width) + QRect fillRect=option.rect.adjusted(offset,deltaH,-offset,-deltaH-1); + if(option.state & QStyle::State_Selected) + fillRect.adjust(0,0,0,-0); + + int currentRight=fillRect.right(); + + //The text rectangle + QString text=index.data(Qt::DisplayRole).toString(); + int textWidth=fm.width(text); + QRect textRect = fillRect.adjusted(offset,0,0,0); + textRect.setWidth(textWidth); + + if(textRect.right() > currentRight) + currentRight=textRect.right(); + + //Define clipping + int rightPos=currentRight+1; + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + QRect cr=option.rect.adjusted(0,0,-offset,0); + painter->setClipRect(cr); + } + + //Fill rect + QColor bg=index.data(Qt::BackgroundRole).value(); + QColor bgLight=bg.lighter(lighter_); + QBrush bgBrush; + if(useStateGrad_) + { + grad_.setColorAt(0,bgLight); + grad_.setColorAt(1,bg); + bgBrush=QBrush(grad_); + } + else + bgBrush=QBrush(bg); + + painter->fillRect(fillRect,bgBrush); + + //Draw text + painter->setFont(font_); + painter->setPen(Qt::black); + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + if(setClipRect) + { + painter->restore(); + } +} + +//======================================================== +// data is encoded as a QStringList as follows: +// "meter" name value min max colChange +//======================================================== + +void NodeViewDelegate::renderMeter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 6) + return; + + //The data + int val=data.at(2).toInt(); + int min=data.at(3).toInt(); + int max=data.at(4).toInt(); + int threshold=data.at(5).toInt(); + + bool selected=option.state & QStyle::State_Selected; + + QString name=data.at(1) + ":"; + + if(data.count() == 7) + name.prepend(data[6] + ":"); + + QString valStr=data.at(2); + QFontMetrics fm(attrFont_); + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin, + attrBox_->topMargin+attrBox_->topPadding,0, + -attrBox_->bottomMargin-attrBox_->bottomPadding); + + //The status rectangle + int stHeight=static_cast(static_cast(fm.ascent())*0.8); + int stHeightDiff=(contRect.height()-stHeight)/2; + + QRect stRect=contRect.adjusted(attrBox_->leftPadding,stHeightDiff,0,-stHeightDiff); + stRect.setWidth(50); + + //The text rectangle + QFont nameFont=attrFont_; + nameFont.setBold(true); + fm=QFontMetrics(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect; + nameRect.setLeft(stRect.x()+stRect.width()+attrBox_->spacing); + nameRect.setWidth(nameWidth); + + //The value rectangle + QFont valFont=attrFont_; + fm=QFontMetrics(valFont); + int valWidth=fm.width(valStr); + QRect valRect = nameRect; + valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); + valRect.setWidth(valWidth); + + //Define clipping + int rightPos=valRect.x()+valRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Fill st rect + painter->fillRect(stRect,QColor(229,229,229)); + + //Draw progress + if(max > min) + { + QRect progRect=stRect; + + float valPercent=static_cast(val-min)/static_cast(max-min); + if(threshold > min && threshold < max && val > threshold) + { + int progWidth=static_cast(static_cast(stRect.width())*valPercent); + if(val < max) + { + progRect.setWidth(progWidth); + } + painter->fillRect(progRect,meterThresholdBrush_); + + float thresholdPercent=static_cast(threshold-min)/static_cast(max-min); + progWidth=static_cast(static_cast(stRect.width())*thresholdPercent); + progRect.setWidth(progWidth); + painter->fillRect(progRect,meterFillBrush_); + } + else + { + int progWidth=static_cast(static_cast(stRect.width())*valPercent); + if(val < max) + { + progRect.setWidth(progWidth); + } + painter->fillRect(progRect,meterFillBrush_); + } + } + + //Draw st rect border + if(max > min) + { + painter->setPen(QColor(140,140,140)); + } + else + { + painter->setPen(QPen(QColor(140,140,140),Qt::DotLine)); + } + painter->setBrush(Qt::NoBrush); + painter->drawRect(stRect); + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + //Draw value + painter->setPen(Qt::black); + painter->setFont(valFont); + painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valStr); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +//======================================================== +// data is encoded as a QStringList as follows: +// "label" name value +//======================================================== + +void NodeViewDelegate::renderLabel(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 2) + return; + + QString name=data.at(1) + ":"; + QString val; + if(data.count() > 2) + val=data.at(2); + + bool selected=option.state & QStyle::State_Selected; + + //The border rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin, + attrBox_->topMargin+attrBox_->topPadding,0, + -attrBox_->bottomMargin-attrBox_->bottomPadding); + + int currentRight=contRect.x(); + int multiCnt=val.count('\n'); + + QRect nameRect; + QRect valRect,valRestRect; + + QFont nameFont=attrFont_; + nameFont.setBold(true); + QFont valFont=attrFont_; + QString valFirst,valRest; + + if(multiCnt ==0 ) + { + //The text rectangle + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //The value rectangle + fm=QFontMetrics(valFont); + int valWidth=fm.width(val); + valRect = nameRect; + valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); + valRect.setWidth(valWidth); + + //Adjust the filled rect width + currentRight=valRect.x()+valRect.width(); + } + else + { + //The text rectangle + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + nameRect.setHeight(attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding); + + //The value rectangles + fm=QFontMetrics(valFont); + + //First row comes after the name rect! + QStringList valLst=val.split("\n"); + Q_ASSERT(valLst.count() > 0); + valFirst=valLst[0]; + + valRect=nameRect; + valRect.setX(nameRect.x() + nameRect.width() + attrBox_->spacing); + valRect.setWidth(fm.width(valFirst)); + + //The rest of the rows + valLst.takeFirst(); + valRest=valLst.join("\n"); + QSize valSize=fm.size(0,valRest); + + valRestRect = QRect(nameRect.x(), + nameRect.y()+nameRect.height()+2, + valSize.width(),valSize.height()); + + currentRight=qMax(valRect.x()+valRect.width(), + valRestRect.x() + valRestRect.width()); + } + + + //Define clipping + int rightPos=currentRight+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.left(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + //Draw value + painter->setPen(Qt::black); + painter->setFont(valFont); + + if(multiCnt ==0 ) + painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,val); + else + { + painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valFirst); + painter->drawText(valRestRect,Qt::AlignLeft | Qt::AlignVCenter,valRest); + } + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size=QSize(totalWidth,labelHeight(multiCnt+1)); +} + +void NodeViewDelegate::labelSize(QStringList data,int& totalWidth,int& totalHeight) const +{ + if(data.count() < 2) + return; + + QString name=data.at(1) + ":"; + QString val; + if(data.count() > 2) + val=data.at(2); + + int currentRight=attrBox_->leftMargin; + int currentBottom=attrBox_->topMargin+attrBox_->topPadding; + + int multiCnt=val.count('\n'); + QFont nameFont=attrFont_; + nameFont.setBold(true); + QFont valFont=attrFont_; + + if(multiCnt ==0 ) + { + //The text rectangle + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + currentRight+=attrBox_->leftPadding+nameWidth; + currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; + + //The value rectangle + fm=QFontMetrics(valFont); + int valWidth=fm.width(val); + currentRight+=attrBox_->spacing+valWidth; + } + + else + { + //The text rectangle + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + int startX=currentRight+attrBox_->leftPadding; + currentRight+=attrBox_->leftPadding+nameWidth; + currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; + + //The value rectangle + fm=QFontMetrics(valFont); + + //First row comes after the name rect! + QStringList valLst=val.split("\n"); + Q_ASSERT(valLst.count() > 0); + + currentRight+=attrBox_->spacing+fm.width(valLst[0]); + + //The rest of the rows + valLst.takeFirst(); + QString valRest=valLst.join("\n"); + QSize valSize=fm.size(0,valRest); + + currentRight=qMax(currentRight,startX+valSize.width()); + currentBottom+=2+valSize.height(); + } + + totalWidth=currentRight+attrBox_->rightPadding+attrBox_->rightMargin; + totalHeight=currentBottom+attrBox_->bottomPadding+attrBox_->bottomMargin; +} + +int NodeViewDelegate::labelHeight(int lineNum) const +{ + if(lineNum <=1) + return attrBox_->sizeHintCache.height(); + + int currentBottom=attrBox_->topMargin+attrBox_->topPadding; + + //text rect + currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; + + //value rect + QStringList lst; + for(int i=0; i < lineNum-1; i++) + lst << "1"; + + QFontMetrics fm(attrFont_); + QSize valSize=fm.size(0,lst.join(QString("\n"))); + currentBottom+=2+valSize.height(); + + return currentBottom+attrBox_->bottomPadding+attrBox_->bottomMargin; +} + +//======================================================== +// data is encoded as a QStringList as follows: +// "event" name value +//======================================================== + +void NodeViewDelegate::renderEvent(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 2) + return; + + QString name=data[1]; + bool val=false; + if(data.count() > 2) val=(data[2] == "1"); + + //Add full path + if(data.count() > 3) + { + name.prepend(data[3] + ":/"); + } + + bool selected=option.state & QStyle::State_Selected; + QFont font=attrFont_; + QFontMetrics fm(font); + + //The border rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The control rect + int ctHeight=static_cast(static_cast(fm.ascent())*0.8); + int ctHeightDiff=qMax((contRect.height()-ctHeight)/2,2); + + QRect ctRect=contRect.adjusted(attrBox_->leftPadding,ctHeightDiff,0,-ctHeightDiff); + ctRect.setWidth(ctRect.height()); + + //The text rectangle + int nameWidth=fm.width(name); + QRect nameRect = contRect; + nameRect.setX(ctRect.x()+ctRect.width()+attrBox_->spacing); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.left(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw control + painter->setPen(Qt::black); + painter->setBrush((val)?eventFillBrush_:eventBgBrush_); + painter->drawRect(ctRect); + + //Draw name + painter->setPen(Qt::black); + painter->setFont(font); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderVarCore(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size,QColor textCol) const +{ + int totalWidth=0; + QString text; + + if(data.count() >1) + text+=data.at(1) + "="; + if(data.count() > 2) + text+=data.at(2); + + if(data.count() == 4) + text.prepend(data[3] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The text rectangle + QFont font=attrFont_; + QFontMetrics fm(font); + int textWidth=fm.width(text); + QRect textRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + textRect.setWidth(textWidth); + + //Define clipping + int rightPos=textRect.x()+textRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.left(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw text + painter->setPen(textCol); + painter->setFont(font); + painter->drawText(attrBox_->adjustTextRect(textRect),Qt::AlignLeft | Qt::AlignVCenter,text); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size=QSize(totalWidth,attrBox_->fullHeight); +} + + +void NodeViewDelegate::renderVar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + renderVarCore(painter,data,option,size,Qt::black); +} + +void NodeViewDelegate::renderGenvar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + renderVarCore(painter,data,option,size,Qt::blue); +} + +void NodeViewDelegate::renderLimit(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 4) + return; + + //The data + int val=data.at(2).toInt(); + int maxVal=data.at(3).toInt(); + QString name=data.at(1) + ":"; + QString valStr=QString::number(val) + "/" + QString::number(maxVal); + int totalVal=qMax(val,maxVal); //val can be larger than maxVal!! + + if(data.count() == 5) + name.prepend(data[4] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + QFontMetrics fm(attrFont_); + int itemOffset=0; + int itemWidth=0; + int itemHeight=0; + + if(limitShape_ == RectLimitShape) + { + itemWidth=fm.width('p')/2; + itemOffset=qMax(itemWidth/2,2); + itemHeight=static_cast(contRect.height())*0.8; + } + else + { + itemWidth=limitFillPix_.width(); + itemHeight=itemWidth; + itemOffset=0; + } + + //The text rectangle + QFont nameFont=attrFont_; + nameFont.setBold(true); + fm=QFontMetrics(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //The value rectangle + QFont valFont=attrFont_; + fm=QFontMetrics(valFont); + int valWidth=fm.width(valStr); + QRect valRect = nameRect; + valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); + valRect.setWidth(valWidth); + + int xItem=valRect.x()+valRect.width()+attrBox_->spacing; + int rightPos=xItem+totalVal*(itemWidth+itemOffset)+itemOffset; + + rightPos+=attrBox_->rightPadding + attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + + //Define clipping + const bool setClipRect = rightPos > option.rect.right(); + + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + //Draw value + if(val < maxVal) + painter->setPen(Qt::black); + else if(val == maxVal) + painter->setPen(QColor(14,148,26)); + else + painter->setPen(Qt::red); + + painter->setFont(valFont); + painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valStr); + + //Draw items + if(limitShape_ == RectLimitShape) + { + int yItem=option.rect.y()+(option.rect.height()-itemHeight)/2; + painter->setRenderHint(QPainter::Antialiasing,false); + + painter->setPen(Qt::NoPen); + painter->setBrush(limitFillBrush_); + + for(int i=0; i < totalVal; i++) + { + QRect r(xItem,yItem,itemWidth,itemHeight); + + if(i == val && i < maxVal) + { + painter->setPen(QPen(QColor(190,190,190),0)); + painter->setBrush(Qt::NoBrush); + } + else if( i == maxVal) + painter->setBrush(limitExtraFillBrush_); + + painter->drawRect(r); + + if(i < val) + { + painter->setPen(painter->brush().color().darker(120)); + painter->drawLine(xItem,yItem,xItem+itemWidth,yItem); + painter->drawLine(xItem,yItem,xItem,yItem+itemHeight); + + painter->setPen(painter->brush().color().darker(170)); + painter->drawLine(xItem+itemWidth,yItem,xItem+itemWidth,yItem+itemHeight); + painter->drawLine(xItem,yItem+itemHeight,xItem+itemWidth,yItem+itemHeight); + } + + xItem+=itemOffset+itemWidth; + } + } + else + { + int yItem=option.rect.y()+(option.rect.height()-itemHeight)/2; + for(int i=0; i < totalVal; i++) + { + if(i >= maxVal) + painter->drawPixmap(xItem,yItem,itemWidth,itemHeight,limitExtraFillPix_); + else if(i >= val) + painter->drawPixmap(xItem,yItem,itemWidth,itemHeight,limitEmptyPix_); + else + painter->drawPixmap(xItem,yItem,itemHeight,itemWidth,limitFillPix_); + + xItem+=itemOffset+itemHeight; + } + } + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderLimiter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 4) + return; + + QString name="inlimit " + data[2] +":" + data[1]; + if(data[3] != "1") + name+=" " + data[3]; + + if(data.count() == 5) + name.prepend(data[4] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The text rectangle + QFont nameFont=attrFont_; + //nameFont.setBold(true); + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderTrigger(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() !=3) + return; + + int triggerType=data[1].toInt(); + QString text=data.at(2); + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin, + 0,-attrBox_->bottomMargin); + + //The text rectangle + QFont font=attrFont_; + QFontMetrics fm(font); + int textWidth=fm.width(text)+4; + QRect textRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + textRect.setWidth(textWidth); + + //Define clipping + int rightPos=textRect.x()+textRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //draw rect + if(triggerType==0) + { + painter->setBrush(triggerBgBrush_); + painter->setPen(triggerBorderPen_); + } + else + { + painter->setBrush(completeBgBrush_); + painter->setPen(completeBorderPen_); + } + + //QRect borderRect=option.rect; + //borderRect.setWidth(rightPos-borderRect.x()); + painter->drawRect(attrBox_->adjustTextBgRect(textRect)); + + //Draw text + if(triggerType==0) + painter->setPen(triggerFontPen_); + else + painter->setPen(completeFontPen_); + + painter->setFont(font); + painter->drawText(attrBox_->adjustTextRect(textRect),Qt::AlignHCenter | Qt::AlignVCenter,text); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setX(textRect.x()); + sr.setWidth(textRect.width()); + //sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRectNonOpt(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderTime(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 2) + return; + + QString name=data[1]; + + if(data.count() == 3) + name.prepend(data[2] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The text rectangle + QFont nameFont=attrFont_; + //nameFont.setBold(true); + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderDate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 2) + return; + + QString name=data[1]; + + if(data.count() == 3) + name.prepend(data[2] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The text rectangle + QFont nameFont=attrFont_; + //nameFont.setBold(true); + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} + +//======================================================== +// data is encoded as a QStringList as follows: +// "repeat" name value +//======================================================== + +void NodeViewDelegate::renderRepeat(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 7) + return; + + QString type=data.at(1); + QString name=data.at(2); + QString val=data.at(3); + QString start=data.at(4); + QString end=data.at(5); + QString step=data.at(6); + + if(data.count() == 9) + name.prepend(data[8] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The contents rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + if(type == "day") + { + QFont nameFont=attrFont_; + QFontMetrics fm(nameFont); + name="day=" + step; + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + } + else + { + QString endDot; + if(start == val) + { + name+="="; + endDot="..."; + } + else if(end == val) + { + name+="=..."; + endDot=""; + } + else + { + name+="=..."; + endDot="..."; + } + + //The name rectangle + QFont nameFont=attrFont_; + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //The value rectangle + QFont valFont=attrFont_; + valFont.setBold(true); + fm=QFontMetrics(valFont); + int valWidth=fm.width(val); + QRect valRect = nameRect; + if(name.endsWith("...")) + valRect.setX(nameRect.x()+nameRect.width() + fm.width('A')/2); + else + valRect.setX(nameRect.x()+nameRect.width() + fm.width(' ')/2); + + valRect.setWidth(valWidth); + + int rightPos=valRect.x()+valRect.width(); + + //End ... + QRect dotRect; + if(!endDot.isEmpty()) + { + fm=QFontMetrics(nameFont); + int dotWidth=fm.width("..."); + dotRect = valRect; + dotRect.setX(rightPos+fm.width('A')/2); + dotRect.setWidth(dotWidth); + rightPos=dotRect.x()+dotRect.width(); + } + + //Define clipping + rightPos+=+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + //Draw value + painter->setPen(Qt::black); + painter->setFont(valFont); + painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,val); + + //Draw end dots + if(!endDot.isEmpty()) + { + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(dotRect),Qt::AlignLeft | Qt::AlignVCenter,"..."); + } + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + } + + size.setWidth(totalWidth); +} + +void NodeViewDelegate::renderLate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const +{ + int totalWidth=0; + + size=QSize(totalWidth,attrBox_->fullHeight); + + if(data.count() < 2) + return; + + QString name="late: " + data[1]; + + if(data.count() == 3) + name.prepend(data[2] + ":"); + + bool selected=option.state & QStyle::State_Selected; + + //The border rect (we will adjust its width) + QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); + + //The text rectangle + QFont nameFont=attrFont_; + QFontMetrics fm(nameFont); + int nameWidth=fm.width(name); + QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); + nameRect.setWidth(nameWidth); + + //Define clipping + int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; + totalWidth=rightPos-option.rect.x(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw name + painter->setPen(Qt::black); + painter->setFont(nameFont); + painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); + + if(selected && drawAttrSelectionRect_) + { + QRect sr=option.rect; + sr.setWidth(rightPos-sr.x()); + renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); + } + + if(setClipRect) + { + painter->restore(); + } + + size.setWidth(totalWidth); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeViewDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,195 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef NODEVIEWDELEGATE_HPP_ +#define NODEVIEWDELEGATE_HPP_ + +#include +#include +#include +#include +#include + +#include "FontMetrics.hpp" +#include "RectMetrics.hpp" +#include "VProperty.hpp" + +#include + +class PropertyMapper; + +//Base class for the node/attr renderer properties +struct BaseNodeDelegateBox +{ + BaseNodeDelegateBox() : fontHeight(10), height(10), fullHeight(10), topMargin(0), bottomMargin(0), + leftMargin(0), rightMargin(0), topPadding(0), bottomPadding(0), + leftPadding(0), rightPadding(0), sizeHintCache(QSize(10,10)), spacing(2), selectRm(2) {} + + virtual ~BaseNodeDelegateBox() {} + + virtual void adjust(const QFont& f)=0; + virtual QRect adjustTextRect(const QRect& rIn) const { QRect r=rIn; return r;} + virtual QRect adjustTextBgRect(const QRect& rIn) const { QRect r=rIn; return r;} + virtual QRect adjustSelectionRect(const QRect& optRect) const { + QRect r=optRect; + r.adjust(leftMargin,1,-1,-1); + return r; + } + virtual QRect adjustSelectionRectNonOpt(const QRect& optRect) const { + QRect r=optRect; + r.adjust(0,1,-1,-1); + return r; + } + + int fontHeight; + int height; + int fullHeight; + int topMargin; + int bottomMargin; + int leftMargin; + int rightMargin; + int topPadding; + int bottomPadding; + int leftPadding; + int rightPadding; + QSize sizeHintCache; + int spacing; + RectMetrics selectRm; +}; + +//Node renderer properties +struct NodeDelegateBox : public BaseNodeDelegateBox +{ + NodeDelegateBox() : iconSize(16), iconPreGap(2), + iconGap(2) {} + + int iconSize; + int iconPreGap; + int iconGap; + + void adjust(const QFont& f) { + QFontMetrics fm(f); + fontHeight=fm.height(); + height=fontHeight+topPadding+bottomPadding; + fullHeight=height+topMargin+bottomMargin; + sizeHintCache=QSize(100,fullHeight); + spacing=fm.width('A')*3/4; + + int h=static_cast(static_cast(fm.height())*0.7); + iconSize=h; + if(iconSize % 2 == 1) + iconSize+=1; + + iconGap=1; + if(iconSize > 16) + iconGap=2; + + iconPreGap=fm.width('A')/2; + } +}; + +//Attr renderer properties +struct AttrDelegateBox : public BaseNodeDelegateBox +{ + AttrDelegateBox() {} + + void adjust(const QFont& f) { + QFontMetrics fm(f); + fontHeight=fm.height(); + height=fontHeight+topPadding+bottomPadding; + fullHeight=height+topMargin+bottomMargin; + sizeHintCache=QSize(100,fullHeight); + spacing=fm.width('A')*3/4; + } +}; + +class NodeViewDelegate : public QStyledItemDelegate, public VPropertyObserver +{ +public: + explicit NodeViewDelegate(QWidget *parent=0); + ~NodeViewDelegate(); + + void notifyChange(VProperty*); + +protected: + virtual void updateSettings()=0; + void addBaseSettings(std::vector&); + void updateBaseSettings(); + + void renderSelectionRect(QPainter* painter,QRect r) const; + + virtual void renderStatus(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option) const; + + typedef void (NodeViewDelegate::*AttributeRendererProc)(QPainter *painter,QStringList data,const QStyleOptionViewItem& optio,QSize&) const; + + virtual void renderMeter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderLabel(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderEvent(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderVar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderGenvar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderLimit(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderLimiter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderTrigger(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderTime(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderDate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderRepeat(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + virtual void renderLate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; + + void labelSize(QStringList data,int& totalWidth,int& totalHeight) const; + int labelHeight(int) const; + void renderVarCore(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&,QColor) const; + + QPen hoverPen_; + QBrush hoverBrush_; + QPen selectPen_; + QBrush selectBrush_; + QPen nodePen_; + QPen nodeSelectPen_; + QPixmap errPix_; + + QBrush lostConnectBgBrush_; + QBrush lostConnectBandBrush_; + + QMap attrRenderers_; + + PropertyMapper* prop_; + QFont font_; + QFont attrFont_; + NodeDelegateBox* nodeBox_; + AttrDelegateBox* attrBox_; + + bool useStateGrad_; + mutable QLinearGradient grad_; + static int lighter_; + bool drawAttrSelectionRect_; + QBrush eventFillBrush_; + QBrush eventBgBrush_; + QBrush meterFillBrush_; + QBrush meterThresholdBrush_; + QBrush limitFillBrush_; + QBrush limitExtraFillBrush_; + QPixmap limitFillPix_; + QPixmap limitEmptyPix_; + QPixmap limitExtraFillPix_; + enum LimitShape {RectLimitShape,CircleLimitShape}; + LimitShape limitShape_; + QBrush triggerBgBrush_; + QPen triggerBorderPen_; + QPen triggerFontPen_; + QBrush completeBgBrush_; + QPen completeBorderPen_; + QPen completeFontPen_; +}; + +#endif + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,185 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#include "NodeWidget.hpp" +#include "NodeViewBase.hpp" + +#include "AbstractNodeModel.hpp" +#include "InfoPanelHandler.hpp" +#include "VFilter.hpp" +#include "VModelData.hpp" + +#include +#include + +NodeWidget::NodeWidget(const std::string& type,ServerFilter* serverFilter,QWidget* parent) : + DashboardWidget(type,parent), + serverFilter_(serverFilter), + model_(0), + view_(0), + icons_(0), + atts_(0), + filterDef_(0), + states_(0), + broadcastSelection_(true) +{ + //Define the icon filter for the model. It controls what icons + //are displayed next to the nodes. This is exposed via a menu. + icons_=new IconFilter; + + //Define the attribute filter for the model. It controls what attributes + //are displayed for a given node. This is exposed via a menu. + atts_=new AttributeFilter; + + createActions(); +} + +NodeWidget::~NodeWidget() +{ + //We only need to delete the non-qobject members + delete icons_; + delete atts_; + + if(filterDef_) + delete filterDef_; +} + +QWidget* NodeWidget::widget() +{ + return view_->realWidget(); +} + +VInfo_ptr NodeWidget::currentSelection() +{ + return view_->currentSelection(); +} + +/*void NodeWidget::currentSelection(VInfo_ptr info) +{ + view_->currentSelection(info); +}*/ + +void NodeWidget::setCurrentSelection(VInfo_ptr info) +{ + if(!detached() && info) + { + //We need to avoid inifinite recursion. This could happen like this: + //1. Select node in panel A + //2. We broadcast the selection to panel B + //3. Panel B selects the node + //4 Panel B then brodcasts the selection back to panel A + // ... + //So here we need to check if the given item is already selected!! + VInfo_ptr csInfo=currentSelection(); + if(csInfo && info && *(csInfo.get()) == *(info.get())) + return; + + //This will broadcast the selection!!! + view_->setCurrentSelection(info); + } +} + +void NodeWidget::slotSelectionChangedInBc(VInfo_ptr info) +{ + view_->setCurrentSelection(info); +} + +void NodeWidget::reload() +{ + active(true); + //model_->reload(); +} + +void NodeWidget::active(bool b) +{ + model_->active(b); + view_->realWidget()->setEnabled(b); +} + +bool NodeWidget::active() const +{ + return model_->active(); +} + +void NodeWidget::createActions() +{ + /*QAction* infoAc=new QAction(" ",this); + QPixmap pix(":/viewer/dock_info.svg"); + infoAc->setIcon(QIcon(pix)); + infoAc->setToolTip(tr("Start up information panel as dialog")); + dockActions_ << infoAc; + dockActionMap_["info"]=infoAc; + + QMenu *menu=new QMenu(this); + infoAc->setMenu(menu); + + for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); + it != InfoPanelHandler::instance()->panels().end(); it++) + { + if((*it)->show().find("toolbar") != std::string::npos) + { + QAction *ac=new QAction(QString::fromStdString((*it)->label()) + "...",this); + //QPixmap pix(":/viewer/" + QString::fromStdString((*it)->dockIcon())); + QPixmap pix(":/viewer/" + QString::fromStdString((*it)->icon())); + ac->setIcon(QIcon(pix)); + ac->setData(QString::fromStdString((*it)->name())); + ac->setEnabled(false); + + connect(ac,SIGNAL(triggered()), + this,SLOT(slotInfoPanelAction())); + + infoPanelActions_ << ac; + } + }*/ +} + +void NodeWidget::slotInfoPanelAction() +{ + /*if(QAction* ac=static_cast(sender())) + { + Q_EMIT popInfoPanel(view_->currentSelection(),ac->data().toString()); + }*/ +} + +void NodeWidget::updateActionState(VInfo_ptr info) +{ + /* std::vector ids; + InfoPanelHandler::instance()->visible(info,ids); + + QAction *infoAc=dockActionMap_["info"]; + assert(infoAc); + + QMenu* menu=infoAc->menu(); + assert(menu); + + menu->clear(); + + Q_FOREACH(QAction* ac,infoPanelActions_) + { + ac->setEnabled(false); + + std::string name=ac->data().toString().toStdString(); + + for(std::vector::const_iterator it=ids.begin(); it != ids.end(); it++) + { + if((*it)->name() == name) + { + ac->setEnabled(true); + menu->addAction(ac); + break; + } + } + }*/ +} + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/NodeWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/NodeWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/NodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/NodeWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,81 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#ifndef NODEWIDGET_HPP_ +#define NODEWIDGET_HPP_ + +#include +#include + +#include "DashboardWidget.hpp" +#include "Viewer.hpp" +#include "VInfo.hpp" + +class QStackedLayout; +class QWidget; + +class AbstractNodeModel; +class AttributeFilter; +class IconFilter; +class NodeFilterDef; +class NodeStateFilter; +class VModelData; +class NodePathWidget; +class NodeViewBase; +class ServerFilter; + +class NodeWidget : public DashboardWidget +{ +Q_OBJECT + +public: + void active(bool); + bool active() const; + NodeViewBase* view() const {return view_;} + QWidget* widget(); + VInfo_ptr currentSelection(); + //void currentSelection(VInfo_ptr info); + void reload(); + void populateDialog() {} + QList dockTitleActions() {return dockActions_;} + +public Q_SLOTS: + void setCurrentSelection(VInfo_ptr); + +protected Q_SLOTS: + void slotInfoPanelAction(); + void slotSelectionChangedInBc(VInfo_ptr info); + +protected: + explicit NodeWidget(const std::string& type,ServerFilter* serverFilter,QWidget* parent=0); + virtual ~NodeWidget(); + + void updateActionState(VInfo_ptr); + bool broadcastSelection() const {return broadcastSelection_;} + + ServerFilter* serverFilter_; + + AbstractNodeModel* model_; + NodeViewBase* view_; + + IconFilter* icons_; + AttributeFilter* atts_; + + NodeFilterDef* filterDef_; + NodeStateFilter *states_; + +private: + void createActions(); + QList dockActions_; + QMap dockActionMap_; + QList infoPanelActions_; + bool broadcastSelection_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OneLineTextEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OneLineTextEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OneLineTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OneLineTextEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,67 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OneLineTextEdit.hpp" + +#include "Highlighter.hpp" + +#include +#include +#include +#include + +OneLineTextEdit::OneLineTextEdit(QWidget* parent) : QTextEdit(parent) +{ + setReadOnly(true); + setWordWrapMode(QTextOption::NoWrap); + setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); + + document()->setDocumentMargin(2); + + QFont f; + QFontMetrics fm(f); + + int h=fm.height()+fm.leading() + 1 +6; + + setFixedHeight(h); + + //The document becomes the owner of the highlighter + new Highlighter(document(),"query"); +} + +QSize OneLineTextEdit::sizeHint() const +{ + return QTextEdit::sizeHint(); + /* + QFontMetrics fm(font()); + QStyleOptionFrameV3 opt; + QString text = document()->toHtml(); + + int h = qMax(fm.height(), 14) + 4; + int w = fm.width(text) + 4; + + opt.initFrom(this); + + return style()->sizeFromContents( + QStyle::CT_LineEdit, + &opt, + QSize(w, h).expandedTo(QApplication::globalStrut()), + this + );*/ +} + +void OneLineTextEdit::mousePressEvent(QMouseEvent *e) +{ + Q_EMIT clicked(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OneLineTextEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OneLineTextEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OneLineTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OneLineTextEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,33 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_ONELINETEXTEDIT_HPP_ +#define VIEWER_SRC_ONELINETEXTEDIT_HPP_ + +#include + +class OneLineTextEdit : public QTextEdit +{ +Q_OBJECT + +public: + OneLineTextEdit(QWidget* parent=0); + QSize sizeHint() const; + +Q_SIGNALS: + void clicked(); + +protected: + void mousePressEvent(QMouseEvent *e); +}; + + + +#endif /* VIEWER_SRC_ONELINETEXTEDIT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputBrowser.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputBrowser.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputBrowser.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputBrowser.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,574 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OutputBrowser.hpp" + +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +#include "Highlighter.hpp" + +#include "MessageLabel.hpp" +#include "HtmlEdit.hpp" +#include "PlainTextEdit.hpp" +#include "PlainTextSearchInterface.hpp" +#include "TextEditSearchLine.hpp" +#include "TextPager/TextPagerSearchInterface.hpp" +#include "TextPagerWidget.hpp" +#include "DirectoryHandler.hpp" +#include "TextFilterWidget.hpp" +#include "UserMessage.hpp" +#include "UiLog.hpp" + +int OutputBrowser::minPagerTextSize_=1*1024*1024; +int OutputBrowser::minPagerSparseSize_=30*1024*1024; +int OutputBrowser::minConfirmSearchSize_=5*1024*1024; + +OutputBrowser::OutputBrowser(QWidget* parent) : + QWidget(parent), + searchTb_(0) +{ + QVBoxLayout *vb=new QVBoxLayout(this); + vb->setContentsMargins(0,0,0,0); + vb->setSpacing(2); + + //Text filter editor + textFilter_=new TextFilterWidget(this); + vb->addWidget(textFilter_); + + connect(textFilter_,SIGNAL(runRequested(QString,bool,bool)), + this,SLOT(slotRunFilter(QString,bool,bool))); + + connect(textFilter_,SIGNAL(clearRequested()), + this,SLOT(slotRemoveFilter())); + + connect(textFilter_,SIGNAL(closeRequested()), + this,SLOT(slotRemoveFilter())); + + stacked_=new QStackedWidget(this); + vb->addWidget(stacked_,1); + + confirmSearchLabel_=new MessageLabel(this); + confirmSearchLabel_->setShowTypeTitle(false); + confirmSearchLabel_->setNarrowMode(true); + vb->addWidget(confirmSearchLabel_); + + searchLine_=new TextEditSearchLine(this); + vb->addWidget(searchLine_); + + //Basic textedit + textEdit_=new PlainTextEdit(this); + textEdit_->setReadOnly(true); + textEdit_->setWordWrapMode(QTextOption::NoWrap); + textEdit_->setShowLineNumbers(false); + + textEditSearchInterface_=new PlainTextSearchInterface(); + textEditSearchInterface_->setEditor(textEdit_); + + //This highlighter only works for jobs + jobHighlighter_=new Highlighter(textEdit_->document(),"job"); + jobHighlighter_->setDocument(NULL); + + //Pager for very large files + textPager_=new TextPagerWidget(this); + textPager_->textEditor()->setShowLineNumbers(false); + + //textEdit_->setReadOnly(true); + + textPagerSearchInterface_=new TextPagerSearchInterface(); + textPagerSearchInterface_->setEditor(textPager_->textEditor()); + + //Html browser for html files + htmlEdit_=new HtmlEdit(this); + + stacked_->addWidget(textEdit_); + stacked_->addWidget(textPager_); + stacked_->addWidget(htmlEdit_); + + stacked_->setCurrentIndex(BasicIndex); + searchLine_->hide(); + + connect(searchLine_,SIGNAL(visibilityChanged()), + this,SLOT(showConfirmSearchLabel())); + + //the textfilter is is hidden by default + textFilter_->hide(); +} + +OutputBrowser::~OutputBrowser() +{ + delete textEditSearchInterface_; + delete textPagerSearchInterface_; + + if(jobHighlighter_ && !jobHighlighter_->parent()) + { + delete jobHighlighter_; + } +} + +void OutputBrowser::setSearchButtons(QToolButton* searchTb) +{ + searchTb_=searchTb; +} + +void OutputBrowser::setFilterButtons(QToolButton* statusTb,QToolButton* optionTb) +{ + textFilter_->setExternalButtons(statusTb,optionTb); +} + +void OutputBrowser::clear() +{ + textEdit_->clear(); + textPager_->clear(); + htmlEdit_->clear(); + file_.reset(); + oriFile_.reset(); +} + +void OutputBrowser::changeIndex(IndexType indexType,qint64 fileSize) +{ + if(indexType == BasicIndex) + { + stacked_->setCurrentIndex(indexType); + textPager_->clear(); + htmlEdit_->clear(); + + //enable and init search + if(searchTb_) searchTb_->setEnabled(true); + searchLine_->setConfirmSearch(false); + searchLine_->setSearchInterface(textEditSearchInterface_); + + //enable filter + textFilter_->setEnabledExternalButtons(true); + } + else if(indexType == PagerIndex) + { + stacked_->setCurrentIndex(indexType); + textEdit_->clear(); + htmlEdit_->clear(); + + //enable and init search + if(searchTb_) searchTb_->setEnabled(true); + searchLine_->setConfirmSearch(fileSize >=minConfirmSearchSize_); + searchLine_->setSearchInterface(textPagerSearchInterface_); + + //enable filter + textFilter_->setEnabledExternalButtons(true); + } + else if(indexType == HtmlIndex) + { + stacked_->setCurrentIndex(indexType); + textPager_->clear(); + textEdit_->clear(); + if(oriFile_) + oriFile_.reset(); + + //Disable search + if(searchTb_) searchTb_->setEnabled(false); + searchLine_->setSearchInterface(0); + searchLine_->hide(); + + //Disable filter + textFilter_->closeIt(); + textFilter_->setEnabledExternalButtons(false); + } + + showConfirmSearchLabel(); +} + +//This should only be called externally when a new output is loaded +void OutputBrowser::loadFile(VFile_ptr file) +{ + if(!file) + { + clear(); + return; + } + + file_=file; + if(file_->storageMode() == VFile::DiskStorage) + { + loadFile(QString::fromStdString(file_->path())); + } + else + { + QString s(file_->data()); + loadText(s,QString::fromStdString(file_->sourcePath()),true); + } + + //Run the filter if defined + if(textFilter_->isActive()) + { + slotRunFilter(textFilter_->filterText(),textFilter_->isMatched(), + textFilter_->isCaseSensitive()); + } +} + +void OutputBrowser::loadFilteredFile(VFile_ptr file) +{ + if(!file) + { + clear(); + return; + } + + file_=file; + Q_ASSERT(file_->storageMode() == VFile::DiskStorage); + loadFile(QString::fromStdString(file_->path())); +} + +void OutputBrowser::loadFile(QString fileName) +{ + QFile file(fileName); + file.open(QIODevice::ReadOnly); + QFileInfo fInfo(file); + qint64 fSize=fInfo.size(); + + if(isHtmlFile(fileName)) + { + changeIndex(HtmlIndex,fSize); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + QString str=file.readAll(); + htmlEdit_->document()->setHtml(str); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + + } + else if(!isJobFile(fileName) && fSize >= minPagerTextSize_) + { + changeIndex(PagerIndex,fSize); + + TextPagerDocument::DeviceMode mode=(fSize >= minPagerSparseSize_)? + TextPagerDocument::Sparse:TextPagerDocument::LoadAll; + textPager_->load(fileName, mode); + } + else + { + changeIndex(BasicIndex,fSize); + + adjustHighlighter(fileName); + + QString str=file.readAll(); + textEdit_->document()->setPlainText(str); + } +} + +void OutputBrowser::loadText(QString txt,QString fileName,bool resetFile) +{ + // prior to the ability to save local copies of files, we reset the file_ member here; + // but now we need to keep it so that we can save a copy of it + //if(resetFile) + //file_.reset(); + + //We estimate the size in bytes + qint64 txtSize=txt.size()*2; + + if(isHtmlFile(fileName)) + { + changeIndex(HtmlIndex,txtSize); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + htmlEdit_->document()->setHtml(txt); +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + } + else if(!isJobFile(fileName) && txtSize > minPagerTextSize_) + { + changeIndex(PagerIndex,txtSize); + textPager_->setText(txt); + } + else + { + changeIndex(BasicIndex,txtSize); + adjustHighlighter(fileName); + textEdit_->document()->setPlainText(txt); + } + + //Set the cursor position from the cache + //updateCursorFromCache(fileName.toStdString()); +} + +void OutputBrowser::saveCurrentFile(QString &fileNameToSaveTo) +{ + assert(file_); + + if (file_->storageMode() == VFile::DiskStorage) + { + // if we have the file on disk, then just copy it to the new location + std::string to = fileNameToSaveTo.toStdString(); + std::string errorMessage; + bool ok = DirectoryHandler::copyFile(file_->path(), to, errorMessage); + if (!ok) + { + UserMessage::message(UserMessage::ERROR,true,"Failed to copy file. Reason:\n " + errorMessage); + } + } + else + { + // file in memory - just dump it to file + QFile file(fileNameToSaveTo); + if (file.open(QFile::WriteOnly | QFile::Text)) + { + QTextStream out(&file); + QString s(file_->data()); + out << s; + } + else + { + UserMessage::message(UserMessage::ERROR,true,"Failed to save file to " + fileNameToSaveTo.toStdString()); + } + } +} + +bool OutputBrowser::isFileLoaded() +{ + return (file_ != 0); +} + +bool OutputBrowser::isJobFile(QString fileName) +{ + return fileName.contains(".job"); +} + +bool OutputBrowser::isHtmlFile(QString fileName) +{ + return fileName.endsWith(".html"); +} + +void OutputBrowser::adjustHighlighter(QString fileName) +{ + //For job files we set the proper highlighter + if(isJobFile(fileName)) + { + if(!jobHighlighter_) + { + jobHighlighter_=new Highlighter(textEdit_->document(),"job"); + } + else if(jobHighlighter_->document() != textEdit_->document()) + { + jobHighlighter_->setDocument(textEdit_->document()); + } + } + else if(jobHighlighter_) + { + jobHighlighter_->setDocument(NULL); + } +} + +void OutputBrowser::gotoLine() +{ + if(stacked_->currentIndex() == BasicIndex) + textEdit_->gotoLine(); + else if(stacked_->currentIndex() == PagerIndex) + textPager_->gotoLine(); +} + +void OutputBrowser::showSearchLine() +{ + if(searchLine_->hasInterface()) + { + searchLine_->setVisible(true); + searchLine_->setFocus(); + searchLine_->selectAll(); + } +} + +void OutputBrowser::searchOnReload(bool userClickedReload) +{ + if(searchLine_->hasInterface()) + { + searchLine_->searchOnReload(userClickedReload); + } +} + +void OutputBrowser::setFontProperty(VProperty* p) +{ + textEdit_->setFontProperty(p); + textPager_->setFontProperty(p); + htmlEdit_->setFontProperty(p); +} + +void OutputBrowser::updateFont() +{ + textEdit_->updateFont(); +} + +void OutputBrowser::zoomIn() +{ + textEdit_->slotZoomIn(); + textPager_->zoomIn(); + htmlEdit_->zoomIn(); +} + +void OutputBrowser::zoomOut() +{ + textEdit_->slotZoomOut(); + textPager_->zoomOut(); + htmlEdit_->zoomOut(); +} + +void OutputBrowser::showConfirmSearchLabel() +{ + if(searchLine_->isVisible() && searchLine_->confirmSearch()) + { + confirmSearchLabel_->showWarning(searchLine_->confirmSearchText()); + //confirmSearchLabel_->show(); + } + else + { + confirmSearchLabel_->hide(); + } +} + +void OutputBrowser::setCursorPos(qint64 pos) +{ + if(stacked_->currentIndex() == BasicIndex) + { + QTextCursor c=textEdit_->textCursor(); + c.setPosition(pos); + textEdit_->setTextCursor(c); + } + else if(stacked_->currentIndex() == PagerIndex) + { + textPager_->textEditor()->setCursorPosition(pos); + } +} + +void OutputBrowser::slotRunFilter(QString filter,bool matched,bool caseSensitive) +{ + if(stacked_->currentIndex() == HtmlIndex) + return; + + if(!file_) + return; + + if(oriFile_) + file_=oriFile_; + + VFile_ptr fTarget=VFile::createTmpFile(true); + VFile_ptr fSrc=VFile_ptr(); + + // file is on disk - we use it as it is + if(file_->storageMode() == VFile::DiskStorage) + { + fSrc=file_; + } + // file in memory - just dump it to the tmp file + else + { + fSrc=VFile::createTmpFile(true); + QFile file(QString::fromStdString(fSrc->path())); + if(file.open(QFile::WriteOnly | QFile::Text)) + { + QTextStream out(&file); + QString s(file_->data()); + out << s; + } + else + { + } + } + + //At this point point fSrc must contain the text to filter + QProcess proc; + proc.setStandardOutputFile(QString::fromStdString(fTarget->path())); + + //Run grep to filter fSrc into fTarget + QString extraOptions; + if(!matched) + extraOptions+=" -v "; + if(!caseSensitive) + extraOptions+=" -i "; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + + proc.start("/bin/sh", + QStringList() << "-c" << "grep " + extraOptions + " -e \'" + filter + "\' " + + QString::fromStdString(fSrc->path())); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + UiLog().dbg() << "args=" << proc.arguments().join(" "); +#endif + + if(!proc.waitForStarted(1000)) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + QString errStr; + UI_FUNCTION_LOG +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + errStr="Failed to start text filter using command:
      \'" + + proc.program() + " " + proc.arguments().join(" ") + "\'"; +#else + errStr="Failed to start grep command!"; +#endif + UserMessage::message(UserMessage::ERROR,true,errStr.toStdString()); + fTarget.reset(); + return; + } + + proc.waitForFinished(60000); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + + QString errStr=proc.readAllStandardError(); + if(proc.exitStatus() == QProcess::NormalExit && errStr.isEmpty()) + { + oriFile_=file_; + textFilter_->setStatus(fTarget->isEmpty()?(TextFilterWidget::NotFoundStatus):(TextFilterWidget::FoundStatus)); + loadFilteredFile(fTarget); + } + else + { + QString msg; + UI_FUNCTION_LOG +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + msg="Failed to filter output file using command:
      \'" + + proc.program() + " " + proc.arguments().join(" ") + "\'"; +#else + msg="Failed to run grep command!"; +#endif + if(!errStr.isEmpty()) + msg+="
      Error message: " + errStr; + + UserMessage::message(UserMessage::ERROR,true,msg.toStdString()); + textFilter_->setStatus(TextFilterWidget::NotFoundStatus); + fTarget.reset(); //delete + } + + return; +} + +void OutputBrowser::slotRemoveFilter() +{ + if(stacked_->currentIndex() == HtmlIndex) + return; + + if(oriFile_) + { + loadFile(oriFile_); + oriFile_.reset(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputBrowser.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputBrowser.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputBrowser.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputBrowser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,104 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_OUTPUTBROWSER_HPP_ +#define VIEWER_SRC_OUTPUTBROWSER_HPP_ + +#include +#include +#include + +#include "VFile.hpp" + +class Highlighter; +class MessageLabel; +class TextEditSearchLine; +class PlainTextEdit; +class PlainTextSearchInterface; +class TextPagerWidget; +class TextPagerSearchInterface; +class VProperty; +class TextFilterWidget; +class QToolButton; +class HtmlEdit; + +class OutputBrowser; + +class CursorCacheItem +{ + friend class OutputBrowser; +public: + CursorCacheItem() : pos_(0), verticalScrollValue_(0), viewportHeight_(0) {} +protected: + int pos_; + int verticalScrollValue_; + int viewportHeight_; +}; + +class OutputBrowser : public QWidget +{ +Q_OBJECT + +public: + explicit OutputBrowser(QWidget* parent); + ~OutputBrowser(); + + void clear(); + void loadFile(VFile_ptr file); + bool isFileLoaded(); + void saveCurrentFile(QString &fileNameToSaveTo); + void adjustHighlighter(QString fileName); + void setFontProperty(VProperty* p); + void updateFont(); + void gotoLine(); + void showSearchLine(); + void searchOnReload(bool userClickedReload); + void zoomIn(); + void zoomOut(); + void setSearchButtons(QToolButton* searchTb); + void setFilterButtons(QToolButton* statusTb,QToolButton* optionTb); + +protected Q_SLOTS: + void showConfirmSearchLabel(); + void slotRunFilter(QString,bool,bool); + void slotRemoveFilter(); + +private: + enum IndexType {BasicIndex=0,PagerIndex=1,HtmlIndex=2}; + void changeIndex(IndexType indexType,qint64 fileSize); + bool isJobFile(QString fileName); + bool isHtmlFile(QString fileName); + void loadFile(QString fileName); + void loadText(QString text,QString fileName,bool resetFile=true); + void loadFilteredFile(VFile_ptr file); + void setCursorPos(qint64 pos); + + QStackedWidget *stacked_; + PlainTextEdit* textEdit_; + TextPagerWidget* textPager_; + HtmlEdit* htmlEdit_; + TextEditSearchLine* searchLine_; + TextFilterWidget* textFilter_; + Highlighter* jobHighlighter_; + PlainTextSearchInterface *textEditSearchInterface_; + TextPagerSearchInterface *textPagerSearchInterface_; + MessageLabel *confirmSearchLabel_; + QToolButton* searchTb_; + + //we keep a reference to it to make sure that it does not get deleted while + //it is being displayed + VFile_ptr file_; + VFile_ptr oriFile_; + + static int minPagerTextSize_; + static int minPagerSparseSize_; + static int minConfirmSearchSize_; +}; + +#endif /* VIEWER_SRC_OUTPUTBROWSER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputCache.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputCache.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputCache.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputCache.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,234 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OutputCache.hpp" + +#include + +#include "UiLog.hpp" + +#define _UI_OUTPUTCACHE_DEBUG + +OutputCacheItem::OutputCacheItem(QString id, VFile_ptr file) : + id_(id), file_(file), used_(false) +{ +} + +bool OutputCacheItem::isAttached() const +{ + return used_; +} + +void OutputCacheItem::attach() +{ + used_=true; +} + +void OutputCacheItem::detach() +{ + used_=false; + inTimeOut_.start(); +} + +std::ostream& operator<<(std::ostream& stream,const OutputCacheItem& item) +{ + stream << "id:" << item.id_.toStdString() << " tmp:" << + item.file_->path() << " countdown:" << + (!item.isAttached()); + return stream; +} + +//======================================================== +// +// OutputCache +// +//======================================================== + +OutputCache::OutputCache(QObject *parent) : + QObject(parent), + timeOut_(1000*15), //we check the state every 15 sec + maxAttachedPeriod_(1000*120) //cache retention period is 2 min +{ + timer_=new QTimer(this); + connect(timer_,SIGNAL(timeout()), + this,SLOT(slotTimeOut())); +} + +OutputCache::~OutputCache() +{ + clear(); +} + +void OutputCache::adjustTimer() +{ + if(items_.isEmpty()) + stopTimer(); + else + startTimer(); +} + +void OutputCache::startTimer() +{ + if(!timer_->isActive()) + timer_->start(timeOut_); +} + +void OutputCache::stopTimer() +{ + timer_->stop(); +} + +void OutputCache::clear() +{ + Q_FOREACH(OutputCacheItem* item,items_.values()) + { + delete item; + } + stopTimer(); +} + +OutputCacheItem* OutputCache::add(VInfo_ptr info,const std::string& sourcePath,VFile_ptr file) +{ + if(!file) + return NULL; + +#ifdef _UI_OUTPUTCACHE_DEBUG + UI_FUNCTION_LOG + file->print(); + //print(); +#endif + + if(info && file && info->isNode() && info->server()) + { + //The key we would store for the item in the map + QString id=QString::fromStdString(info->path() + ":" + sourcePath); + + //The item exists + QMap::iterator it = items_.find(id); + if(it != items_.end()) + { + it.value()->attach(); + it.value()->file_=file; + startTimer(); + return it.value(); + } + //The item not yet exists + else + { + OutputCacheItem* item=new OutputCacheItem(id,file); + items_[id]=item; + item->attach(); +#ifdef _UI_OUTPUTCACHE_DEBUG + UiLog().dbg() << " add " << *item; + //print(); +#endif + startTimer(); + return item; + } + } + + return NULL; +} + +//Attach one item and detach all the others +OutputCacheItem* OutputCache::attachOne(VInfo_ptr info,const std::string& fileName) +{ + OutputCacheItem* attachedItem=0; + if(info && info->isNode() && info->server()) + { + QString inPath=QString::fromStdString(info->path() + ":"); + QString inFileName=QString::fromStdString(fileName); + + QMap::iterator it=items_.begin(); + while (it != items_.end() ) + { + if(it.key().startsWith(inPath) && it.key().endsWith(inFileName)) + { + it.value()->attach(); + attachedItem=it.value(); + } + else + { + it.value()->detach(); + } + ++it; + } + } + adjustTimer(); + return attachedItem; +} + +//Detach all the items +void OutputCache::detach() +{ + QMap::iterator it=items_.begin(); + while(it != items_.end()) + { +#ifdef _UI_OUTPUTCACHE_DEBUG + UI_FUNCTION_LOG + //print(); + UiLog().dbg() << " detach -> " << *(it.value()); +#endif + it.value()->detach(); + Q_ASSERT(!it.value()->isAttached()); + +#ifdef _UI_OUTPUTCACHE_DEBUG + //print(); + UiLog().dbg() << " detached -> " << *(it.value()); +#endif + ++it; + } + + adjustTimer(); +} + +void OutputCache::slotTimeOut() +{ +#ifdef _UI_OUTPUTCACHE_DEBUG +UI_FUNCTION_LOG +#endif + + QMap::iterator it=items_.begin(); + while(it != items_.end() ) + { + OutputCacheItem* item=it.value(); + if(!item->isAttached() && + item->inTimeOut_.elapsed() > maxAttachedPeriod_) + { +#ifdef _UI_OUTPUTCACHE_DEBUG + UiLog().dbg() << " remove " << it.value(); + UiLog().dbg() << " -> ref_count:" << it.value()->file_.use_count(); +#endif + it=items_.erase(it); + delete item; + //item->deleteLater(); +#ifdef _UI_OUTPUTCACHE_DEBUG + print(); +#endif + } + else + { + ++it; + } + } + + adjustTimer(); +} + +void OutputCache::print() +{ + UI_FUNCTION_LOG + QMap::iterator it = items_.begin(); + while (it != items_.end() ) + { + UiLog().dbg() << *(it.value()); + ++it; + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputCache.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputCache.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputCache.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputCache.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,79 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef OUTPUTCHACHE_HPP_ +#define OUTPUTCHACHE_HPP_ + +#include +#include +#include +#include + +#include + +#include "OutputClient.hpp" +#include "VFile.hpp" +#include "VInfo.hpp" + + +class OutputCache; + +struct OutputCacheItem +{ + friend class OutputCache; + +public: + OutputCacheItem(QString id,VFile_ptr file); + VFile_ptr file() const {return file_;} + bool isAttached() const; + friend std::ostream& operator<<(std::ostream& stream,const OutputCacheItem& item); + +protected: + void attach(); + void detach(); + + QString id_; + VFile_ptr file_; + bool used_; + QTime inTimeOut_; +}; + +class OutputCache: public QObject +{ + Q_OBJECT + +public: + OutputCache(QObject* parent=0); + ~OutputCache(); + + OutputCacheItem* add(VInfo_ptr info,const std::string& sourcePath,VFile_ptr file); + OutputCacheItem* attachOne(VInfo_ptr info,const std::string& fileName); + void detach(); + void clear(); + void print(); + +protected Q_SLOTS: + void slotTimeOut(); + +private: + OutputCache(const OutputClient&); + OutputCache& operator=(const OutputCache&); + void adjustTimer(); + void startTimer(); + void stopTimer(); + + QMap items_; + int timeOut_; + int maxAttachedPeriod_; + QTimer* timer_; +}; + +#endif // OUTPUTCHACHE_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputClient.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputClient.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputClient.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputClient.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,63 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OutputClient.hpp" + +#include + +OutputClient::OutputClient(const std::string& host,const std::string& portStr,QObject* parent) : + QObject(parent), + soc_(NULL), + host_(host), + portStr_(portStr), + port_(19999), + timeout_(3000) +{ + if(!portStr_.empty()) + port_=atoi(portStr.c_str()); + + soc_=new QTcpSocket(0); + connect(soc_,SIGNAL(readyRead()), + this, SLOT(slotRead())); + + connect(soc_,SIGNAL(error(QAbstractSocket::SocketError)), + this,SLOT(slotError(QAbstractSocket::SocketError))); + + connect(soc_,SIGNAL(connected()), + this, SLOT(slotConnected())); + + if(char* timeoutStr = getenv ("ECFLOWVIEW_LOGTIMEOUT")) + timeout_ = atoi(timeoutStr)*1000; +} + +OutputClient::~OutputClient() +{ + soc_->abort(); +} + + +void OutputClient::connectToHost(std::string host,int port) +{ + stopper_.start(); + soc_->abort(); + soc_->connectToHost(QString::fromStdString(host),port); + + //We cannot change the timeout through the qt api so we need this hack. + QTimer::singleShot(timeout_, this, SLOT(slotCheckTimeout())); +} + +void OutputClient::slotCheckTimeout() +{ + if(soc_->state() == QAbstractSocket::HostLookupState || + soc_->state() == QAbstractSocket::ConnectingState) + { + soc_->abort(); + Q_EMIT error("Timeout error"); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputClient.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputClient.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputClient.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputClient.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,63 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_OUTPUTCLIENT_HPP_ +#define VIEWER_SRC_OUTPUTCLIENT_HPP_ + +#include +#include +#include + +class QTcpSocket; + +class OutputClient : public QObject +{ + Q_OBJECT + +public: + OutputClient(const std::string& host,const std::string& port,QObject *parent); + ~OutputClient(); + + const std::string& host() const {return host_;} + int port() const {return port_;} + const std::string& portStr() const {return portStr_;} + + const std::string& remoteFile() const {return remoteFile_;} + bool ok() const { return soc_ != NULL; } + +protected Q_SLOTS: + virtual void slotError(QAbstractSocket::SocketError err)=0; + virtual void slotRead()=0; + virtual void slotConnected()=0; + void slotCheckTimeout(); + +Q_SIGNALS: + void error(QString); + void progress(QString,int); + void finished(); + +protected: + void connectToHost(std::string,int); + + QTcpSocket* soc_; + std::string host_; + std::string portStr_; + int port_; + int timeout_; + std::string remoteFile_; + QTime stopper_; + +private: + OutputClient(const OutputClient&); + OutputClient& operator=(const OutputClient&); +}; + + +#endif /* VIEWER_SRC_OUTPUTCLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirClient.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirClient.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirClient.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirClient.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,170 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OutputDirClient.hpp" + +#include "UiLog.hpp" + +#include +#include + +#define _UI_OUTPUTDIRCLIENT_DEBUG + +OutputDirClient::OutputDirClient(const std::string& host,const std::string& portStr,QObject* parent) : + OutputClient(host,portStr,parent) +{ +} + +VDir_ptr OutputDirClient::result() const +{ + return dir_; +} + +void OutputDirClient::slotCheckTimeout() +{ + if(soc_->state() == QAbstractSocket::HostLookupState || + soc_->state() == QAbstractSocket::ConnectingState) + { + soc_->abort(); + if(dir_) + dir_.reset(); + + Q_EMIT error("Timeout error"); + } +} + +void OutputDirClient::slotConnected() +{ + UiLog().dbg() << "OutputDirClient::slotConnected() connected to " << + soc_->peerName(); + + soc_->write("list ",5); + soc_->write(remoteFile_.c_str(),remoteFile_.size()); + soc_->write("\n",1); +} + +void OutputDirClient::slotError(QAbstractSocket::SocketError err) +{ +#ifdef _UI_OUTPUTDIRCLIENT_DEBUG + UiLog().dbg() << "OutputDirClient::slotError --> " << soc_->errorString(); +#endif + switch(err) + { + //The logserver does not notify us if the file trasfer finish. We simply get this error. + case QAbstractSocket::RemoteHostClosedError: + +#ifdef _UI_OUTPUTDIRCLIENT_DEBUG + UiLog().dbg() << " RemoteHostClosedError "; +#endif + //If no data was transferred we think it is a real error. + if(data_.isEmpty()) + { +#ifdef _UI_OUTPUTDIRCLIENT_DEBUG + UiLog().dbg() << " --> data is empty: file transfer failed"; +#endif + break; + } + //If there is some data we think the transfer succeeded. + else + { +#ifdef _UI_OUTPUTDIRCLIENT_DEBUG + UiLog().dbg() << " --> has data: file transfer succeeded"; +#endif + if(dir_) + { + parseData(); + } + + Q_EMIT finished(); + + if(dir_) + dir_.reset(); + + return; + } + + break; + case QAbstractSocket::UnknownSocketError: + if(soc_->state() != QAbstractSocket::ConnectedState) + { + break; + } + break; + default: + break; + } + + soc_->abort(); + if(dir_) + { + dir_.reset(); + } + Q_EMIT error(soc_->errorString()); +} + +void OutputDirClient::getDir(const std::string& name) +{ + connectToHost(host_,port_); + + boost::filesystem::path fp(name); + std::string dirName=fp.parent_path().string(); + + remoteFile_=name; + dir_.reset(); + dir_=VDir_ptr(new VDir(dirName)); + data_.clear(); + + //indicates the source of the files + dir_->where(host_ + "@" + portStr_); +} + +void OutputDirClient::slotRead() +{ + const qint64 size = 64*1024; + char buf[size+1]; + quint64 len = 0; + + while((len = soc_->read(buf,size)) > 0) + { + data_.append(buf,len); + } +} + +void OutputDirClient::parseData() +{ + if(!dir_) + return; + + QTextStream in(data_); + while(!in.atEnd()) + { + int mode,uid,gid; + unsigned int size; + unsigned int atime,mtime,ctime; + QString name; + + in >> mode >> uid >> gid >> size >> atime >> mtime >> ctime >> name; + + if(!name.isEmpty()) + { + boost::filesystem::path p(name.toStdString()); + std::string fileDirName=p.parent_path().string(); + std::string fileName=p.leaf().string(); + + //Adjust the path in the dir + if(dir_->path() != fileDirName) + { + dir_->path(fileDirName,false); + } + + dir_->addItem(fileName,size,mtime); + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirClient.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirClient.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirClient.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirClient.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,46 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ +#define VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ + +#include "OutputClient.hpp" +#include "VDir.hpp" + +#include + +class OutputDirClient : public OutputClient +{ + Q_OBJECT + +public: + OutputDirClient(const std::string& host,const std::string& port,QObject *parent); + + VDir_ptr result() const; + void getDir(const std::string& name); + +protected Q_SLOTS: + void slotError(QAbstractSocket::SocketError err); + void slotRead(); + void slotConnected(); + void slotCheckTimeout(); + +private: + OutputDirClient(const OutputDirClient&); + OutputDirClient& operator=(const OutputDirClient&); + + void parseData(); + + VDir_ptr dir_; + QByteArray data_; +}; + + +#endif /* VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,399 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OutputDirProvider.hpp" + +#include "OutputDirClient.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" + +#include +#include +#include + +OutputDirProvider::OutputDirProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::NoTask), + outClient_(NULL), + currentTask_(-1) +{ +} + +void OutputDirProvider::clear() +{ + if(outClient_) + { + delete outClient_; + outClient_=NULL; + } + + queue_.clear(); + currentTask_=-1; + InfoProvider::clear(); +} + +//Node +void OutputDirProvider::visit(VInfoNode* info) +{ + //Reset the reply + reply_->reset(); + + //Clear the queue + queue_.clear(); + currentTask_=-1; + + if(!info) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + VNode *n=info->node(); + + if(!server || !n || !n->node()) + { + owner_->infoFailed(reply_); + return; + } + + std::string joboutFile=n->findVariable("ECF_JOBOUT",true); + queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::RemoteFetch); + + //With the readFromdisk option we always try read locally + if(server->readFromDisk()) + { + queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::LocalFetch); + } + //Otherwise we only read the local disk if the remote access (ie logserver) failed + else + { + queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::LocalFetch, + OutputDirProviderTask::RunIfPrevFailed); + } + + std::string outFile = n->findVariable("ECF_OUT",true); + std::string jobFile = n->findVariable("ECF_JOB",true); + if(!outFile.empty() && !jobFile.empty()) + { + queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::RemoteFetch); + + //With the readFromdisk option we always try read locally + if(server->readFromDisk()) + { + queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::LocalFetch); + } + //Otherwise we only read the local disk if the remote access (ie logserver) failed + else + { + queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::LocalFetch, + OutputDirProviderTask::RunIfPrevFailed); + } + } + + //Start fetching the dirs + fetchNext(); +} + +void OutputDirProvider::fetchIgnored() +{ + if(hasNext()) + fetchNext(); + else + completed(); +} + +void OutputDirProvider::fetchFinished(VDir_ptr dir,QString msg) +{ + Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); + OutputDirProviderTask task=queue_[currentTask_]; + queue_[currentTask_].dir_=dir; + queue_[currentTask_].status_=OutputDirProviderTask::FinishedStatus; + + if(hasNext()) + fetchNext(); + else + completed(); +} + +void OutputDirProvider::fetchFailed(QString msg) +{ + Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); + OutputDirProviderTask task=queue_[currentTask_]; + queue_[currentTask_].status_=OutputDirProviderTask::FailedStatus; + queue_[currentTask_].error_=msg; + + if(hasNext()) + fetchNext(); + else + completed(); +} + +void OutputDirProvider::failed(QString msg) +{ + queue_.clear(); + currentTask_=-1; + + reply_->setInfoText(""); + reply_->setErrorText(msg.toStdString()); + owner_->infoFailed(reply_); +} + +void OutputDirProvider::completed() +{ + bool hasAllFailed=true; + std::vector dirVec; + Q_FOREACH(OutputDirProviderTask task,queue_) + { + if(task.dir_) + dirVec.push_back(task.dir_); + + if(!task.error_.isEmpty()) + reply_->appendErrorText(task.error_.toStdString()); + + if(task.status_ != OutputDirProviderTask::FailedStatus) + hasAllFailed=false; + } + + //clear the queue + queue_.clear(); + currentTask_=-1; + + //Notify owner + reply_->setDirectories(dirVec); + owner_->infoReady(reply_); + + reply_->setInfoText(""); + reply_->setDirectories(dirVec); + if(!hasAllFailed) + owner_->infoReady(reply_); + else + owner_->infoFailed(reply_); +} + +bool OutputDirProvider::hasNext() const +{ + return currentTask_ < queue_.count()-1; +} + +void OutputDirProvider::fetchNext() +{ + //point to the next task + currentTask_++; + + Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); + if(currentTask_ >= queue_.count()) + return completed(); + + VDir_ptr dir; + if(!info_ || !info_->isNode() || !info_->node() || !info_->node()->node()) + { + failed("Node not found"); //fatal error + return; + } + + ServerHandler* server=info_->server(); + VNode *n=info_->node(); + if(!server || !n || !n->node()) + { + failed("Node not found"); //fatal error + return; + } + + //Get the current task + OutputDirProviderTask task=queue_[currentTask_]; + //Check conditions + if(currentTask_ > 0) + { + OutputDirProviderTask prevTask=queue_[currentTask_-1]; + if(task.condition_ == OutputDirProviderTask::RunIfPrevFailed && + prevTask.status_ != OutputDirProviderTask::FailedStatus) + { + fetchIgnored(); //we simply skip this task + return; + } + + } + + //Get the file name + std::string fileName=task.path_; + + //Jobout is empty: no dir path is availabale + if(fileName.empty()) + { + fetchFailed("Directory path is empty"); + return; + } + + //We try the output client, its asynchronous! Here we create an + //output client and ask to the fetch the dir asynchronously. The ouput client will call + //slotOutputClientFinished() or slotOutputClientError() eventually!! + if(task.fetchMode_ == OutputDirProviderTask::RemoteFetch) + { + if(!fetchDirViaOutputClient(n,fileName)) + { + //no logserver is defined + fetchIgnored(); + } + return; + } + + //We try to read it from disk + if(task.fetchMode_ == OutputDirProviderTask::LocalFetch) + { + //If there is no output client we try to read it from disk + std::string errStr; + dir=fetchLocalDir(fileName,errStr); + if(dir) + { + fetchFinished(dir); + } + else + { + fetchFailed(QString::fromStdString(errStr)); + } + return; + } + + //If we are here the error or warning is already set in reply + fetchFailed(); +} + +bool OutputDirProvider::fetchDirViaOutputClient(VNode *n,const std::string& fileName) +{ + std::string host, port; + if(n->logServer(host,port)) + { + outClient_=makeOutputClient(host,port); + outClient_->getDir(fileName); + return true; + } + + return false; +} + +void OutputDirProvider::slotOutputClientFinished() +{ + if(!info_ || queue_.isEmpty()) + return; + + Q_ASSERT(outClient_); + VDir_ptr dir = outClient_->result(); + if(dir) + { + dir->setFetchMode(VDir::LogServerFetchMode); + std::string method="served by " + outClient_->host() + "@" + outClient_->portStr(); + dir->setFetchModeStr(method); + dir->setFetchDate(QDateTime::currentDateTime()); + fetchFinished(dir); + return; + } + + fetchFailed(); +} + +void OutputDirProvider::slotOutputClientProgress(QString,int) +{ +} + +void OutputDirProvider::slotOutputClientError(QString msg) +{ + if(!info_ || queue_.isEmpty()) + return; + + QString sDesc; + if(outClient_) + { + sDesc="Failed to fetch from " + QString::fromStdString(outClient_->host()) + + "@" + QString::fromStdString(outClient_->portStr()); + if(!msg.isEmpty()) + sDesc+=" error: " + msg; + + } + else + { + sDesc="Failed to fetch from logserver"; + if(!msg.isEmpty()) + sDesc+=": " + msg; + } + + fetchFailed(sDesc); +} + +VDir_ptr OutputDirProvider::fetchLocalDir(const std::string& path,std::string& errorStr) +{ + VDir_ptr res; + + boost::filesystem::path p(path); + + //Is it a directory? + boost::system::error_code errorCode; + if(boost::filesystem::is_directory(p,errorCode)) + { + return res; + } + + try + { + if(boost::filesystem::exists(p.parent_path())) + { + std::string dirName=p.parent_path().string(); + if(info_ && info_->isNode() && info_->node()) + { + std::string nodeName=info_->node()->strName(); + std::string pattern=nodeName+"."; + res=VDir_ptr(new VDir(dirName,pattern)); + res->setFetchDate(QDateTime::currentDateTime()); + res->setFetchMode(VDir::LocalFetchMode); + res->setFetchModeStr("from disk"); + return res; + } + } + + errorStr="No access to path on disk!"; + return res; + } + catch (const boost::filesystem::filesystem_error& e) + { + errorStr="No access to path on disk! error: " + std::string(e.what()); + UiLog().warn() << "fetchLocalDir failed:" << std::string(e.what()); + } + + return res; +} + +OutputDirClient* OutputDirProvider::makeOutputClient(const std::string& host,const std::string& port) +{ + if(outClient_) + { + if(outClient_->host() != host || outClient_->portStr() != port) + { + delete outClient_; + outClient_=0; + } + } + + if(!outClient_) + { + outClient_=new OutputDirClient(host,port,this); + + connect(outClient_,SIGNAL(error(QString)), + this,SLOT(slotOutputClientError(QString))); + + connect(outClient_,SIGNAL(progress(QString,int)), + this,SLOT(slotOutputClientProgress(QString,int))); + + connect(outClient_,SIGNAL(finished()), + this,SLOT(slotOutputClientFinished())); + } + + return outClient_; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputDirProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputDirProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,77 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ +#define VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ + +#include + +#include "VDir.hpp" +#include "VInfo.hpp" +#include "InfoProvider.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +class OutputDirClient; + +class OutputDirProviderTask +{ +public: + enum FetchMode {LocalFetch,RemoteFetch}; + enum Status {UnkownStatus,FinishedStatus,FailedStatus}; + enum Condition {NoCondition,RunIfPrevFailed}; + + OutputDirProviderTask(const std::string& path,FetchMode fetchMode,Condition cond=NoCondition) : + path_(path), fetchMode_(fetchMode), condition_(cond), status_(UnkownStatus) {} + + std::string path_; + VDir_ptr dir_; + QString error_; + FetchMode fetchMode_; + Condition condition_; + Status status_; +}; + + +class OutputDirProvider : public QObject, public InfoProvider +{ +Q_OBJECT + +public: + explicit OutputDirProvider(InfoPresenter* owner); + + void visit(VInfoNode*); + void clear(); + +private Q_SLOTS: + void slotOutputClientError(QString); + void slotOutputClientProgress(QString,int); + void slotOutputClientFinished(); + +private: + bool hasNext() const; + void fetchNext(); + void fetchIgnored(); + void fetchFinished(VDir_ptr,QString msg=QString()); + void fetchFailed(QString msg=QString()); + void failed(QString); + void completed(); + + bool fetchDirViaOutputClient(VNode *n,const std::string& fileName); + VDir_ptr fetchLocalDir(const std::string& path,std::string& errorStr); + OutputDirClient* makeOutputClient(const std::string& host,const std::string& port); + + OutputDirClient *outClient_; + QList queue_; + int currentTask_; +}; + + +#endif /* VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFetchInfo.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFetchInfo.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFetchInfo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFetchInfo.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,192 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OutputFetchInfo.hpp" + +#include +#include +#include +#include +#include + +#include + +#include "ServerHandler.hpp" + +OutputFetchInfo::OutputFetchInfo(QWidget* parent) : QWidget(parent) +{ + QVBoxLayout *vb=new QVBoxLayout(this); + label_=new QLabel(this); + label_->setText("Additional information"); + + te_=new QTextEdit(this); + te_->setReadOnly(true); + te_->setMinimumWidth(350); + + vb->addWidget(label_); + vb->addWidget(te_,1); +} + +void OutputFetchInfo::setInfo(VReply *reply,VInfo_ptr info) +{ + Q_ASSERT(reply); + + te_->clear(); + + static QMap nums; + if(nums.isEmpty()) + { + nums[1]="1st"; + nums[2]="2nd"; + nums[3]="3rd"; + nums[4]="4th"; + } + + QStringList options; + QStringList remarks; + QStringList msg; + QStringList tries; + QStringList other; + QString alg; + + QString html; + + int cnt=1; + for(std::vector::const_iterator it=reply->log().begin(); it != reply->log().end(); ++it) + { + QString s=QString::fromStdString(*it); + if(s.startsWith("REMARK>")) + { + remarks << s.remove(0,7); + continue; + } + else if(s.startsWith("OPTION>")) + { + options << s.remove(0,7); + continue; + } + else if(s.startsWith("MSG>")) + { + msg << s.remove(0,4); + continue; + } + else if(s.startsWith("TRY>")) + { + s.remove(0,4); + s.prepend("tried to "); + s.replace(" OK"," SUCCEEDED"); + s.replace(" FAILED"," FAILED"); + s.replace(" NO ACCESS"," NO ACCESS"); + s.replace(" NOT DEFINED"," NOT DEFINED"); + tries << s; + cnt++; + continue; + } + else + other << s; + } + + if(info && info->server()) + { + ServerHandler* server=info->server(); + bool rfd=server->readFromDisk(); + QString t; + + t="The following are tried in order:
        "; + + if(rfd) + { + t+="
      • Try to read the output files from the logserver \ + (if defined)
      • from disk
      • \ + through the ecflow server (if the current job output)
      • "; + } + else + { + t+="
      • Try to read the output files from the logserver \ + (if defined)
      • from disk (if not the current job output)
      • \ +
      • from the ecflow server (if the current job output)
      • "; + } + t+="
      (To change this behaviour go Tools -> Preferences -> Server settings -> Fetching files)"; + + alg=t; + + if(reply->tmpFile() && reply->fileReadMode() == VReply::LocalReadMode && + !server->isLocalHost()) + { + remarks << "The output file was read from disk but the server (" + + QString::fromStdString(server->host()) + + ") is not running on the local machine. If the path is machine-specific (e.g. /tmp) \ + and there exists a file with the same path on the local machine, then\ + this will have been read instead."; + } + } + + if(!msg.isEmpty()) + { + html+="

      Messages

      "; + html+=buildList(msg); + } + + if(!options.isEmpty()) + { + html+="

      Options

      "; + html+=buildList(options); + } + + if(!tries.isEmpty()) + { + html+="

      How was this file fetched?

      "; + html+=buildList(tries,true); + } + + if(!alg.isEmpty()) + { + html+="

      Algorithm:

      "+alg; + } + + if(!remarks.isEmpty()) + { + html+="

      Remarks

      "; + html+=buildList(remarks); + } + + if(!other.isEmpty()) + { + html+=buildList(other); + + } + + te_->setHtml(html); + + QTextCursor cursor=te_->textCursor(); + cursor.movePosition(QTextCursor::Start); + te_->setTextCursor(cursor); +} + +QString OutputFetchInfo::buildList(QStringList lst,bool ordered) +{ + QString t; + + if(lst.count() > 0) + { + t+=(ordered)?"
        ":"
          "; + Q_FOREACH(QString s,lst) + { + t+="
        • " + s + "
        • "; + } + t+=(ordered)?"
      ":"
    "; + } + + return t; +} + +void OutputFetchInfo::clearInfo() +{ + te_->clear(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFetchInfo.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFetchInfo.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFetchInfo.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFetchInfo.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,36 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ +#ifndef OUTPUTFETCHINFO_HPP +#define OUTPUTFETCHINFO_HPP + +#include + +#include "VInfo.hpp" +#include "VReply.hpp" + +class QLabel; +class QTextEdit; + +class OutputFetchInfo : public QWidget +{ +public: + OutputFetchInfo(QWidget* parent=0); + void setInfo(VReply*,VInfo_ptr); + void clearInfo(); + +private: + QString buildList(QStringList,bool ordered=false); + + QLabel* label_; + QTextEdit* te_; + +}; + +#endif // OUTPUTFETCHINFO_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileClient.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileClient.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileClient.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileClient.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,198 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "OutputFileClient.hpp" + +#include "UiLog.hpp" + +#define _UI_OUTPUTFILECLIENT_DEBUG + +OutputFileClient::OutputFileClient(const std::string& host,const std::string& portStr,QObject* parent) : + OutputClient(host,portStr,parent), + total_(0), + expected_(0), + lastProgress_(0), + progressUnits_("MB"), + progressChunk_(1024*1024) +{ +} + +void OutputFileClient::clearResult() +{ + if(out_) + { + out_->close(); + out_.reset(); + } +} + +void OutputFileClient::slotConnected() +{ + soc_->write("get ",4); + soc_->write(remoteFile_.c_str(),remoteFile_.size()); + soc_->write("\n",1); +} + +void OutputFileClient::slotError(QAbstractSocket::SocketError err) +{ + switch(err) + { + case QAbstractSocket::RemoteHostClosedError: + + if(total_ == 0) + { + break; + } + //Probably there was no error and the connection was closed because all + //the data was transferred. Unfortunately the logserver does not send anything + //at the end of the data transfer. + else + { + soc_->abort(); + if(out_) + { + out_->setTransferDuration(stopper_.elapsed()); + out_->setFetchDate(QDateTime::currentDateTime()); + out_->close(); + } + + Q_EMIT finished(); + + if(out_) + out_.reset(); + + return; + + } + + break; + case QAbstractSocket::UnknownSocketError: + if(soc_->state() != QAbstractSocket::ConnectedState) + { + break; + } + break; + default: + break; + } + + soc_->abort(); + if(out_) + { + out_->close(); + out_.reset(); + } + Q_EMIT error(soc_->errorString()); + +} + +void OutputFileClient::getFile(const std::string& name) +{ + connectToHost(host_,port_); + + remoteFile_=name; + out_.reset(); + out_=VFile_ptr(VFile::create(true)); //we will delete the file from disk + out_->setSourcePath(name); + total_=0; + lastProgress_=0; + estimateExpectedSize(); +} + +void OutputFileClient::slotRead() +{ + const qint64 size = 64*1024; + char buf[size]; + quint64 len = 0; + + while((len = soc_->read(buf,size)) > 0) + { + std::string err; + if(!out_->write(buf,len,err)) + { + soc_->abort(); + Q_EMIT error("write failed"); + } + total_ += len; + + if(total_/progressChunk_ > lastProgress_) + { + lastProgress_=total_/progressChunk_; + + int prog=0; + if(expected_ > 0) + { + prog=static_cast(100.*static_cast(total_)/static_cast(expected_)); + if(prog>100) prog=100; + Q_EMIT progress(QString::number(lastProgress_) + "/" + QString::number(expected_/progressChunk_) + + " " + progressUnits_ + " transferred",prog); + } + else + Q_EMIT progress(QString::number(lastProgress_) + " " + progressUnits_ + " transferred",prog); + } + } +} + +VFile_ptr OutputFileClient::result() const +{ + return out_; +} + +void OutputFileClient::setExpectedSize(qint64 v) +{ + expected_=v; +} + +int OutputFileClient::maxProgress() const +{ + return expected_/progressChunk_; +} + +void OutputFileClient::setDir(VDir_ptr dir) +{ + if(dir_ && dir && dir_.get() == dir.get()) + return; + + dir_=dir; + //if(expected_ == 0) + estimateExpectedSize(); +} + +void OutputFileClient::estimateExpectedSize() +{ + if(!dir_) + { + expected_=0; + return; + } + +#ifdef _UI_OUTPUTFILECLIENT_DEBUG + UiLog().dbg() << "OutputFileClient::estimateExpectedSize -->"; +#endif + for(int i=0; i < dir_->count(); i++) + { +#ifdef _UI_OUTPUTFILECLIENT_DEBUG + UiLog().dbg() << "file: " << dir_->fullName(i); +#endif + if(dir_->fullName(i) == remoteFile_) + { + expected_=dir_->items().at(i)->size_; +#ifdef _UI_OUTPUTFILECLIENT_DEBUG + UiLog().dbg() << " expected size=" << expected_; +#endif + return; + } + } + + expected_=0; +#ifdef _UI_OUTPUTFILECLIENT_DEBUG + UiLog().dbg() << " expected size=" << QString::number(expected_); +#endif +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileClient.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileClient.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileClient.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileClient.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,51 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_OUTPUTFILECLIENT_HPP_ +#define VIEWER_SRC_OUTPUTFILECLIENT_HPP_ + +#include "OutputClient.hpp" +#include "VDir.hpp" +#include "VFile.hpp" + +class OutputFileClient : public OutputClient +{ + Q_OBJECT + +public: + OutputFileClient(const std::string& host,const std::string& port,QObject *parent); + + VFile_ptr result() const; + void clearResult(); + void getFile(const std::string& name); + void setExpectedSize(qint64 v); + int maxProgress() const; + void setDir(VDir_ptr); + +protected Q_SLOTS: + void slotError(QAbstractSocket::SocketError err); + void slotRead(); + void slotConnected(); + +private: + OutputFileClient(const OutputClient&); + OutputFileClient& operator=(const OutputClient&); + void estimateExpectedSize(); + + qint64 total_; + qint64 expected_; + VFile_ptr out_; + VDir_ptr dir_; + qint64 lastProgress_; + const QString progressUnits_; + const qint64 progressChunk_; +}; + +#endif /* VIEWER_SRC_OUTPUTFILECLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,567 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OutputFileProvider.hpp" + +#include "OutputCache.hpp" +#include "OutputFileClient.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" + +#include + +#include +#include +#include + +OutputFileProvider::OutputFileProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::OutputTask), + outClient_(NULL), + useOutputClientOnly_(false) +{ + outCache_=new OutputCache(this); +} + +void OutputFileProvider::clear() +{ + useOutputClientOnly_=false; + + //Detach all the outputs registered for this instance in cache + outCache_->detach(); + + if(outClient_) + { + delete outClient_; + outClient_=NULL; + } + InfoProvider::clear(); + + dirs_.clear(); +} + +//This is called when we load a new node in the Output panel. In this +//case we always try to load the current jobout file. +void OutputFileProvider::visit(VInfoNode* infoNode) +{ + assert(info_->node() == infoNode->node()); + + useOutputClientOnly_=false; + + //Reset the reply + reply_->reset(); + + if(!info_) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + VNode *n=infoNode->node(); + + if(!n || !n->node()) + { + owner_->infoFailed(reply_); + return; + } + + //Get the filename + std::string jobout=joboutFileName(); + + //We always try to use the cache in this case + outCache_->attachOne(info_,jobout); + fetchFile(server,n,jobout,true,true); +} + +//Get a file +void OutputFileProvider::file(const std::string& fileName,bool useCache) +{ + useOutputClientOnly_=false; + + //If we do not want to use the cache we detach all the output + //attached to this instance + if(!useCache) + outCache_->detach(); + + //Check if the task is already running + if(task_) + { + task_->status(VTask::CANCELLED); + task_.reset(); + } + + //Reset the reply + reply_->reset(); + + if(!info_->isNode() || !info_->node() || !info_->node()->node()) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + VNode *n=info_->node(); + + //Get the filename + std::string jobout=joboutFileName(); //n->findVariable("ECF_JOBOUT",true); + + fetchFile(server,n,fileName,(fileName==jobout),useCache); +} + + +void OutputFileProvider::fetchFile(ServerHandler *server,VNode *n,const std::string& fileName,bool isJobout,bool useCache) +{ + //If we do not want to use the cache we detach all the output + //attached to this instance + if(!useCache) + outCache_->detach(); + + if(!n || !n->node() || !server) + { + owner_->infoFailed(reply_); + return; + } + + //Set the filename in reply + reply_->fileName(fileName); + + //No filename is available + if(fileName.empty()) + { + //Joubout variable is not defined or empty + if(isJobout) + { + reply_->setErrorText("Variable ECF_JOBOUT is not defined!"); + reply_->addLog("MSG>Variable ECF_JOBOUT is not defined!."); + owner_->infoFailed(reply_); + } + else + { + reply_->setErrorText("Output file is not defined!"); + reply_->addLog("MSG>Output file is not defined."); + owner_->infoFailed(reply_); + } + + return; + } + + //Check if tryno is 0. ie. the file is the current jobout file and ECF_TRYNO = 0 + if(isTryNoZero(fileName)) + { + reply_->setInfoText("Current job output does not exist yet (TRYNO is 0)!)"); + reply_->addLog("MSG>Current job output does not exist yet (TRYNO is 0)!"); + owner_->infoReady(reply_); + return; + } + + //---------------------------------- + // The host is the localhost + //---------------------------------- + + if(isJobout) + { + if(n->isSubmitted()) + reply_->addLog("REMARK>This file is the current job output (defined by variable ECF_JOBOUT), but \ + because the node status is submitted it may contain the ouput from a previous run!"); + else + reply_->addLog("REMARK>This file is the current job output (defined by variable ECF_JOBOUT)."); + } + else + reply_->addLog("REMARK>This file is not the current job output (defined by ECF_JOBOUT)."); + +#if 0 + if(server->readFromDisk()) + { + if(server->isLocalHost()) + { + //We try to read the file directly from the disk + if(server->readFromDisk()) + { + if(fetchLocalFile(fileName)) + return; + } + } +#endif + +#if 0 + //if(server->isLocalHost()) + // { + //We try to read the file directly from the disk + if(server->readFromDisk()) + { + if(fetchLocalFile(fileName)) + return; + } + //} + +#endif + + + //We try the output client (aka logserver), its asynchronous! + if(fetchFileViaOutputClient(n,fileName,useCache)) + { + //If we are here we created an output client and asked it to the fetch the + //file asynchronously. The ouput client will call slotOutputClientFinished() or + //slotOutputClientError eventually!! + return; + } + + //If we are here there is no output client defined + reply_->addLog("TRY>fetch file from logserver: NOT DEFINED"); + + //If there is no output client we try + //to read it from the disk (if the settings allow it) + if(server->readFromDisk() || !isJobout) + { + //Get the fileName + if(fetchLocalFile(fileName)) + return; + } + + //If we are here no output client is defined and we could not read the file from + //the local disk, so we try the server if it is the jobout file. + if(isJobout) + { + fetchJoboutViaServer(server,n,fileName); + return; + } + + //If we are here we coud not get the file + if(n->isFlagSet(ecf::Flag::JOBCMD_FAILED)) + { + reply_->setErrorText("Submission command failed! Check .sub file, ssh, or queueing system error."); + } + owner_->infoFailed(reply_); +} + +//Get a file with the given fetch mode +void OutputFileProvider::fetchFile(const std::string& fileName,VDir::FetchMode fetchMode,bool useCache) +{ + //If we do not want to use the cache we detach all the output + //attached to this instance + if(!useCache) + outCache_->detach(); + + //Check if the task is already running + if(task_) + { + task_->status(VTask::CANCELLED); + task_.reset(); + } + + //Reset the reply + reply_->reset(); + + if(!info_->isNode() || !info_->node() || !info_->node()->node()) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + VNode *n=info_->node(); + + //Get the filename + std::string jobout=joboutFileName(); //n->findVariable("ECF_JOBOUT",true); + bool isJobout=(fileName==jobout); + + //Set the filename in reply + reply_->fileName(fileName); + + //No filename is available + if(fileName.empty()) + { + reply_->setErrorText("Output file is not defined!"); + reply_->addLog("MSG>Output file is not defined."); + owner_->infoFailed(reply_); + return; + } + + //Check if tryno is 0. ie. the file is the current jobout file and ECF_TRYNO = 0 + if(isTryNoZero(fileName)) + { + reply_->setInfoText("Current job output does not exist yet (TRYNO is 0)!)"); + reply_->addLog("MSG>Current job output does not exist yet (TRYNO is 0)!"); + owner_->infoReady(reply_); + return; + } + + //We try the output client (aka logserver), its asynchronous! + if(fetchMode == VDir::LogServerFetchMode) + { + useOutputClientOnly_=true; + if(fetchFileViaOutputClient(n,fileName,useCache)) + { + //If we are here we created an output client and asked it to the fetch the + //file asynchronously. The ouput client will call slotOutputClientFinished() or + //slotOutputClientError eventually!! + return; + } + + //If we are here there is no output client defined + reply_->addLog("TRY>fetch file from logserver: NOT DEFINED"); + } + else if(fetchMode == VDir::LocalFetchMode) + { + if(fetchLocalFile(fileName)) + return; + } + + //If we are here no output client is defined and we could not read the file from + //the local disk, so we try the server if it is the jobout file. + else if(isJobout && fetchMode == VDir::ServerFetchMode) + { + fetchJoboutViaServer(server,n,fileName); + return; + } + + //If we are here we coud not get the file + owner_->infoFailed(reply_); +} + +bool OutputFileProvider::fetchFileViaOutputClient(VNode *n,const std::string& fileName,bool useCache) +{ + UI_FUNCTION_LOG + UiLog().dbg() << "OutputFileProvider::fetchFileViaOutputClient <-- file: " << fileName; + + std::string host, port; + assert(n); + + //We try use the cache + if(useCache) + { + //Check if the given output is already in the cache + if(OutputCacheItem* item=outCache_->attachOne(info_,fileName)) + { + VFile_ptr f=item->file(); + assert(f); + f->setCached(true); + + UiLog().dbg() << " File found in cache"; + + reply_->setInfoText(""); + reply_->fileReadMode(VReply::LogServerReadMode); + + reply_->setLog(f->log()); + reply_->addLog("REMARK>File was read from cache."); + + reply_->tmpFile(f); + owner_->infoReady(reply_); + return true; + } + } + + //We did/could not use the cache + if(n->logServer(host,port)) + { + //host=host + "baaad"; + + UiLog().dbg() << "OutputFileProvider::fetchFileViaOutputClient --> host:" << host << + " port:" << port << " file: " << fileName; + + //reply_->setInfoText("Getting file through log server: " + host + "@" + port); + //owner_->infoProgress(reply_); + owner_->infoProgressStart("Getting file " + fileName + " from log server " + host + "@" + port +"",0); + + if(!outClient_) + { + outClient_=new OutputFileClient(host,port,this); + + connect(outClient_,SIGNAL(error(QString)), + this,SLOT(slotOutputClientError(QString))); + + connect(outClient_,SIGNAL(progress(QString,int)), + this,SLOT(slotOutputClientProgress(QString,int))); + + connect(outClient_,SIGNAL(finished()), + this,SLOT(slotOutputClientFinished())); + } + + VDir_ptr dir=dirToFile(fileName); + outClient_->setDir(dir); + outClient_->getFile(fileName); + + return true; + } + + return false; +} + +void OutputFileProvider::slotOutputClientFinished() +{ + VFile_ptr tmp = outClient_->result(); + assert(tmp); + + useOutputClientOnly_=false; + outClient_->clearResult(); + + //Files retrieved from the log server are automatically added to the cache! + outCache_->add(info_,tmp->sourcePath(),tmp); + + reply_->setInfoText(""); + reply_->fileReadMode(VReply::LogServerReadMode); + reply_->addLog("TRY> fetch file from logserver: " + outClient_->host() + "@" + outClient_->portStr() + ": OK"); + + tmp->setFetchMode(VFile::LogServerFetchMode); + tmp->setLog(reply_->log()); + std::string method="served by " + outClient_->host() + "@" + outClient_->portStr(); + tmp->setFetchModeStr(method); + + reply_->tmpFile(tmp); + owner_->infoReady(reply_); +} + +void OutputFileProvider::slotOutputClientProgress(QString msg,int value) +{ + //UiLog().dbg() << "OutputFileProvider::slotOutputClientProgress " << msg; + + owner_->infoProgress(msg.toStdString(),value); + + //reply_->setInfoText(msg.toStdString()); + //owner_->infoProgress(reply_); + //reply_->setInfoText(""); +} + +void OutputFileProvider::slotOutputClientError(QString msg) +{ + UiLog().dbg() << "OutputFileProvider::slotOutputClientError error:" << msg; + reply_->addLog("TRY> fetch file from logserver: " + outClient_->host() + "@" + outClient_->portStr() + " FAILED"); + + if(info_ && !useOutputClientOnly_) + { + useOutputClientOnly_=false; + ServerHandler* server=info_->server(); + VNode *n=info_->node(); + + if(server && n) + { + std::string jobout=n->findVariable("ECF_JOBOUT",true); + bool isJobout=(outClient_->remoteFile() == jobout); + + //We try to read the file directly from the disk + if(server->readFromDisk() || !isJobout) + { + if(fetchLocalFile(outClient_->remoteFile())) + return; + } + + //Then we try the server + if(isJobout) + { + fetchJoboutViaServer(server,n,jobout); + return; + } + } + } + + reply_->setErrorText("Failed to fetch file from logserver " + + outClient_->host() + "@" + outClient_->portStr() + + "Error: " + msg.toStdString()); + owner_->infoFailed(reply_); +} + +void OutputFileProvider::fetchJoboutViaServer(ServerHandler *server,VNode *n,const std::string& fileName) +{ + assert(server); + assert(n); + + //Define a task for getting the info from the server. + task_=VTask::create(taskType_,n,this); + + task_->reply()->fileReadMode(VReply::ServerReadMode); + task_->reply()->fileName(fileName); + task_->reply()->setLog(reply_->log()); + + //owner_->infoProgressStart("Getting file " + fileName + " from server",0); + + //Run the task in the server. When it finish taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + server->run(task_); +} + +bool OutputFileProvider::fetchLocalFile(const std::string& fileName) +{ + //we do not want to delete the file once the VFile object is destroyed!! + VFile_ptr f(VFile::create(fileName,false)); + if(f->exists()) + { + reply_->fileReadMode(VReply::LocalReadMode); + reply_->addLog("TRY> read file from disk: OK"); + + f->setSourcePath(f->path()); + f->setFetchMode(VFile::LocalFetchMode); + f->setFetchDate(QDateTime::currentDateTime()); + f->setLog(reply_->log()); + + reply_->tmpFile(f); + owner_->infoReady(reply_); + return true; + } + reply_->addLog("TRY> read file from disk: NO ACCESS"); + return false; +} + + +std::string OutputFileProvider::joboutFileName() const +{ + if(info_ && info_->isNode() && info_->node() && info_->node()->node()) + { + return info_->node()->findVariable("ECF_JOBOUT",true); + } + + return std::string(); +} + +//Returns true if +// -the file is the current jobout +// -id contains the string ".0" +// -ECF_TRYNO = 0 + +bool OutputFileProvider::isTryNoZero(const std::string& filename) const +{ + if(filename.find(".0") != std::string::npos && + joboutFileName() == filename && + info_ && info_->isNode() && info_->node() && info_->node()->node()) + { + return (info_->node()->findVariable("ECF_TRYNO",true) == "0"); + } + return false; +} + +//We use directories to figure out the size of the file to be transfered +void OutputFileProvider::setDirectories(const std::vector& dirs) +{ +#if 0 + if(outClient_) + outClient_->setDir(dir); + + if(dir != dir_) + dir_=dir; +#endif + + dirs_=dirs; +} + +VDir_ptr OutputFileProvider::dirToFile(const std::string& fileName) const +{ + VDir_ptr dir; + + if(fileName.empty()) + return dir; + + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i] && fileName.find(dirs_[i]->path()) == 0) + return dirs_[i]; + + } + return dir; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputFileProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputFileProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,60 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef OUTPUTFILEPROVIDER_HPP_ +#define OUTPUTFILEPROVIDER_HPP_ + +#include + +#include "VDir.hpp" +#include "VInfo.hpp" +#include "InfoProvider.hpp" +#include "VTask.hpp" +#include "VTaskObserver.hpp" + +class OutputFileClient; +class OutputCache; + +class OutputFileProvider : public QObject, public InfoProvider +{ +Q_OBJECT + +public: + explicit OutputFileProvider(InfoPresenter* owner); + + void visit(VInfoNode*); + void clear(); + + //Get a particular jobout file + void file(const std::string& fileName,bool useCache=true); + void fetchFile(const std::string& fileName,VDir::FetchMode fetchMode,bool useCache=true); + void setDirectories(const std::vector&); + + std::string joboutFileName() const; + bool isTryNoZero(const std::string& fileName) const; + +private Q_SLOTS: + void slotOutputClientError(QString); + void slotOutputClientProgress(QString,int); + void slotOutputClientFinished(); + +private: + VDir_ptr dirToFile(const std::string& fileName) const; + void fetchFile(ServerHandler *server,VNode *n,const std::string& fileName,bool isJobout,bool detachCache); + void fetchJoboutViaServer(ServerHandler *server,VNode *n,const std::string&); + bool fetchFileViaOutputClient(VNode *n,const std::string& fileName,bool useCache); + bool fetchLocalFile(const std::string& fileName); + + OutputFileClient *outClient_; + bool useOutputClientOnly_; + std::vector dirs_; + OutputCache* outCache_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,809 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OutputItemWidget.hpp" + +#include "OutputDirProvider.hpp" +#include "OutputFetchInfo.hpp" +#include "OutputFileProvider.hpp" +#include "OutputModel.hpp" +#include "PlainTextEdit.hpp" +#include "ServerHandler.hpp" +#include "TextFormat.hpp" +#include "TextPagerEdit.hpp" +#include "VConfig.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" + +#include +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#else +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define _UI_OUTPUTITEMWIDGET_DEBUG + +int OutputItemWidget::updateDirTimeout_=1000*60; + +OutputItemWidget::OutputItemWidget(QWidget *parent) : + QWidget(parent), + userClickedReload_(false), + ignoreOutputSelection_(false), + dirColumnsAdjusted_(false), + submittedWarning_(false) +{ + //We try to keep the contents when clicking away + //tryToKeepContents_=true; + + setupUi(this); + + //-------------------------------- + // The file contents + //-------------------------------- + + messageLabel_->hide(); + messageLabel_->setShowTypeTitle(false); + warnLabel_->hide(); + fileLabel_->setProperty("fileInfo","1"); + + infoProvider_=new OutputFileProvider(this); + + //-------------------------------- + // The dir contents + //-------------------------------- + + dirMessageLabel_->hide(); + dirMessageLabel_->setShowTypeTitle(false); + //dirLabel_->hide(); + dirLabel_->setProperty("fileInfo","1"); + + dirProvider_=new OutputDirProvider(this); + + //The view + OutputDirLitsDelegate* dirDelegate=new OutputDirLitsDelegate(this); + dirView_->setItemDelegate(dirDelegate); + dirView_->setRootIsDecorated(false); + dirView_->setAllColumnsShowFocus(true); + dirView_->setUniformRowHeights(true); + dirView_->setAlternatingRowColors(true); + dirView_->setSortingEnabled(true); + + //Sort by column "modifiied (ago)", latest files first + dirView_->sortByColumn(3, Qt::AscendingOrder); + + //The models + dirModel_=new OutputModel(this); + dirSortModel_=new OutputSortModel(this); + dirSortModel_->setSourceModel(dirModel_); + dirSortModel_->setSortRole(Qt::UserRole); + dirSortModel_->setDynamicSortFilter(true); + + dirView_->setModel(dirSortModel_); + + //When the selection changes in the view + connect(dirView_->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this,SLOT(slotOutputSelected(QModelIndex,QModelIndex))); + + //Set splitter's initial size. + int wHeight=size().height(); + if(wHeight > 100) + { + QList sizes; + sizes << wHeight-80 << 80; + splitter_->setSizes(sizes); + } + + //Dir contents update timer + updateDirTimer_=new QTimer(this); + updateDirTimer_->setInterval(updateDirTimeout_); + + connect(updateDirTimer_,SIGNAL(timeout()), + this,SLOT(slotUpdateDir())); + + //Editor font + browser_->setFontProperty(VConfig::instance()->find("panel.output.font")); + + fetchInfo_=new OutputFetchInfo(this); + QWidgetAction* fetchInfoAction=new QWidgetAction(this); + fetchInfoAction->setDefaultWidget(fetchInfo_); + fetchInfoTb_->addAction(fetchInfoAction); + + filterTb_->setProperty("strip","first"); + filterOptionTb_->setProperty("strip","last"); + + QMenu *menu=new QMenu(this); + menu->addAction(actionSaveFileAs_); + menu->addAction(actionGotoLine_); + + //TODO: needs proper implementation + gotoLineTb_->hide(); + + //Sets the menu on the toolbutton + moreActionTb_->setMenu(menu); + moreActionTb_->hide(); + moreActionTb_->setEnabled(false); + actionSaveFileAs_->setEnabled(false); + actionGotoLine_->setEnabled(false); + + //Init filter in output browser + browser_->setFilterButtons(filterTb_,filterOptionTb_); + + // + browser_->setSearchButtons(searchTb_); +} + +OutputItemWidget::~OutputItemWidget() +{ +} + +QWidget* OutputItemWidget::realWidget() +{ + return this; +} + +void OutputItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + //set the info + adjust(info); + + userClickedReload_ = false; + + //info must be a node + if(info_ && info_->isNode() && info_->node()) + { + //Get file contents + infoProvider_->info(info_); + + //Get dir contents + dirProvider_->info(info_); + + //Start contents update timer + updateDirTimer_->start(); + } +} + +//Get information (description) about the current selection in the dir list +void OutputItemWidget::currentDesc(std::string& fullName,VDir::FetchMode& fetchMode) const +{ + QModelIndex current=dirSortModel_->mapToSource(dirView_->currentIndex()); + + if(current.isValid()) + { + dirModel_->itemDesc(current,fullName,fetchMode); + } + else + { + OutputFileProvider* op=static_cast(infoProvider_); + fullName=op->joboutFileName(); + fetchMode=VDir::NoFetchMode; + } +} + +void OutputItemWidget::getLatestFile() +{ + messageLabel_->hide(); + messageLabel_->stopProgress(); + fileLabel_->clear(); + browser_->clear(); + dirMessageLabel_->hide(); + fetchInfo_->clearInfo(); + + //Get the latest file contents + infoProvider_->info(info_); + + updateDir(false); // get the directory listing +} + +//Load the currently selected file in the dir view +void OutputItemWidget::getCurrentFile(bool doReload) +{ + messageLabel_->hide(); + messageLabel_->stopLoadLabel(); + messageLabel_->stopProgress(); + fileLabel_->clear(); + browser_->clear(); + fetchInfo_->clearInfo(); + + if(info_) + { + std::string fullName; + VDir::FetchMode fetchMode; + currentDesc(fullName,fetchMode); + if(!fullName.empty()) + { +#ifdef _UI_OUTPUTITEMWIDGET_DEBUG + UI_FUNCTION_LOG + UiLog().dbg() << "output selected: " << fullName; +#endif + //Fetch the file with given fetchmode + OutputFileProvider* op=static_cast(infoProvider_); + + //If the fetchmode is not defined we use the normal fetch policy + if(fetchMode == VDir::NoFetchMode) + op->file(fullName,!doReload); + //Otherwise we need to use the given fetch mode + else + op->fetchFile(fullName,fetchMode,!doReload); + + updateDir(false); // get the directory listing + } + } +} + +void OutputItemWidget::clearContents() +{ + updateDirTimer_->stop(); + InfoPanelItem::clear(); + enableDir(false); + messageLabel_->hide(); + messageLabel_->stopProgress(); + fileLabel_->clear(); + browser_->clear(); + reloadTb_->setEnabled(true); + userClickedReload_ = false; + fetchInfo_->clearInfo(); + submittedWarning_=false; +} + +void OutputItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SelectedChanged)) + { + if(selected_ && !suspended_) + { + slotUpdateDir(); + updateDirTimer_->start(); + } + //If unselected we stop the dir update + else + { + updateDirTimer_->stop(); + } + } + + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + updateDirTimer_->stop(); + reloadTb_->setEnabled(false); + enableDir(false); + } + //Resume + else + { + if(info_ && info_->node()) + { + reloadTb_->setEnabled(true); + enableDir(true); + if(selected_) + { + slotUpdateDir(); + updateDirTimer_->start(); + } + } + else + { + clearContents(); + } + } + } +} + +void OutputItemWidget::infoReady(VReply* reply) +{ + //------------------------ + // From output provider + //------------------------ + + if(reply->sender() == infoProvider_) + { + messageLabel_->stopProgress(); + + //For some unknown reason the textedit font, although it is properly set in the constructor, + //is reset to default when we first call infoready. So we need to set it again!! + browser_->updateFont(); + + //TODO: make it possible to show warning and info at the same time + bool hasMessage=false; + submittedWarning_=false; + OutputFileProvider* op=static_cast(infoProvider_); + if(reply->fileName() == op->joboutFileName() && !op->isTryNoZero(reply->fileName()) && + info_ && info_->isNode() && info_->node() && info_->node()->isSubmitted()) + { + hasMessage=true; + submittedWarning_=true; + messageLabel_->showWarning("This is the current job output (as defined by variable ECF_JOBOUT), but \ + because the node status is submitted it may contain the ouput from a previous run!"); + } + else + { + if(reply->hasWarning()) + { + messageLabel_->showWarning(QString::fromStdString(reply->warningText())); + hasMessage=true; + } + else if(reply->hasInfo()) + { + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); + hasMessage=true; + } + } + + browser_->adjustHighlighter(QString::fromStdString(reply->fileName())); + + VFile_ptr f=reply->tmpFile(); + + //If the info is stored in a tmp file + if(f) + { + browser_->loadFile(f); + if(f->storageMode() == VFile::DiskStorage) + hasMessage=false; + + } + //If the info is stored as a string in the reply object + else + { + //QString s=QString::fromStdString(reply->text()); + //browser_->loadText(s,QString::fromStdString(reply->fileName())); + } + + if(!hasMessage) + { + messageLabel_->hide(); + } + //messageLabel_->stopLoadLabel(); + + //Update the file label + fileLabel_->update(reply); + + //Search for some keywords in the current jobout + + if(f) + { + //We do not have dir info so the file must be the jobout + if(dirModel_->isEmpty()) + searchOnReload(); + + //We have dir info + else + { + OutputFileProvider* op=static_cast(infoProvider_); + if(reply->fileName() == op->joboutFileName()) + { + searchOnReload(); + } + } + } + + userClickedReload_ = false; + reloadTb_->setEnabled(true); + + //If we got a local file or a file via the logserver we restart the dir update timer + if(!suspended_ && + (reply->fileReadMode() == VReply::LocalReadMode || + reply->fileReadMode() == VReply::LogServerReadMode)) + { + updateDirTimer_->start(); + } + //Update the selection in the dir list according to the file + if(f) + { + setCurrentInDir(f->sourcePath(),f->fetchMode()); + } +#if 0 + if(reply->tmpFile() && reply->fileReadMode() == VReply::LocalReadMode && + info_ && !info_->server()->isLocalHost()) + { + QString msg="The output file was read from disk but the server's \ + host (" + QString::fromStdString(info_->server()->host()) + + ") is not running on the local machine. If the path is machine-specific (e.g. /tmp) \ + and there exists a file with the same path on the local machine, then\ + this will have been read instead."; + + warnLabel_->showWarning(msg); + } +#endif + + + fetchInfo_->setInfo(reply,info_); + } + + //------------------------ + // From output dir provider + //------------------------ + else + { + //We do not display info/warning here! The dirMessageLabel_ is not part of the dirWidget_ + //and is only supposed to display error messages! + + enableDir(true); + + //Update the dir widget and select the proper file in the list + updateDir(reply->directories(),true); + + //Update the dir label + dirLabel_->update(reply); + + //Enable the update button + dirReloadTb_->setEnabled(true); +#if 0 + //Even though infoReady is called there could be some errors since we could + //try to read multiple directories + displayDirErrors(reply->errorTextVec()); +#endif + } +} + +void OutputItemWidget::infoProgress(VReply* reply) +{ + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); + //messageLabel_->startLoadLabel(); + //updateDir(true); +} + +void OutputItemWidget::infoProgressStart(const std::string& text,int max) +{ + messageLabel_->showInfo(QString::fromStdString(text)); + messageLabel_->startProgress(max); +} + +void OutputItemWidget::infoProgress(const std::string& text,int value) +{ + messageLabel_->progress(QString::fromStdString(text),value); +} + +void OutputItemWidget::infoFailed(VReply* reply) +{ + //File + if(reply->sender() == infoProvider_) + { + QString s=QString::fromStdString(reply->errorText()); + messageLabel_->showError(s); + messageLabel_->stopProgress(); + submittedWarning_=false; + + //Update the file label + fileLabel_->update(reply); + + userClickedReload_ = false; + reloadTb_->setEnabled(true); + + fetchInfo_->setInfo(reply,info_); + } + //Directories + else + { + //We do not have directories + enableDir(false); + + displayDirErrors(reply->errorTextVec()); + + dirReloadTb_->setEnabled(true); + + //the timer is stopped. It will be restarted again if we get a local file or + //a file via the logserver + updateDirTimer_->stop(); + } +} + +void OutputItemWidget::on_reloadTb__clicked() +{ + userClickedReload_ = true; + reloadTb_->setEnabled(false); + getCurrentFile(true); + //userClickedReload_ = false; +} + +//------------------------------------ +// Directory contents +//------------------------------------ + +void OutputItemWidget::on_dirReloadTb__clicked() +{ + dirReloadTb_->setEnabled(false); + updateDir(false); // get the directory listing +} + +void OutputItemWidget::setCurrentInDir(const std::string& fullName,VFile::FetchMode fetchMode) +{ + if(!dirModel_->isEmpty()) + { + //Try to preserve the selection + ignoreOutputSelection_=true; + + VDir::FetchMode fm=VDir::NoFetchMode; + switch(fetchMode) + { + case VFile::LocalFetchMode: + fm=VDir::LocalFetchMode; + break; + case VFile::ServerFetchMode: + fm=VDir::ServerFetchMode; + break; + case VFile::LogServerFetchMode: + fm=VDir::LogServerFetchMode; + break; + default: + break; + } + + QModelIndex idx=dirModel_->itemToIndex(fullName,fm); + if(idx.isValid()) + dirView_->setCurrentIndex(dirSortModel_->mapFromSource(idx)); + + ignoreOutputSelection_=false; + } +} + +void OutputItemWidget::updateDir(const std::vector& dirs,bool restartTimer) +{ +UI_FUNCTION_LOG + + if(restartTimer) + updateDirTimer_->stop(); + + bool status=false; + for(std::size_t i=0; i < dirs.size(); i++) + { + if(dirs[i] && dirs[i]->count() > 0) + { + status=true; + break; + } + } + + if(status) + { + OutputFileProvider* op=static_cast(infoProvider_); + op->setDirectories(dirs); + + std::string fullName; + VDir::FetchMode fetchMode; + currentDesc(fullName,fetchMode); + + dirView_->selectionModel()->clearSelection(); + dirModel_->setData(dirs,op->joboutFileName()); + //dirWidget_->show(); + + //Adjust column width + if(!dirColumnsAdjusted_) + { + dirColumnsAdjusted_=true; + for(int i=0; i< dirModel_->columnCount()-1; i++) + dirView_->resizeColumnToContents(i); + + if(dirModel_->columnCount() > 1) + dirView_->setColumnWidth(1,dirView_->columnWidth(0)); + + } +#ifdef _UI_OUTPUTITEMWIDGET_DEBUG + UiLog().dbg() << " dir item count=" << dirModel_->rowCount(); +#endif + //Try to preserve the selection + ignoreOutputSelection_=true; + QModelIndex idx=dirModel_->itemToIndex(fullName,fetchMode); + if(idx.isValid()) + dirView_->setCurrentIndex(dirSortModel_->mapFromSource(idx)); + + ignoreOutputSelection_=false; + } + else + { + //dirWidget_->hide(); + dirModel_->clearData(); + } + + if(restartTimer) + updateDirTimer_->start(updateDirTimeout_); +} + +void OutputItemWidget::updateDir(bool restartTimer) +{ + dirProvider_->info(info_); + + //Remember the selection + //std::string fullName=currentFullName(); + //updateDir(restartTimer,fullName); +} + +void OutputItemWidget::slotUpdateDir() +{ + updateDir(false); +} + +void OutputItemWidget::enableDir(bool status) +{ + if(status) + { + dirWidget_->show(); + dirMessageLabel_->hide(); + reloadTb_->setEnabled(true); + } + else + { + dirWidget_->hide(); + dirModel_->clearData(); + dirMessageLabel_->show(); + } +} + +void OutputItemWidget::displayDirErrors(const std::vector& errorVec) +{ + QString s; + if(errorVec.size() > 0) + { + QColor col(70,71,72); + s=Viewer::formatBoldText("Output directory: ",col); + + if(errorVec.size() > 1) + { + for(size_t i=0; i < errorVec.size(); i++) + s+=Viewer::formatBoldText("[" + QString::number(i+1) + "] ",col) + + QString::fromStdString(errorVec[i]) + ".   "; + } + else if(errorVec.size() == 1) + s+=QString::fromStdString(errorVec[0]); + } + + if(!s.isEmpty()) + dirMessageLabel_->showError(s); + else + dirMessageLabel_->hide(); +} + +//--------------------------------------------- +// Search +//--------------------------------------------- + +void OutputItemWidget::on_searchTb__clicked() +{ + browser_->showSearchLine(); +} + +void OutputItemWidget::on_gotoLineTb__clicked() +{ + browser_->gotoLine(); +} + + +// Called when we load a new node's information into the panel, or +// when we move to the panel from another one. +// If the search box is open, then search for the first matching item; +// otherwise, search for a pre-configured list of keywords. If none +// are found, and the user has clicked on the 'reload' button then +// we just go to the last line of the output +void OutputItemWidget::searchOnReload() +{ + browser_->searchOnReload(userClickedReload_); +} + +//This slot is called when a file item is selected in the dir view +void OutputItemWidget::slotOutputSelected(QModelIndex idx1,QModelIndex idx2) +{ + if(!ignoreOutputSelection_) + getCurrentFile(false); +} + +//----------------------------------------- +// Fontsize management +//----------------------------------------- + +void OutputItemWidget::on_fontSizeUpTb__clicked() +{ + //We need to call a custom slot here instead of "zoomIn"!!! + browser_->zoomIn(); +} + +void OutputItemWidget::on_fontSizeDownTb__clicked() +{ + //We need to call a custom slot here instead of "zoomOut"!!! + browser_->zoomOut(); +} + +//----------------------------------------- +// Save local copy of file +//----------------------------------------- + +void OutputItemWidget::on_saveFileAsTb__clicked() +{ + if (browser_->isFileLoaded()) + { + QString fileName = QFileDialog::getSaveFileName(this); + if (fileName.isEmpty()) + return; + + browser_->saveCurrentFile(fileName); + } + else + { + UserMessage::message(UserMessage::INFO,true,"No file loaded!"); + } +} + +//----------------------------------------- +// Copy file path +//----------------------------------------- + +void OutputItemWidget::on_copyPathTb__clicked() +{ + std::string fullName; + VDir::FetchMode fetchMode; + currentDesc(fullName,fetchMode); + + if(!fullName.empty()) + { + QString txt=QString::fromStdString(fullName); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif + } +} + +//------------------------- +// Update +//------------------------- + +void OutputItemWidget::nodeChanged(const VNode* n, const std::vector& aspect) +{ + //Changes in the nodes + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::DEFSTATUS || + *it == ecf::Aspect::SUSPENDED) + { + if(submittedWarning_) + getLatestFile(); + else if(info_ && info_->node() == n && info_->node()->isSubmitted()) + { + OutputFileProvider* op=static_cast(infoProvider_); + Q_ASSERT(op); + std::string fullName; + VDir::FetchMode fetchMode; + currentDesc(fullName,fetchMode); + if(fullName == op->joboutFileName()) + getLatestFile(); + } + return; + } + } +} + +static InfoPanelItemMaker maker1("output"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,87 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef OUTPUTITEMWIDGET_HPP_ +#define OUTPUTITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" + +#include "VFile.hpp" +#include "VDir.hpp" + +#include "ui_OutputItemWidget.h" + +class OutputDirProvider; +class OutputFetchInfo; +class OutputModel; +class OutputSortModel; + +class OutputItemWidget : public QWidget, public InfoPanelItem, protected Ui::OutputItemWidget +{ +Q_OBJECT + +public: + explicit OutputItemWidget(QWidget *parent=0); + ~OutputItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + void infoProgressStart(const std::string& text,int max); + void infoProgress(const std::string& text,int value); + + void nodeChanged(const VNode*, const std::vector&); + void defsChanged(const std::vector&) {} + +protected Q_SLOTS: + void slotOutputSelected(QModelIndex,QModelIndex); + void slotUpdateDir(); + void on_searchTb__clicked(); + void on_gotoLineTb__clicked(); + void on_reloadTb__clicked(); + void on_fontSizeUpTb__clicked(); + void on_fontSizeDownTb__clicked(); + void on_saveFileAsTb__clicked(); + void on_copyPathTb__clicked(); + void on_dirReloadTb__clicked(); + +protected: + void setCurrentInDir(const std::string&,VFile::FetchMode fetchMode); + void updateDir(bool); + void updateDir(const std::vector&,bool); + void enableDir(bool); + void updateState(const FlagSet&); + void searchOnReload(); + void getCurrentFile(bool doReload); + void getLatestFile(); + void currentDesc(std::string& fullName,VDir::FetchMode& fetchMode) const; + void updateHistoryLabel(const std::vector&); + void displayDirErrors(const std::vector& errorVec); + + OutputDirProvider* dirProvider_; + OutputModel* dirModel_; + OutputSortModel* dirSortModel_; + + bool userClickedReload_; + bool ignoreOutputSelection_; + QTimer* updateDirTimer_; + static int updateDirTimeout_; + OutputFetchInfo* fetchInfo_; + bool dirColumnsAdjusted_; + bool submittedWarning_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,432 @@ + + + OutputItemWidget + + + + 0 + 0 + 494 + 449 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + + 1 + + + + + + + + 0 + + + + + + + + + + + + Copy file path + + + Copy file path + + + + :/viewer/copy_path.svg:/viewer/copy_path.svg + + + true + + + + + + + Save a local copy of the current file to disk + + + ... + + + + :/viewer/filesaveas.svg:/viewer/filesaveas.svg + + + true + + + + + + + Additional information about the fetched output file + + + ... + + + + :/viewer/grey_info.svg:/viewer/grey_info.svg + + + QToolButton::InstantPopup + + + true + + + + + + + Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg + + + Ctrl++ + + + true + + + + + + + Descrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> + + + ... + + + + :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg + + + Ctrl+- + + + true + + + + + + + Show search bar (CTRL-F) + + + ... + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + Ctrl+F + + + false + + + true + + + + + + + Goto line number (CTRL-L) + + + ... + + + + :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg + + + Ctrl+L + + + true + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + Toggle to <b>show</b> text filter bar + + + ... + + + + :/viewer/filter_decor.svg:/viewer/filter_decor.svg + + + true + + + QToolButton::InstantPopup + + + true + + + + + + + Text filter menu + + + + + + + :/viewer/down_arrow.svg:/viewer/down_arrow.svg + + + QToolButton::InstantPopup + + + true + + + + + + + Qt::Horizontal + + + + 1 + 20 + + + + + + + + Reload the selected output file + + + ... + + + + :/viewer/sync.svg:/viewer/sync.svg + + + Ctrl+R + + + true + + + + + + + ... + + + + :/viewer/cogwheel.svg:/viewer/cogwheel.svg + + + QToolButton::InstantPopup + + + true + + + + + + + + + + + + + + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + Reload directory listing + + + ... + + + + :/viewer/sync_black.svg:/viewer/sync_black.svg + + + true + + + + + + + + + + 0 + 0 + + + + QFrame::Sunken + + + + + + + + + + + + + + + :/viewer/filesaveas.svg:/viewer/filesaveas.svg + + + Save file as ... + + + Save a local copy of the current task to disk + + + + + + :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg + + + Go to line ... + + + Go to line number + + + Ctrl+L + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    + + FileInfoLabel + QLabel +
    FileInfoLabel.hpp
    +
    + + DirInfoLabel + QLabel +
    FileInfoLabel.hpp
    +
    + + OutputBrowser + QWidget +
    OutputBrowser.hpp
    + 1 +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,425 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OutputModel.hpp" + +#include +#include +#include +#include +#include + +QColor OutputModel::joboutCol_=QColor(0,115,48); + +//======================================================================= +// +// OutputModel +// +//======================================================================= + +OutputModel::OutputModel(QObject *parent) : + QAbstractItemModel(parent) +{ +} + +void OutputModel::setData(const std::vector& dirs,const std::string& jobout) +{ + beginResetModel(); + dirs_=dirs; + joboutRows_.clear(); + + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + { + int idx=dirs_[i]->findByFullName(jobout); + if(idx != -1) + { + joboutRows_.insert(idx); + break; + } + } + } + + endResetModel(); +} + +void OutputModel::clearData() +{ + beginResetModel(); + dirs_.clear(); + endResetModel(); +} + +int OutputModel::columnCount( const QModelIndex& parent ) const +{ + return 6; +} + +int OutputModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + if(!parent.isValid()) + { + int cnt=0; + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + cnt+=dirs_[i]->count(); + } + return cnt; + } + + return 0; +} + +QVariant OutputModel::data(const QModelIndex& index, int role) const +{ + if(!hasData() || (role != Qt::DisplayRole && role != Qt::UserRole && + role != Qt::ForegroundRole && role != Qt::ToolTipRole && role != Qt::FontRole)) + return QVariant(); + + int row=index.row(); + VDir_ptr dir; + VDirItem *item=itemAt(row,dir); + + if(!item || !dir) + return QVariant(); + + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case 0: + return QString::fromStdString(item->name_); + case 1: + return QString::fromStdString(dir->path()); + case 2: + return formatSize(item->size_); + case 3: + return formatAgo(item->mtime_); + case 4: + return formatDate(item->mtime_); + case 5: + return QString::fromStdString(dir->fetchModeStr()); + default: + break; + } + } + else if(role == Qt::ForegroundRole) + { + if(joboutRows_.find(row) != joboutRows_.end()) + { + return joboutCol_; + } + } + else if(role == Qt::FontRole) + { + if(joboutRows_.find(row) != joboutRows_.end()) + { + QFont f; + f.setBold(true); + return f; + } + } + else if(role == Qt::ToolTipRole) + { + if(joboutRows_.find(row) != joboutRows_.end()) + { + return QString::fromStdString(item->name_) + " is the current job output file."; + } + } + //Used for sorting + else if(role == Qt::UserRole) + { + switch(index.column()) + { + case 0: + return QString::fromStdString(item->name_); + case 1: + return QString::fromStdString(dir->path()); + case 2: + return item->size_; + case 3: + return secsToNow(item->mtime_); + case 4: + return item->mtime_.toTime_t(); + case 5: + return QString::fromStdString(dir->fetchModeStr()); + default: + break; + } + } + + + return QVariant(); +} + +QVariant OutputModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || role != Qt::DisplayRole ) + return QAbstractItemModel::headerData( section, orient, role ); + + switch ( section ) + { + case 0: return tr("Name"); + case 1: return tr("Path"); + case 2: return tr("Size"); + case 3: return tr("Modified (ago)"); + case 4: return tr("Modified"); + case 5: return tr("Source"); + default: return QVariant(); + } + + return QVariant(); +} + +QModelIndex OutputModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column,static_cast(0)); + } + + return QModelIndex(); + +} + +QModelIndex OutputModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); + +} + +VDirItem* OutputModel::itemAt(int row,VDir_ptr& dir) const +{ + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + { + int cnt=dirs_[i]->count(); + if(row < cnt) + { + Q_ASSERT(row>=0); + dir=dirs_[i]; + return dirs_[i]->items()[row]; + } + + row-=cnt; + } + } + + return 0; +} + +bool OutputModel::hasData() const +{ + for(std::size_t i=0; i < dirs_.size(); i++) + if(dirs_[i]) + return true; + + return false; +} + +std::string OutputModel::fullName(const QModelIndex& index) const +{ + if(!hasData()) + return std::string(); + + int row=index.row(); + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + { + int cnt=dirs_[i]->count(); + if(row < cnt) + { + Q_ASSERT(row >=0); + return dirs_[i]->fullName(row); + } + row-=cnt; + } + } + + return std::string(); +} + +void OutputModel::itemDesc(const QModelIndex& index,std::string& itemFullName,VDir::FetchMode& mode) const +{ + itemFullName.clear(); + + if(!hasData()) + return; + + int row=index.row(); + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + { + int cnt=dirs_[i]->count(); + if(row < cnt) + { + Q_ASSERT(row >=0); + itemFullName=dirs_[i]->fullName(row); + mode=dirs_[i]->fetchMode(); + return; + } + row-=cnt; + } + } +} + +QModelIndex OutputModel::itemToIndex(const std::string& itemFullName,VDir::FetchMode fetchMode) const +{ + int row=0;; + for(std::size_t i=0; i < dirs_.size(); i++) + { + if(dirs_[i]) + { + if(dirs_[i]->fetchMode() == fetchMode) + { + for(int j=0; j < dirs_[i]->count(); j++, row++) + if(dirs_[i]->fullName(j) == itemFullName) + return index(row,0); + } + else + row+=dirs_[i]->count(); + } + } + return QModelIndex(); +} + + +QString OutputModel::formatSize(unsigned int size) const +{ + if(size < 1024) + return QString::number(size) + " B"; + else if(size < 1024*1024) + return QString::number(size/1024) + " KB"; + else if(size < 1024*1024*1024) + return QString::number(size/(1024*1024)) + " MB"; + else + return QString::number(size/(1024*1024*1024)) + " GB"; + + return QString(); +} + +QString OutputModel::formatDate(QDateTime dt) const +{ + //QDateTime dt=QDateTime::fromTime_t(t); + return dt.toString("yyyy-MM-dd hh:mm:ss"); +} + +QString OutputModel::formatAgo(QDateTime dt) const +{ + QString str=tr("Right now"); + + QDateTime now=QDateTime::currentDateTime(); + + int delta = dt.secsTo(now); + if(delta<0) delta = 0; + + if(delta ==1) + str=tr("1 second ago"); + + else if(delta >=1 && delta < 60) + { + str=QString::number(delta) + tr(" second") + ((delta==1)?tr(""):tr("s")) + tr(" ago"); + } + + else if(delta >= 60 && delta < 60*60) + { + int val=delta/60; + str=QString::number(val) + tr(" minute") + ((val==1)?tr(""):tr("s")) + tr(" ago"); + } + + else if(delta >= 60*60 && delta < 60*60*24) + { + int val=delta/(60*60); + str=QString::number(val) + tr(" hour") + ((val==1)?tr(""):tr("s")) + tr(" ago"); + } + + else if(delta >= 60*60*24) + { + int val=delta/(60*60*24); + str=QString::number(val) + tr(" day") + ((val==1)?tr(""):tr("s")) + tr(" ago"); + } + + return str; +} + +qint64 OutputModel::secsToNow(QDateTime dt) const +{ + QDateTime now=QDateTime::currentDateTime(); + + qint64 delta = dt.secsTo(now); + return (delta<0)?0:delta; +} + +//======================================================================= +// +// OutputSortModel +// +//======================================================================= + +OutputSortModel::OutputSortModel(QObject* parent) : + QSortFilterProxyModel(parent) +{ + +} + +QModelIndex OutputSortModel::fullNameToIndex(const std::string& fullName) +{ + QString name=QString::fromStdString(fullName); + + for(int i=0; i < rowCount(QModelIndex()); i++) + { + QModelIndex idx=index(i,0); + if(name.endsWith(data(idx,Qt::DisplayRole).toString())) + { + return idx; + } + } + return QModelIndex(); +} + +//======================================================== +// +// OutputDirLitsDelegate +// +//======================================================== + +OutputDirLitsDelegate::OutputDirLitsDelegate(QWidget *parent) : QStyledItemDelegate(parent) +{ +} + +void OutputDirLitsDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ + if(index.column()==1) + { + QStyleOptionViewItem vopt(option); + initStyleOption(&vopt, index); + vopt.textElideMode=Qt::ElideRight; + QStyledItemDelegate::paint(painter,vopt,index); + } + else + { + QStyledItemDelegate::paint(painter,option,index); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OutputModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OutputModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OutputModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OutputModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,72 @@ +#ifndef OUTPUTMODEL_H +#define OUTPUTMODEL_H + +#include +#include +#include +#include + +#include +#include + +#include "NodeObserver.hpp" +#include "VDir.hpp" +#include "VInfo.hpp" + +class OutputModel : public QAbstractItemModel +{ +public: + explicit OutputModel(QObject *parent=0); + + void setData(const std::vector&,const std::string& jobout); + void clearData(); + bool isEmpty() const {return (!hasData());} + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + //Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + std::string fullName(const QModelIndex& index) const; + void itemDesc(const QModelIndex& index,std::string& itemFullName,VDir::FetchMode& mode) const; + QModelIndex itemToIndex(const std::string& itemFullName,VDir::FetchMode fetchMode) const; + +protected: + VDirItem* itemAt(int row,VDir_ptr& dir) const; + bool hasData() const; + QString formatSize(unsigned int size) const; + QString formatDate(QDateTime) const; + QString formatAgo(QDateTime) const; + qint64 secsToNow(QDateTime dt) const; + + std::vector dirs_; + std::set joboutRows_; + static QColor joboutCol_; +}; + +//Filters and sorts the output +class OutputSortModel : public QSortFilterProxyModel +{ +public: + explicit OutputSortModel(QObject *parent=0); + ~OutputSortModel() {} + + QModelIndex fullNameToIndex(const std::string& fullName); +}; + +class OutputDirLitsDelegate : public QStyledItemDelegate +{ +public: + explicit OutputDirLitsDelegate(QWidget *parent=0); + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + + //QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OverviewItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OverviewItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OverviewItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OverviewItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,187 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OverviewItemWidget.hpp" +#include "Highlighter.hpp" +#include "OverviewProvider.hpp" +#include "VConfig.hpp" +#include "VReply.hpp" + +#include + + +//======================================================== +// +// InfoItemWidget +// +//======================================================== + +OverviewItemWidget::OverviewItemWidget(QWidget *parent) : + CodeItemWidget(parent), + lastScrollPos_(0) +{ + fileLabel_->hide(); + externalTb_->hide(); + copyPathTb_->hide(); + messageLabel_->setShowTypeTitle(false); + + textEdit_->setShowLineNumbers(false); + + //The document becomes the owner of the highlighter + new Highlighter(textEdit_->document(),"info"); + + infoProvider_=new OverviewProvider(this); + + //Editor font + textEdit_->setFontProperty(VConfig::instance()->find("panel.overview.font")); +} + +OverviewItemWidget::~OverviewItemWidget() +{ +} + +QWidget* OverviewItemWidget::realWidget() +{ + return this; +} + +void OverviewItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + //set the info + adjust(info); + + //Info must be a node + if(info_) + { + reloadTb_->setEnabled(false); + infoProvider_->info(info_); + } +} + +void OverviewItemWidget::reload() +{ + //Save the vertical scrollbar pos + lastScrollPos_=textEdit_->verticalScrollBar()->value(); + + textEdit_->clear(); + reloadTb_->setEnabled(false); + infoProvider_->info(info_); +} + +void OverviewItemWidget::clearContents() +{ + InfoPanelItem::clear(); + textEdit_->clear(); + reloadTb_->setEnabled(true); +} + +void OverviewItemWidget::infoReady(VReply* reply) +{ + Q_ASSERT(reply); + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); + + //Restore the vertical scrollbar pos + textEdit_->verticalScrollBar()->setValue(lastScrollPos_); + + reloadTb_->setEnabled(true); +} + +void OverviewItemWidget::infoProgress(VReply* reply) +{ + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); +} + +void OverviewItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); + textEdit_->setPlainText(s); + + reloadTb_->setEnabled(true); +} + +//At this point we can be sure that the node is handled by this item. +void OverviewItemWidget::nodeChanged(const VNode* node, const std::vector& aspect) +{ + if(frozen_) + return; + + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::ADD_REMOVE_NODE || *it == ecf::Aspect::ADD_REMOVE_ATTR || + *it == ecf::Aspect::DEFSTATUS || *it == ecf::Aspect::SUSPENDED || *it == ecf::Aspect::NODE_VARIABLE) + { + reload(); + return; + } + } +} + +void OverviewItemWidget::defsChanged(const std::vector& aspect) +{ + if(frozen_) + return; + + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::SERVER_STATE || *it == ecf::Aspect::SERVER_VARIABLE || *it == ecf::Aspect::ADD_REMOVE_ATTR) + { + reload(); + return; + } + } +} + +void OverviewItemWidget::connectStateChanged() +{ + if(frozen_) + return; + + reload(); +} + + +void OverviewItemWidget::reloadRequested() +{ + reload(); +} + +void OverviewItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + reloadTb_->setEnabled(false); + } + //Resume + else + { + if(info_ && info_->node()) + { + reloadTb_->setEnabled(true); + } + else + { + clearContents(); + } + } + } +} + +static InfoPanelItemMaker maker1("overview"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OverviewItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OverviewItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OverviewItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OverviewItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,45 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef OVERVIEWITEMWIDGET_HPP_ +#define OVERVIEWITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" +#include "CodeItemWidget.hpp" + +class OverviewItemWidget : public CodeItemWidget, public InfoPanelItem +{ +public: + explicit OverviewItemWidget(QWidget *parent=0); + ~OverviewItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&); + void defsChanged(const std::vector&); + void connectStateChanged(); + +protected: + void reload(); + void updateState(const ChangeFlags&); + void reloadRequested(); + + int lastScrollPos_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OverviewProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/OverviewProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OverviewProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OverviewProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,303 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "OverviewProvider.hpp" + +#include "ConnectState.hpp" +#include "ServerHandler.hpp" +#include "VAttribute.hpp" +#include "VNode.hpp" +#include "VNState.hpp" +#include "VSState.hpp" +#include "VFileInfo.hpp" + +OverviewProvider::OverviewProvider(InfoPresenter* owner) : InfoProvider(owner,VTask::NoTask) +{ + +} + +//================================================== +// Server +//================================================== + +void OverviewProvider::visit(VInfoServer* info) +{ + reply_->reset(); + + //Build the second part of the server info. We do not need + //information from the ClientInvoker for this!! + std::stringstream ss; + serverInfo(info,ss); + reply_->text(ss.str()); + + //If not connected we reply immediately! + if(info->server()->connectState()->state() != ConnectState::Normal) + { + owner_->infoReady(reply_); + return; + } + + //Define a task for getting the stats from the server. + //We need ClientInvoker for this + task_=VTask::create(VTask::StatsTask,this); + + //Run the task in the server. When it finish taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + info->server()->run(task_); +} + +//Node +void OverviewProvider::visit(VInfoNode* ni) +{ + reply_->reset(); + std::stringstream ss; + nodeInfo(ni,ss); + reply_->text(ss.str()); + owner_->infoReady(reply_); +} + +void OverviewProvider::visit(VInfoAttribute* ptr) +{ +} + +void OverviewProvider::taskChanged(VTask_ptr task) +{ + if(task_ != task) + return; + + switch(task->status()) + { + case VTask::FINISHED: + //We prepend the results to the existing text + reply_->prependText(task->reply()->text()); + owner_->infoReady(reply_); + //We do not need the task anymore. + task_.reset(); + break; + case VTask::ABORTED: + case VTask::CANCELLED: + case VTask::REJECTED: + reply_->prependText(task->reply()->text()); + owner_->infoFailed(reply_); + //We do not need the task anymore. + task_.reset();break; + default: + break; + } +} + +void OverviewProvider::serverInfo(VInfoServer* info,std::stringstream& f) +{ + static const std::string inc = " "; + + ServerHandler *server=info->server(); + if(!server) return; + VServer *snode=server->vRoot(); + + ConnectState* cst=server->connectState(); + + //If the server is not connected!! + if(cst->state() != ConnectState::Normal) + { + f << cst->describe() << "\n"; + f << inc << "Name : " << server->name() << "\n"; + f << inc << "Host : " << server->host() << "\n"; + f << inc << "Port : " << server->port() << "\n"; + + if(cst->state() == ConnectState::Lost) + { + f << inc << "Last connection attempt : " << VFileInfo::formatDate(cst->lastLostTime()).toStdString() << "\n"; + f << "\n"; + if(!cst->errorMessage().empty()) + { + f << "Error message:\n"; + f << cst->errorMessage(); + } + } + else if(cst->state() == ConnectState::Disconnected) + { + f << inc << "Disconnected : " << VFileInfo::formatDate(cst->lastDisconnectTime()).toStdString() << "\n"; + } + return; + } + + //if(!ServerDefsAccess(server).defs()) return; + + using namespace boost::posix_time; + using namespace boost::gregorian; + + std::string typeName="server"; + std::string nodeName=server->name(); + std::string statusName(VSState::toName(server).toStdString()); + std::string flags(snode->flagsAsStr()); + + //Header + f << "name : " << nodeName << "\n"; + f << "type : " << typeName << "\n"; + f << "status : " << statusName << "\n"; + + if(!flags.empty()) + f << "flags : " << flags << "\n"; + + f << "----------\n"; + //Start block: Type, name + f << typeName << " " << server->name() << "\n"; + + //Generated variables + std::vector gvar; + snode->genVariables(gvar); + for(std::vector::const_iterator it = gvar.begin(); it != gvar.end(); ++it) + { + f << inc << "# edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; + } + + //Variables + std::vector var; + snode->variables(var); + for(std::vector::const_iterator it = var.begin(); it != var.end(); ++it) + { + f << inc << "edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; + } + + //Print children + VNode *vr=server->vRoot(); + for(int i=0; i < vr->numOfChildren(); i++) + { + f << inc << vr->childAt(i)->nodeType() << " " << + vr->childAt(i)->strName() << "\n"; + } + + //End block + f << "end" << typeName << " # " << nodeName << "\n"; +} + +void OverviewProvider::nodeInfo(VInfoNode* info,std::stringstream& f) +{ + ServerHandler *server=info->server(); + if(!server) return; + //if(!ServerDefsAccess(server).defs()) return; + + VNode* node=info->node(); + if(!node) return; + + static const std::string inc = " "; + + using namespace boost::posix_time; + using namespace boost::gregorian; + + std::string typeName=node->nodeType(); + std::string nodeName(node->name().toStdString()); + std::string statusName(node->stateName().toStdString()); + std::string flags(node->flagsAsStr()); + + //Header + f << "name : " << nodeName << "\n"; + f << "type : " << typeName << "\n"; + f << "status : " << statusName << "\n"; + + if(!flags.empty()) + f << "flags : " << flags << "\n"; + + node_ptr nn=node->node(); + + boost::posix_time::ptime state_change_time = nn->state_change_time(); + if(!state_change_time.is_special()) + { + f << "at : " << boost::posix_time::to_simple_string(state_change_time) << "\n"; + } + + f << "----------\n"; + + if(node->isAborted()) + { + const std::string& abTxt=node->abortedReason(); + if(!abTxt.empty()) + { + f << "aborted reason : " + abTxt + "\n"; + f << "----------\n"; + } + } + + //Start block: Type, name + f << typeName << " " << nodeName << "\n"; + + //Clock information for suites + if(Suite *suite=nn->isSuite()) + { + //Suite* suite = dynamic_cast(nn); + // f << "clock : "; + if (suite->clockAttr()) + { + suite->clockAttr().get()->print(f); // f << "\n"; + } + } + + //Default status: the status the node should have when the begin/re-queue is called + //if(st != DState::QUEUED && st != DState::UNKNOWN) + f << inc << "defstatus " << node->defaultStateName().toStdString() << "\n"; + + //Zombies attribute + const std::vector & vect = nn->zombies(); + for (std::vector::const_iterator it = vect.begin(); it != vect.end(); ++it) + f << inc << it->toString() << "\n"; + + //Autocancel + if(nn->hasAutoCancel() && nn->get_autocancel()) + f << inc << nn->get_autocancel()->toString() << "\n"; + + //For suspended nodes + if(nn->isSuspended()) + { + f << inc << "# " << typeName << " " << nodeName << " is " << statusName << "\n"; + } + + if(nn->hasTimeDependencies()) + { + f << inc << "# time-date-dependencies: "; + if (nn->isTimeFree()) f << "free\n"; + else f << "holding\n"; + } + + //Generated variables + std::vector gvar; + node->genVariables(gvar); + for(std::vector::const_iterator it = gvar.begin(); it != gvar.end(); ++it) + { + f << inc << "# edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; + } + + //Variables + std::vector var; + node->variables(var); + for(std::vector::const_iterator it = var.begin(); it != var.end(); ++it) + { + f << inc << "edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; + } + + //Other attributes + const std::vector& attr=node->attr(); + for(std::vector::const_iterator it=attr.begin(); it != attr.end(); ++it) + { + if((*it)->typeName() != "var" && (*it)->typeName() != "genvar") + f << inc << (*it)->definition().toStdString() << "\n"; + } + + //Print children + for(int i=0; i < node->numOfChildren(); i++) + { + f << inc << node->childAt(i)->nodeType() << " " << + node->childAt(i)->strName() << "\n"; + } + + + //Here we should print some additional information from the attributes as well. It is not clear exactly what! + + //End block + f << "end" << typeName << " # " << nodeName << "\n"; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/OverviewProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/OverviewProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/OverviewProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/OverviewProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,36 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef OVERVIEWPROVIDER_HPP_ +#define OVERVIEWPROVIDER_HPP_ + +#include "InfoProvider.hpp" + +class InfoPanelItem; + +class OverviewProvider : public InfoProvider +{ +public: + explicit OverviewProvider(InfoPresenter* owner); + + //From VInfoVisitor + void visit(VInfoServer*); + void visit(VInfoNode*); + void visit(VInfoAttribute*); + + //From VTaskObserver + void taskChanged(VTask_ptr); + +protected: + void serverInfo(VInfoServer*,std::stringstream& f); + void nodeInfo(VInfoNode*,std::stringstream& f); + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,593 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "PlainTextEdit.hpp" + +#include "GotoLineDialog.hpp" + +#include +#include +#include +#include +#include +#include + +#include "VConfig.hpp" +#include "UiLog.hpp" + +PlainTextEdit::PlainTextEdit(QWidget * parent) : + QPlainTextEdit(parent), + showLineNum_(true), + rightMargin_(2), + hyperlinkEnabled_(false), + gotoLineDialog_(0), + numAreaBgCol_(232,231,230), + numAreaFontCol_(102,102,102), + numAreaSeparatorCol_(210,210,210), + numAreaCurrentCol_(212,212,255), + fontProp_(0) +{ + lineNumArea_ = new LineNumberArea(this); + + connect(this,SIGNAL(blockCountChanged(int)), + this,SLOT(updateLineNumberAreaWidth(int))); + + connect(this,SIGNAL(updateRequest(QRect,int)), + this,SLOT(updateLineNumberArea(QRect,int))); + + connect(this,SIGNAL(cursorPositionChanged()), + lineNumArea_,SLOT(update())); + + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaBackground")) + numAreaBgCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaFontColour")) + numAreaFontCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaSeparator")) + numAreaSeparatorCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaCurrent")) + numAreaCurrentCol_=p->value().value(); + + updateLineNumberAreaWidth(0); + + QFont f("Courier"); + //QFont f("Monospace"); + //f.setStyleHint(QFont::TypeWriter); + f.setFixedPitch(true); + f.setPointSize(10); + //f.setStyleStrategy(QFont::PreferAntialias); + setFont(f); +} + +PlainTextEdit::~PlainTextEdit() +{ + if (gotoLineDialog_) + delete gotoLineDialog_; + + if(fontProp_) + fontProp_->removeObserver(this); +} + +bool PlainTextEdit::setHyperlinkEnabled(bool h) +{ + hyperlinkEnabled_ = h; + setMouseTracking(h); + return true; +} + +void PlainTextEdit::setShowLineNumbers(bool b) +{ + showLineNum_ = b; + lineNumArea_->setVisible(b); + updateLineNumberAreaWidth(0); +} + + +// --------------------------------------------------------------------------- +// TextEdit::cursorRowCol +// returns the row and column position of the cursor +// - note that the first row and column are (1,1) +// --------------------------------------------------------------------------- + +void PlainTextEdit::cursorRowCol(int *row, int *col) +{ + const QTextCursor cursor = textCursor(); + + QTextBlock cb, b; + int column, line = 1; + cb = cursor.block(); + column = (cursor.position() - cb.position()) + 1; + + // find the line number - is there a better way than this? + + for (b = document()->begin(); b != document()->end(); b = b.next()) + { + if( b==cb ) break; + line++; + } + + *row = line; + *col = column; +} + + +// --------------------------------------------------------------------------- +// TextEdit::characterBehindCursor +// returns the character to the left of the text cursor +// --------------------------------------------------------------------------- + +QChar PlainTextEdit::characterBehindCursor(QTextCursor *cursor) +{ + QTextCursor docTextCursor = textCursor(); + QTextCursor *theCursor = (cursor == 0) ? &docTextCursor : cursor; + return document()->characterAt(theCursor->position() - 1); +} + +// --------------------------------------------------------------------------- +// TextEdit::numLinesSelected +// returns the number of lines in the current selection +// yes - all this code to do that! +// --------------------------------------------------------------------------- + +int PlainTextEdit::numLinesSelected() +{ + QTextCursor cursor = textCursor(); // get the document's cursor + int selStart = cursor.selectionStart(); + int selEnd = cursor.selectionEnd(); + QTextBlock bStart = document()->findBlock(selStart); + QTextBlock bEnd = document()->findBlock(selEnd); + int lineStart = bStart.firstLineNumber(); + int lineEnd = bEnd.firstLineNumber(); + int numLines = (lineEnd - lineStart) + 1; + + return numLines; +} + +// --------------------------------------------------------------------------- +// TextEdit::lineNumberAreaWidth +// returns the required width to display the line numbers. This adapts to the +// maximum number of digits we need to display. +// --------------------------------------------------------------------------- + +int PlainTextEdit::lineNumberAreaWidth() +{ + if (showLineNumbers()) + { + int digits = 1; + int max = qMax(1, blockCount()); + while (max >= 10) + { + max /= 10; + ++digits; + } + + int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits + rightMargin_; + + return space; + } + else + { + return 0; + } +} + +// --------------------------------------------------------------------------- +// TextEdit::updateLineNumberAreaWidth +// called when the number of lines in the document changes. The argument is +// the new number of lines (blocks). +// --------------------------------------------------------------------------- + +void PlainTextEdit::updateLineNumberAreaWidth(int) +{ + setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); +} + + +// --------------------------------------------------------------------------- +// TextEdit::updateLineNumberArea +// called when the editor is updated. We want to ensure that the line number +// widget stays in sync with it. +// --------------------------------------------------------------------------- + +void PlainTextEdit::updateLineNumberArea(const QRect &rect, int dy) +{ + if (dy) + lineNumArea_->scroll(0, dy); + else + lineNumArea_->update(0, rect.y(), lineNumArea_->width(), rect.height()); + + if (rect.contains(viewport()->rect())) + updateLineNumberAreaWidth(0); +} + + +// --------------------------------------------------------------------------- +// TextEdit::resizeEvent +// called when a resize event is triggered. Reset the size of the line widget. +// --------------------------------------------------------------------------- + +void PlainTextEdit::resizeEvent(QResizeEvent *e) +{ + QPlainTextEdit::resizeEvent(e); + + QRect cr = contentsRect(); + lineNumArea_->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); +} + + +// --------------------------------------------------------------------------- +// TextEdit::focusInEvent +// called when the widget gains input focus +// --------------------------------------------------------------------------- + +void PlainTextEdit::focusInEvent(QFocusEvent *event) +{ + Q_EMIT focusRegained(); + QPlainTextEdit::focusInEvent(event); +} + + +// --------------------------------------------------------------------------- +// TextEdit::focusOutEvent +// called when the widget loses input focus + +// --------------------------------------------------------------------------- + +void PlainTextEdit::focusOutEvent(QFocusEvent *event) +{ + Q_EMIT focusLost(); + QPlainTextEdit::focusOutEvent(event); +} + +// --------------------------------------------------------------------------- +// TextEdit::lineNumberAreaPaintEvent +// called when the line number widget needs to be repainted. This is where we +// actually draw the numbers on the widget. +// --------------------------------------------------------------------------- + +void PlainTextEdit::lineNumberAreaPaintEvent(QPaintEvent *event) +{ + int currentRow, currentCol; + cursorRowCol (¤tRow, ¤tCol); // get the current line number so we can highlight it + + + QPainter painter(lineNumArea_); + painter.fillRect(event->rect(), numAreaBgCol_); // light grey background + + painter.setPen(QPen(numAreaSeparatorCol_)); + painter.drawLine(event->rect().topRight(),event->rect().bottomRight()); + + QTextBlock block = firstVisibleBlock(); + int blockNumber = block.blockNumber(); + int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); + int bottom = top + (int) blockBoundingRect(block).height(); + QFont fontNormal(font()); // the font to use for most line numbers + QFont fontBold(fontNormal); // the font to use for the current line number + fontBold.setBold(true); + //painter.setPen(Qt::blue); + painter.setPen(numAreaFontCol_); + + painter.setFont(fontNormal); + + while (block.isValid() && top <= event->rect().bottom()) + { + if (block.isVisible() && bottom >= event->rect().top()) + { + QString number = QString::number(blockNumber + 1); + + if (blockNumber == currentRow-1) // is this the current line? + { + painter.setFont(fontBold); + painter.fillRect(0, top, lineNumArea_->width()-rightMargin_, fontMetrics().height(), numAreaCurrentCol_); // highlight the background + } + + + painter.drawText(0, top, lineNumArea_->width()-rightMargin_, fontMetrics().height(), // draw the line number + Qt::AlignRight, number); + + + if (blockNumber == currentRow-1) // is this the current line? + { + painter.setFont(fontNormal); // reset the font to normal + } + } + + block = block.next(); + top = bottom; + bottom = top + (int) blockBoundingRect(block).height(); + ++blockNumber; + } +} + + +QString PlainTextEdit::emptyString_ ; // used as a default argument to findString() + +bool PlainTextEdit::findString(const QString &s,QTextDocument::FindFlags flags, bool replace, const QString &r) +{ + + lastFindString_ = s; // store for repeat searches + lastFindFlags_ = flags; // store for repeat searches + bool found = false; + + if (find(s,flags)) // find and select the string - were we successful? + { + //statusMessage("", 0); + found = true; + } + else // did not find the string + { + if (1) // 'wraparound' search - on by default, we can add a user option if it might be useful to turn it off + { + QTextCursor original_cursor = textCursor(); // get the document's cursor + QTextCursor cursor(original_cursor); + + if (flags & QTextDocument::FindBackward) // move to the start or end of the document to continue the search + cursor.movePosition(QTextCursor::End); + else + cursor.movePosition(QTextCursor::Start); + + setTextCursor(cursor); // send the cursor back to the document + + if (find(s,flags)) // search again, from the new position + { + //statusMessage("", 0); + found = true; + } + else + { + setTextCursor(original_cursor); // not found - restore the cursor to its original position + } + } + } + + + if (found) + { + if (replace) + { + // perform the 'replace' + insertPlainText (r); + + // highlight the replaced text - the current text cursor will be + // at the end of the replaced text, so we move it back to the start + // (anchored so that the text is selected) + QTextCursor cursor = textCursor(); // get the document's cursor + cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, r.length()); + setTextCursor(cursor); // send the cursor back to the document + } + ensureCursorVisible(); + } + + else + { + //statusMessage(tr("Searched whole file, string not found"), 5000); + } + + return found; +} + + +// --------------------------------------------------------------------------- +// TextEdit::gotoLine +// triggered when the user asks to bring up the 'go to line' dialog +// --------------------------------------------------------------------------- + +void PlainTextEdit::gotoLine() +{ + // create the dialog if it does not already exist + + if (!gotoLineDialog_) + { + gotoLineDialog_ = new GotoLineDialog(this); + + connect(gotoLineDialog_, SIGNAL(gotoLine(int)), this, SLOT(gotoLine(int))); + } + + + // if created, set it up and display it + + if (gotoLineDialog_) + { + gotoLineDialog_->show(); + gotoLineDialog_->raise(); + gotoLineDialog_->activateWindow(); + gotoLineDialog_->setupUIBeforeShow(); + } +} + +// --------------------------------------------------------------------------- +// TextEdit::gotoLine +// triggered from the GotoLine dialog when the user wants to go to that line +// --------------------------------------------------------------------------- + +void PlainTextEdit::gotoLine(int line) +{ + int bn = 0; + QTextBlock b; + + if (line <= document()->blockCount()) + { + for (b = document()->begin(); b != document()->end(); b = b.next()) + { + if (bn == line-1) + { + QTextCursor cursor = textCursor(); // get the document's cursor + cursor.setPosition (b.position()); // set it to the right position + cursor.select(QTextCursor::LineUnderCursor); // select the whole line + setTextCursor(cursor); // send the cursor back to the document + break; + } + bn++; + } + } + + else + { + // line number outside range of line numbers + // TODO: disable the 'ok' button if the number is out of range + } +} + +//--------------------------------------------- +// Fontsize management +//--------------------------------------------- + +void PlainTextEdit::setFontProperty(VProperty* p) +{ + fontProp_=p; + fontProp_->addObserver(this); + updateFont(); +} + +void PlainTextEdit::wheelEvent(QWheelEvent *event) +{ + int fps=font().pointSize(); + + if(isReadOnly()) + { + QPlainTextEdit::wheelEvent(event); + if(font().pointSize() != fps) + fontSizeChangedByZoom(); + } + //For readOnly document the zoom does not work so we + //need this custom code! + else + { + if(event->modifiers() & Qt::ControlModifier) + { + const int delta = event->delta(); + if (delta < 0) + slotZoomOut(); + else if (delta > 0) + slotZoomIn(); + return; + } + + QPlainTextEdit::wheelEvent(event); + } +} + +void PlainTextEdit::slotZoomIn() +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 1) + zoomIn(); +#else + QFont f=font(); + int fps=f.pointSize(); + f.setPointSize(fps+1); + setFont(f); +#endif + fontSizeChangedByZoom(); +} + +void PlainTextEdit::slotZoomOut() +{ + int oriSize=font().pointSize(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 1) + zoomOut(); +#else + QFont f=font(); + int fps=f.pointSize(); + if(fps > 1) + { + f.setPointSize(fps-1); + setFont(f); + } +#endif + + if(font().pointSize() != oriSize) + fontSizeChangedByZoom(); +} + +void PlainTextEdit::fontSizeChangedByZoom() +{ + if(fontProp_) + fontProp_->setValue(font()); +} + +void PlainTextEdit::updateFont() +{ + if(fontProp_) + { + QFont f=fontProp_->value().value(); + if(font() != f) + setFont(f); + } +} + +void PlainTextEdit::notifyChange(VProperty* p) +{ + if(fontProp_ ==p) + { + setFont(p->value().value()); + } +} + + +void PlainTextEdit::mousePressEvent(QMouseEvent *e) +{ + if (hyperlinkEnabled_) + { + // left button only - if pressed, we just store the link that was clicked on + // - we don't want to do anything until the mouse button has been released and + // we know it hasd not been moved away from the hyperlinked text + + if (e->button() & Qt::LeftButton) + currentLink_ = anchorAt(e->pos()); + else + currentLink_ = QString(); + } + + QPlainTextEdit::mousePressEvent(e); +} + +void PlainTextEdit::mouseReleaseEvent(QMouseEvent *e) +{ + if (hyperlinkEnabled_) + { + // only activate the hyperlink if the user releases the left mouse button on the + // same link they were on when they pressed the button + + if ((e->button() & Qt::LeftButton) && !currentLink_.isEmpty()) + { + if (currentLink_ == anchorAt(e->pos())) + { + Q_EMIT hyperlinkActivated(currentLink_); + UiLog().dbg() << "clicked:" << currentLink_; + } + } + } + + QPlainTextEdit::mouseReleaseEvent(e); +} + + +void PlainTextEdit::mouseMoveEvent(QMouseEvent *e) +{ + if (hyperlinkEnabled_) + { + QString thisAnchor = anchorAt(e->pos()); + + if (!thisAnchor.isEmpty()) + { + viewport()->setCursor(Qt::PointingHandCursor); + } + else + { + viewport()->unsetCursor(); + } + } + + QPlainTextEdit::mouseMoveEvent(e); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,106 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TEXTEDIT_HPP_ +#define TEXTEDIT_HPP_ + +#include + +#include "VProperty.hpp" + +class LineNumberArea; +class GotoLineDialog; + +class PlainTextEdit : public QPlainTextEdit, public VPropertyObserver +{ +Q_OBJECT + +public: + explicit PlainTextEdit(QWidget* parent = 0); + ~PlainTextEdit(); + + void lineNumberAreaPaintEvent(QPaintEvent *event); + int lineNumberAreaWidth(); + + bool showLineNumbers() {return showLineNum_;} + void setShowLineNumbers(bool b); + + void cursorRowCol(int *row, int *col); + QChar characterBehindCursor(QTextCursor *cursor=0); + + int numLinesSelected(); + bool findString(const QString &,QTextDocument::FindFlags,bool replace=false,const QString &r=emptyString_); + + void setFontProperty(VProperty* p); + void updateFont(); + void notifyChange(VProperty* p); + bool isHyperlinkEnabled() {return hyperlinkEnabled_;} + bool setHyperlinkEnabled(bool h); + +public Q_SLOTS: + void gotoLine(); + void slotZoomIn(); + void slotZoomOut(); + +private Q_SLOTS: + void updateLineNumberAreaWidth(int newBlockCount); + void updateLineNumberArea(const QRect &, int); + void gotoLine(int line); + +Q_SIGNALS: + void focusRegained (); + void focusLost(); + void fontSizeChangedByWheel(); + void hyperlinkActivated(QString link); + +protected: + void resizeEvent(QResizeEvent *event); + void focusInEvent(QFocusEvent *event); + void focusOutEvent(QFocusEvent *event); + void wheelEvent(QWheelEvent *event); + void mousePressEvent(QMouseEvent *e); + void mouseReleaseEvent(QMouseEvent *e); + void mouseMoveEvent(QMouseEvent *e); + +private: + void fontSizeChangedByZoom(); + + bool showLineNum_; + QWidget *lineNumArea_; + int rightMargin_; + bool hyperlinkEnabled_; + QString lastFindString_; + QString currentLink_; + QTextDocument::FindFlags lastFindFlags_; + GotoLineDialog *gotoLineDialog_; + static QString emptyString_; + + QColor numAreaBgCol_; + QColor numAreaFontCol_; + QColor numAreaSeparatorCol_; + QColor numAreaCurrentCol_; + VProperty *fontProp_; +}; + + +class LineNumberArea : public QWidget +{ +public: + explicit LineNumberArea(PlainTextEdit *editor) : QWidget(editor) {textEditor = editor;} + QSize sizeHint() const {return QSize(textEditor->lineNumberAreaWidth(), 0);} + +protected: + void paintEvent(QPaintEvent *event) { textEditor->lineNumberAreaPaintEvent(event);} + +private: + PlainTextEdit *textEditor; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchInterface.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchInterface.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchInterface.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,232 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "PlainTextSearchInterface.hpp" + +#include + + +PlainTextSearchInterface::PlainTextSearchInterface() : editor_(NULL) +{ +} + + +bool PlainTextSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode) +{ + if(!editor_) + return false; + + if(editor_->document()->isEmpty()) + return false; + + QTextCursor cursor(editor_->textCursor()); + + if (highlightAll) // if highlighting all matches, start from the start of the document + cursor.movePosition(QTextCursor::Start); + + else // move the cursor? + cursor.movePosition(move); + + + QList extraSelections; + bool found = false; + bool keepGoing = true; + int numMatches = 0; + + Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; + + while (keepGoing) + { + switch (matchMode) + { + case StringMatchMode::ContainsMatch: + { + cursor = editor_->document()->find(str, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + case StringMatchMode::WildcardMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + regexp.setPatternSyntax(QRegExp::Wildcard); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + case StringMatchMode::RegexpMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + + default: + { + break; + } + } + + + if (found) + { + if (highlightAll) + { + QTextEdit::ExtraSelection highlight; + highlight.cursor = cursor; + highlight.format.setBackground(highlightColour_); + extraSelections << highlight; + numMatches++; + } + else + { + editor_->setTextCursor(cursor); // mark the selection of the match + } + } + + + if (found && !highlightAll) // found a match and we only want one - stop here and select it + keepGoing = false; + + else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around + keepGoing = false; + + if (!found && highlightAll) // want to highlight all, but no more matches found + keepGoing = false; + + + + // not found, and we only want one match, then we need to wrap around and keep going + if (keepGoing) + { + if (!highlightAll) + { + cursor=editor_->textCursor(); + if (flags & QTextDocument::FindBackward) + cursor.movePosition(QTextCursor::End); + else + cursor.movePosition(QTextCursor::Start); + iteration = 1; // iteration=1 to avoid infinite wraparound! + } + } + } + + + if (highlightAll) + { + //char num[64]; + //sprintf(num, "%d", numMatches); + //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); + + editor_->setExtraSelections( extraSelections ); + } + + return (found); +} + +void PlainTextSearchInterface::automaticSearchForKeywords(bool userClickedReload) +{ + if(editor_->document()->isEmpty()) + return; + + bool performSearch = vpPerformAutomaticSearch_->value().toBool(); + + if (performSearch) + { + // search direction + QTextDocument::FindFlags findFlags; + QTextCursor cursor(editor_->textCursor()); + std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); + QTextCursor::MoveOperation move; + if (searchFrom == "bottom") + { + findFlags = QTextDocument::FindBackward; + move = QTextCursor::End; + } + else + { + move = QTextCursor::Start; + } + + // case sensitivity + bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); + if (caseSensitive) + findFlags = findFlags | QTextDocument::FindCaseSensitively; + + // string match mode + std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); + StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); + + // the term to be searched for + std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); + QString searchTerm = QString::fromStdString(searchTerm_s); + + // perform the search + bool found = findString (searchTerm, false, findFlags, move, 1, mode); + + if(!found) + { + if(userClickedReload) + { + // move the cursor to the start of the last line + gotoLastLine(); + } + } + } + else + { + // move the cursor to the start of the last line + gotoLastLine(); + } +} + +void PlainTextSearchInterface::refreshSearch() +{ + if(!editor_) + return; + + QTextCursor cursor(editor_->textCursor()); + if (cursor.hasSelection()) + { + cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); + editor_->setTextCursor(cursor); + } +} + + +void PlainTextSearchInterface::clearHighlights() +{ + if(!editor_) + return; + + QList empty; + editor_->setExtraSelections(empty); +} + +void PlainTextSearchInterface::disableHighlights() +{ + clearHighlights(); +} + + +void PlainTextSearchInterface::gotoLastLine() +{ + // move the cursor to the start of the last line + QTextCursor cursor = editor_->textCursor(); + cursor.movePosition(QTextCursor::End); + cursor.movePosition(QTextCursor::StartOfLine); + editor_->setTextCursor(cursor); + editor_->ensureCursorVisible(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchInterface.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchInterface.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchInterface.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ +#define VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ + +#include "AbstractTextEditSearchInterface.hpp" + +class QPlainTextEdit; + +class PlainTextSearchInterface : public AbstractTextEditSearchInterface +{ +public: + PlainTextSearchInterface(); + void setEditor(QPlainTextEdit* e) {editor_=e;} + + bool findString (QString str, bool highlightAll, QTextDocument::FindFlags findFlags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); + + void automaticSearchForKeywords(bool); + void refreshSearch(); + void clearHighlights(); + void disableHighlights(); + void enableHighlights() {} + bool highlightsNeedSearch() {return true;} + void gotoLastLine(); + +protected: + + QPlainTextEdit *editor_; + +}; + +#endif /* VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,333 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "PlainTextSearchLine.hpp" +#include "PlainTextSearchInterface.hpp" + +#include + +PlainTextSearchLine::PlainTextSearchLine(QWidget *parent) : + TextEditSearchLine(parent) +{ + interface_=new PlainTextSearchInterface; + TextEditSearchLine::setSearchInterface(interface_); +} + +PlainTextSearchLine::~PlainTextSearchLine() +{ + delete interface_; +} + +void PlainTextSearchLine::setEditor(QPlainTextEdit *e) +{ + PlainTextSearchInterface *pti=static_cast(interface_); + assert(pti); + pti->setEditor(e); +} + + +#if 0 +PlainTextSearchLine::PlainTextSearchLine(QWidget *parent) : + AbstractSearchLine(parent), + editor_(0), + highlightColour_(QColor(200, 255, 200)) +{ + connect(matchModeCb_,SIGNAL(currentIndexChanged(int)), + this, SLOT(matchModeChanged(int))); + + if(VProperty *p=VConfig::instance()->find("panel.search.highlightColour")) + { + highlightColour_=p->value().value(); + } +} + +PlainTextSearchLine::~PlainTextSearchLine() +{ + +} + +void PlainTextSearchLine::setEditor(QPlainTextEdit *e) +{ + editor_=e; +} + + +bool PlainTextSearchLine::findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, bool gotoStartOfWord, int iteration) +{ + QTextDocument::FindFlags flags = findFlags() | extraFlags; + + QTextCursor cursor(editor_->textCursor()); + + if (highlightAll) // if highlighting all matches, start from the start of the document + cursor.movePosition(QTextCursor::Start); + + else if (gotoStartOfWord) // go to start of word? + cursor.movePosition(QTextCursor::StartOfWord); + + + QList extraSelections; + bool found = false; + bool keepGoing = true; + int numMatches = 0; + + Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; + + while (keepGoing) + { + switch (matchModeCb_->currentMatchMode()) + { + case StringMatchMode::ContainsMatch: + { + cursor = editor_->document()->find(str, cursor, flags); // perform the search + found = (!cursor.isNull()); + + break; + } + case StringMatchMode::WildcardMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + regexp.setPatternSyntax(QRegExp::Wildcard); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + + break; + } + case StringMatchMode::RegexpMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + + break; + } + + default: + { + break; + } + } + + + if (found) + { + if (highlightAll) + { + QTextEdit::ExtraSelection highlight; + highlight.cursor = cursor; + highlight.format.setBackground(highlightColour_); + extraSelections << highlight; + numMatches++; + } + else + { + editor_->setTextCursor(cursor); // mark the selection of the match + } + } + + + if (found && !highlightAll) // found a match and we only want one - stop here and select it + keepGoing = false; + + else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around + keepGoing = false; + + if (!found && highlightAll) // want to highlight all, but no more matches found + keepGoing = false; + + + + // not found, and we only want one match, then we need to wrap around and keep going + if (keepGoing) + { + if (!highlightAll) + { + cursor=editor_->textCursor(); + if (extraFlags & QTextDocument::FindBackward) + cursor.movePosition(QTextCursor::End); + else + cursor.movePosition(QTextCursor::Start); + editor_->setTextCursor(cursor); + iteration = 1; // iteration=1 to avoid infinite wraparound! + } + } + } + + + if (highlightAll) + { + //char num[64]; + //sprintf(num, "%d", numMatches); + //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); + + editor_->setExtraSelections( extraSelections ); + } + + return (found); +} + + + +void PlainTextSearchLine::highlightMatches(QString txt) +{ + if (!txt.isEmpty()) + findString(txt, true, 0, true, 0); // highlight all matches +} + + +void PlainTextSearchLine::slotHighlight() +{ + //UserMessage::message(UserMessage::DBG, false," highlight: " + searchLine_->text().toStdString()); + + highlightAllTimer_.stop(); + + if (highlightAll()) + highlightMatches(searchLine_->text()); +} + + +void PlainTextSearchLine::slotFind(QString txt) +{ + if(!editor_) + return; + + highlightAllTimer_.stop(); + bool found = findString(txt, false, 0, true, 0); // find the next match + + if (!isEmpty()) // there is a search term supplied by the user + { + // don't highlight the matches immediately - this can be expensive for large files, + // and we don't want to highlight each time the user types a new character; wait + // a moment and then start the highlight + highlightAllTimer_.setInterval(500); + highlightAllTimer_.disconnect(); + connect(&highlightAllTimer_, SIGNAL(timeout()), this, SLOT(slotHighlight())); + highlightAllTimer_.start(); + } + else + { + clearHighlights(); + } + + updateButtons(found); +} + +void PlainTextSearchLine::slotFindNext() +{ + if(!editor_) + return; + + bool found = findString(searchLine_->text(), false, 0, false, 0); + updateButtons(found); +} + +void PlainTextSearchLine::slotFindPrev() +{ + if(!editor_) + return; + + bool found = findString(searchLine_->text(), false, QTextDocument::FindBackward, false, 0); + updateButtons(found); +} + +QTextDocument::FindFlags PlainTextSearchLine::findFlags() +{ + QTextDocument::FindFlags flags; + + if(caseSensitive()) + { + flags = flags | QTextDocument::FindCaseSensitively; + } + + if(wholeWords()) + { + flags = flags | QTextDocument::FindWholeWords; + } + + return flags; +} + + + + + +// PlainTextSearchLine::refreshSearch +// performed when the user changes search parameters such as case sensitivity - we want to +// re-do the search from the current point, but if the current selection still matches then +// we'd like it to be found first. + +void PlainTextSearchLine::refreshSearch() +{ + // if there's something selected already then move the cursor to the start of the line and search again + QTextCursor cursor(editor_->textCursor()); + if (cursor.hasSelection()) + { + cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); + editor_->setTextCursor(cursor); + } + slotFindNext(); + slotHighlight(); +} + + +void PlainTextSearchLine::clearHighlights() +{ + QList empty; + editor_->setExtraSelections(empty); +} + + +void PlainTextSearchLine::matchModeChanged(int notUsed) +{ + if(matchModeCb_->currentMatchMode() == StringMatchMode::ContainsMatch) + actionWholeWords_->setEnabled(true); + else + actionWholeWords_->setEnabled(false); + + refreshSearch(); +} + + +void PlainTextSearchLine::on_actionCaseSensitive__toggled(bool b) +{ + AbstractSearchLine::on_actionCaseSensitive__toggled(b); + + refreshSearch(); +} + + +void PlainTextSearchLine::on_actionWholeWords__toggled(bool b) +{ + AbstractSearchLine::on_actionWholeWords__toggled(b); + + refreshSearch(); +} + +void PlainTextSearchLine::on_actionHighlightAll__toggled(bool b) +{ + AbstractSearchLine::on_actionHighlightAll__toggled(b); + + if (b) // user switched on the highlights + slotHighlight(); + else // user switched off the highlights + clearHighlights(); + + + refreshSearch(); +} + +void PlainTextSearchLine::slotClose() +{ + AbstractSearchLine::slotClose(); + clearHighlights(); +} +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PlainTextSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PlainTextSearchLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,69 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef PLAINTEXTSEARCHLINE_HPP_ +#define PLAINTEXTSEARCHLINE_HPP_ + +#include + +#include "TextEditSearchLine.hpp" + +class AbstractTextSearchInterface; + +class PlainTextSearchLine : public TextEditSearchLine +{ +public: + explicit PlainTextSearchLine(QWidget *parent=0); + ~PlainTextSearchLine(); + void setEditor(QPlainTextEdit*); + +private: + //The interface is set internally + void setSearchInterface(AbstractTextSearchInterface*) {} + +}; + + +#if 0 +class PlainTextSearchLine : public AbstractSearchLine +{ + Q_OBJECT + +public: + explicit PlainTextSearchLine(QWidget *parent); + ~PlainTextSearchLine(); + void setEditor(QPlainTextEdit*); + +public Q_SLOTS: + void slotFind(QString); + void slotFindNext(); + void slotFindPrev(); + void slotFindNext(bool) {slotFindNext();} + void slotFindPrev(bool) {slotFindPrev();} + void matchModeChanged(int newIndex); + void on_actionCaseSensitive__toggled(bool); + void on_actionWholeWords__toggled(bool); + void on_actionHighlightAll__toggled(bool); + void slotClose(); + void slotHighlight(); + +protected: + QTextDocument::FindFlags findFlags(); + bool findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, bool gotoStartOfWord, int iteration); + void refreshSearch(); + void highlightMatches(QString txt); + void clearHighlights(); + QTimer highlightAllTimer_; + QPlainTextEdit* editor_; + QColor highlightColour_; +}; +#endif + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,237 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "PropertyDialog.hpp" + +#include +#include +#include + +#include "ConfigListDelegate.hpp" +#include "IconProvider.hpp" +#include "PropertyEditor.hpp" +#include "SessionHandler.hpp" +#include "VConfig.hpp" +#include "VConfigLoader.hpp" +#include "VProperty.hpp" +#include "WidgetNameProvider.hpp" + +VProperty* PropertyDialog::prop_=0; + +PropertyDialog::PropertyDialog(QWidget* parent) : + QDialog(parent), + configChanged_(false) +{ + setupUi(this); + + QString wt=windowTitle(); + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + QFont f; + f.setBold(true); + QFontMetrics fm(f); + int maxW=fm.width("Server options ATAT"); + list_->setMaximumWidth(maxW+6); + list_->setFont(f); + + list_->setItemDelegate(new ConfigListDelegate(32,maxW,this)); + + connect(list_,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this, SLOT(slotChangePage(QListWidgetItem*,QListWidgetItem*))); + + connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), + this,SLOT(slotButton(QAbstractButton*))); + + build(); + + readSettings(); + + if(list_->count() >0 && list_->currentRow() == -1) + list_->setCurrentRow(0); + + //Assign name to each object + WidgetNameProvider::nameChildren(this); +} + +void PropertyDialog::closeEvent(QCloseEvent * event) +{ + event->accept(); + writeSettings(); +} + +//Build the property tree from the the definitions +void PropertyDialog::build() +{ + if(prop_) + { + Q_FOREACH(VProperty* vPage,prop_->children()) + { + if(vPage->param("visible") == "false") + continue; + + QPixmap pix(32,32); + QPixmap edPix; + QString iconStr=vPage->param("icon"); + + if(!iconStr.isEmpty()) + { + IconProvider::add(":/viewer/" + iconStr,iconStr); + pix=IconProvider::pixmap(iconStr,32); + edPix=IconProvider::pixmap(iconStr,20); + } + + PropertyEditor* ed=new PropertyEditor(this); + ed->setObjectName(vPage->param("label")); + ed->edit(vPage,edPix); + + addPage(ed,pix,vPage->param("label")); + editors_ << ed; + } + } +} + +void PropertyDialog::apply() +{ + manageChange(true); +} + +void PropertyDialog::accept() +{ + manageChange(false); + writeSettings(); + QDialog::accept(); +} + + +void PropertyDialog::reject() +{ + writeSettings(); + QDialog::reject(); +} + +void PropertyDialog::addPage(QWidget *w,QPixmap pix,QString txt) +{ + QListWidgetItem *item = new QListWidgetItem(list_); + item->setData(Qt::DecorationRole, pix); + item->setData(Qt::DisplayRole,txt); + item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + + page_->addWidget(w); +} + +void PropertyDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) +{ + if (!current) + current = previous; + + page_->setCurrentIndex(list_->row(current)); +} + +void PropertyDialog::slotButton(QAbstractButton* pb) +{ + if(buttonBox_->buttonRole(pb) == QDialogButtonBox::ApplyRole) + { + apply(); + } +} + +void PropertyDialog::showPage(QString path) +{ + QStringList lst=path.split("."); + if(lst.count() > 0) + { + QString pageName=lst[0]; + for(int i=0; i < editors_.count(); i++) + { + PropertyEditor *ed=editors_[i]; + if(VProperty* prop=ed->property()) + { + if(prop->name() == pageName) + { + list_->setCurrentRow(i); + return; + } + } + } + } +} + +void PropertyDialog::manageChange(bool inApply) +{ + bool hasChange=false; + Q_FOREACH(PropertyEditor* ed,editors_) + { + if(ed->applyChange()) + { + hasChange=true; + VProperty* p=ed->property(); + if(p && p->name() != "server") + { + if(inApply) + Q_EMIT configChanged(); + else + configChanged_=true; + } + } + } + + if(hasChange) + VConfig::instance()->saveSettings(); +} + +void PropertyDialog::load(VProperty* p) +{ + prop_=p; +} + +void PropertyDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("PropertyDialog")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.setValue("current",list_->currentRow()); + settings.endGroup(); +} + +void PropertyDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("PropertyDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(550,540)); + } + + if(settings.contains("current")) + { + int current=settings.value("current").toInt(); + if(current >=0) + list_->setCurrentRow(current); + } + settings.endGroup(); +} + + +static SimpleLoader loader("gui"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,64 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef PROPERTYDIALOG_INC_ +#define PROPERTYDIALOG_INC_ + +#include "ui_PropertyDialog.h" + +#include + +class QAbstractButton; + +class PropertyEditor; +class VProperty; + +class PropertyDialog : public QDialog, private Ui::PropertyDialog +{ + +Q_OBJECT + +public: + explicit PropertyDialog(QWidget *parent=0); + ~PropertyDialog() {} + + bool isConfigChanged() const {return configChanged_;} + void showPage(QString); + + //Called from VConfigLoader + static void load(VProperty*); + +public Q_SLOTS: + void accept(); + void reject(); + void slotChangePage(QListWidgetItem *current, QListWidgetItem *previous); + void slotButton(QAbstractButton*); + +Q_SIGNALS: + void configChanged(); + +private: + void build(); + void addPage(QWidget *w,QPixmap pix,QString txt); + void manageChange(bool); + void apply(); + + void closeEvent(QCloseEvent * event); + void readSettings(); + void writeSettings(); + + QList editors_; + bool configChanged_; + + static VProperty* prop_; + +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,118 @@ + + + PropertyDialog + + + + 0 + 0 + 924 + 695 + + + + Preferences + + + true + + + + 2 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + 4 + + + 2 + + + 2 + + + 2 + + + 2 + + + + + + 0 + 0 + + + + + + + + + 1 + 0 + + + + + + + + + + QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox_ + accepted() + PropertyDialog + accept() + + + 199 + 278 + + + 199 + 149 + + + + + buttonBox_ + rejected() + PropertyDialog + reject() + + + 199 + 278 + + + 199 + 149 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,609 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "PropertyEditor.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "ChangeNotifyEditor.hpp" +#include "IconProvider.hpp" +#include "PropertyLine.hpp" +#include "VConfig.hpp" +#include "VProperty.hpp" + +PropertyEditor::PropertyEditor(QWidget* parent) : QWidget(parent), + group_(0), + currentGrid_(0), + holder_(0), + lineLabelLen_(-1) +{ + setupUi(this); + + headerWidget_->setProperty("editorHeader","1"); + scArea_->setProperty("editor","1"); + scAreaContents_->setProperty("editorArea","1"); + + pixLabel_->clear(); +} + +PropertyEditor::~PropertyEditor() +{ +} + +void PropertyEditor::edit(VProperty * vGroup,QPixmap pix) +{ + clear(); + + group_=vGroup; + + QString txt=group_->param("desc"); + headerLabel_->setText(txt); + + pixLabel_->setPixmap(pix); + + build(); +} + +void PropertyEditor::edit(VProperty * vGroup,QString serverName) +{ + clear(); + + group_=vGroup; + + headerWidget_->hide(); + + serverName_=serverName; + + build(); +} + +void PropertyEditor::clear() +{ + if(holder_) + { + vBox_->removeWidget(holder_); + delete holder_; + holder_=NULL; + } + + currentGrid_=0; + lineItems_.clear(); +} + + +//Build the property tree from the the definitions +void PropertyEditor::build() +{ + if(!group_) + return; + + assert(holder_==NULL); + + holder_=new QWidget(scAreaContents_); + holder_->setObjectName("h"); + QVBoxLayout *vb=new QVBoxLayout(holder_); + vb->setContentsMargins(0,0,0,0); + vBox_->addWidget(holder_); + + //Loop over the children of the group + Q_FOREACH(VProperty* vProp,group_->children()) + { + addItem(vProp,vb,holder_); + } + + addRules(); + addHelpers(); +} + +void PropertyEditor::addRules() +{ + Q_FOREACH(PropertyLine* line,lineItems_) + { + if(VProperty* ruleProp=line->ruleProperty()) + { + Q_FOREACH(PropertyLine* ll,lineItems_) + { + if(ll->property() == ruleProp) + { + line->addRuleLine(ll); + break; + } + } + } + } +} + + +void PropertyEditor::addHelpers() +{ + QMap lineMap; + Q_FOREACH(PropertyLine* line,lineItems_) + { + lineMap[line->property()->path()]=line; + } + + Q_FOREACH(PropertyLine* line,lineItems_) + { + QString h=line->guiProperty()->param("helpers"); + if(!h.isEmpty()) + { + Q_FOREACH(QString s,h.split("/")) + { + if(PropertyLine* hl=lineMap.value(s.toStdString(),NULL)) + { + line->addHelper(hl); + } + } + } + } +} + + +void PropertyEditor::addItem(VProperty* vProp,QVBoxLayout *layout,QWidget *parent) +{ + if(vProp->name() == "line") + { + if (!currentGrid_) + { + currentGrid_=new QGridLayout(); + layout->addLayout(currentGrid_); + } + addLine(vProp,currentGrid_,parent); + } + + else if(vProp->name() == "group") + { + currentGrid_=0; + addGroup(vProp,layout,parent); + } + else if(vProp->name() == "grid") + { + currentGrid_=0; + addGrid(vProp,layout,parent); + } + else if(vProp->name() == "custom-notification") + { + currentGrid_=0; + addNotification(vProp,layout,parent); + } + else if(vProp->name() == "note") + { + if(currentGrid_) + { + addNote(vProp,currentGrid_,parent); + } + else + { + addNote(vProp,layout,parent); + } + } + else if(vProp->name() == "tabs") + { + currentGrid_=0; + addTabs(vProp,layout,parent); + } + +} + +PropertyLine* PropertyEditor::addLine(VProperty *vProp,QGridLayout *gridLayout,QWidget *parent) +{ + PropertyLine* item = PropertyLineFactory::create(vProp,true,parent); + + if(item) + { + item->init(); + //item->reset(vProp->link()->value()); + + int row=gridLayout->rowCount(); + + QLabel* lw=item->label(); + QLabel* slw=item->suffixLabel(); + + if(lw) + { + //If lineLabelLen_ is set we adjust the size of the + //line labels so that the editor widgets could be aligned + if(lineLabelLen_ > 0) + { + QFont f; + QFontMetrics fm(f); + QString s; + s=s.leftJustified(lineLabelLen_,'A'); + lw->setMinimumWidth(fm.width(s)); + } + + gridLayout->addWidget(lw,row,0,Qt::AlignLeft); + + if(slw) + { + QHBoxLayout* hb=new QHBoxLayout; + hb->addWidget(item->item()); + hb->addWidget(slw); + gridLayout->addLayout(hb,row,1,Qt::AlignLeft); + } + else + { + if(item->canExpand()) + { + QHBoxLayout* hb=new QHBoxLayout; + hb->addWidget(item->item()); + gridLayout->addLayout(hb,row,1,Qt::AlignLeft); + } + else + gridLayout->addWidget(item->item(),row,1,Qt::AlignLeft); + } + } + else + { + gridLayout->addWidget(item->item(),row,0,1,2,Qt::AlignLeft); + } + + QWidget *bw=item->button(); + if(bw) + gridLayout->addWidget(bw,row,2); + + + QToolButton* defTb=item->defaultTb(); + if(defTb) + { + gridLayout->addWidget(defTb,row,3); + } + + QToolButton* masterTb=item->masterTb(); + if(masterTb) + { + gridLayout->addWidget(masterTb,row,4); + } + + connect(item,SIGNAL(changed()), + this,SIGNAL(changed())); + + lineItems_ << item; + } + + return item; +} + +void PropertyEditor::addGroup(VProperty* vProp,QVBoxLayout * layout,QWidget *parent) +{ + if(vProp->name() != "group") + return; + + QGroupBox *groupBox = new QGroupBox(vProp->param("title"),parent); + groupBox->setObjectName("editorGroupBox"); + QGridLayout *grid=new QGridLayout(); + grid->setColumnStretch(1,1); + groupBox->setLayout(grid); + layout->addWidget(groupBox); + + currentGrid_=grid; + + //Loop over the children of the group + Q_FOREACH(VProperty* chProp,vProp->children()) + { + //Add each item to the the editor + addItem(chProp,layout,groupBox); + } + currentGrid_=0; +} + +void PropertyEditor::addGrid(VProperty* vProp,QVBoxLayout *layout,QWidget *parent) +{ + if(vProp->name() != "grid") + return; + + QGroupBox *groupBox = new QGroupBox(vProp->param("title"),parent); + groupBox->setObjectName("editorGroupBox"); + QGridLayout* grid=new QGridLayout(); + groupBox->setLayout(grid); + + layout->addWidget(groupBox); + + //Add header + for(int i=1; i < 10; i++) + { + QString h=vProp->param("h" + QString::number(i)); + + if(h.isEmpty()) + { + grid->setColumnStretch(i+1,1); + break; + } + + h+=" "; + QLabel* hLabel=new QLabel(h,groupBox); + grid->addWidget(hLabel,0,i,Qt::AlignHCenter); + } + + //Add rows + Q_FOREACH(VProperty* chProp,vProp->children()) + { + addGridRow(chProp,grid,groupBox); + } +} + + +void PropertyEditor::addGridRow(VProperty* vProp,QGridLayout *grid,QWidget *parent) +{ + if(vProp->name() != "row") + { + if(vProp->name() == "note") + { + QLabel *empty=new QLabel(" ",parent); + grid->addWidget(empty,grid->rowCount(),0,1,-1,Qt::AlignVCenter); + QLabel *label=new QLabel("   Note: " + vProp->value().toString(),parent); + grid->addWidget(label,grid->rowCount(),0,1,-1,Qt::AlignVCenter); + } + return; + } + + int row=grid->rowCount(); + QString labelText=vProp->param("label"); + QLabel* label=new QLabel(labelText,parent); + grid->addWidget(label,row,0); + + int col=1; + Q_FOREACH(VProperty* chProp,vProp->children()) + { + if(chProp->name() == "line") + { + PropertyLine* item = PropertyLineFactory::create(chProp,false,parent); + + if(item) + { + item->init(); + //item->reset(chProp->link()->value()); + + //QLabel* lw=item->label(); + //QLabel* slw=item->suffixLabel(); + + //gridLayout->addWidget(item->item(),row,col,Qt::AlignLeft); + + /*QWidget *bw=item->button(); + if(bw) + gridLayout->addWidget(bw,row,2);*/ + + + QToolButton* defTb=item->defaultTb(); + QToolButton* masterTb=item->masterTb(); + if(defTb || masterTb) + { + QHBoxLayout *hb=new QHBoxLayout(); + hb->addWidget(item->item()); + if(defTb) + hb->addWidget(defTb); + if(masterTb) + hb->addWidget(masterTb); + + hb->addSpacing(15); + hb->addStretch(1); + grid->addLayout(hb,row,col); + } + else + { + grid->addWidget(item->item(),row,col,Qt::AlignLeft); + } + + connect(item,SIGNAL(changed()), + this,SIGNAL(changed())); + lineItems_ << item; + col++; + } + } + } + + +} + +void PropertyEditor::addNotification(VProperty* vProp,QVBoxLayout* layout,QWidget *parent) +{ + if(vProp->name() != "custom-notification") + return; + + //ChangeNotifyEditor* ne=new ChangeNotifyEditor(parent); + + QTabWidget* tab=new QTabWidget(parent); + + bool useGroup=(vProp->param("group") == "true"); + + if(useGroup) + { + QString labelText=vProp->param("title"); + QGroupBox *groupBox = new QGroupBox(labelText,parent); + groupBox->setObjectName("editorGroupBox"); + QVBoxLayout* vb=new QVBoxLayout(); + groupBox->setLayout(vb); + vb->addWidget(tab); + layout->addWidget(groupBox); + + } + else + { + layout->addWidget(tab); + } + + //Add rows + Q_FOREACH(VProperty* chProp,vProp->children()) + { + if(chProp->name() == "row") + { + QString labelText=chProp->param("label"); + + QList lineLst; + + QWidget* w=new QWidget(parent); + QVBoxLayout* vb=new QVBoxLayout(w); + //vb->setContentsMargins(4,4,4,4); + + currentGrid_=0; + + if(VProperty *root=VConfig::instance()->find(chProp->param("root").toStdString())) + { + QLabel *labelDesc=new QLabel(tr("Description: ") + root->param("description") + "",w); + //labelDesc->setProperty("editorNotifyHeader","1"); + vb->addWidget(labelDesc); + vb->addSpacing(5); + } + + int lineLstPos=lineItems_.count(); + Q_FOREACH(VProperty* lineProp,chProp->children()) + { + addItem(lineProp,vb,w); + } + for(int i=lineLstPos; i < lineItems_.count(); i++) + lineLst << lineItems_[i]; + + tab->addTab(w,labelText); + + //Connect up different components + PropertyLine* enabledLine=0; + PropertyLine* popupLine=0; + PropertyLine* soundLine=0; + Q_FOREACH(PropertyLine* pl,lineLst) + { + if(pl->property()->name() == "enabled") + { + enabledLine=pl; + } + if(pl->property()->name() == "popup") + { + popupLine=pl; + } + if(pl->property()->name() == "sound") + { + soundLine=pl; + } + } + + if(enabledLine) + { + if(popupLine) + { + connect(enabledLine,SIGNAL(changed(QVariant)), + popupLine,SLOT(slotEnabled(QVariant))); + //init + popupLine->slotEnabled(enabledLine->property()->value()); + } + if(soundLine) + { + connect(enabledLine,SIGNAL(changed(QVariant)), + soundLine,SLOT(slotEnabled(QVariant))); + //init + soundLine->slotEnabled(enabledLine->property()->value()); + } + } + + //ne->addRow(labelText,lineLst,w); + } + } +} + +void PropertyEditor::addTabs(VProperty* vProp,QVBoxLayout *layout,QWidget* parent) +{ + if(vProp->name() != "tabs") + return; + + QTabWidget *t=new QTabWidget(parent); + t->setObjectName("tab"); + layout->addWidget(t); + + Q_FOREACH(VProperty* chProp,vProp->children()) + { + if(chProp->name() == "tab") + { + addTab(chProp,t); + } + } +} + +void PropertyEditor::addTab(VProperty* vProp,QTabWidget* tab) +{ + if(vProp->name() != "tab") + return; + + QWidget *w=new QWidget(tab); + QVBoxLayout* vb=new QVBoxLayout(); + w->setLayout(vb); + + tab->addTab(w,vProp->param("label")); + + + if(!vProp->param("adjustLineLabel").isEmpty()) + { + lineLabelLen_=vProp->param("adjustLineLabel").toInt(); + if(lineLabelLen_ <= 0 || lineLabelLen_ > 300) + lineLabelLen_=-1; + } + + Q_FOREACH(VProperty* chProp,vProp->children()) + { + addItem(chProp,vb,w); + } + + vb->addStretch(1); +} + +void PropertyEditor::addNote(VProperty* vProp,QVBoxLayout* layout,QWidget *parent) +{ + if(vProp->name() != "note") + return; + + QString txt=vProp->value().toString(); + txt.replace("%SERVER%",(serverName_.isEmpty())?"?":"" + serverName_ + ""); + + layout->addSpacing(5); + QLabel *label=new QLabel("Note: " + txt,parent); + layout->addWidget(label); +} + +void PropertyEditor::addNote(VProperty* vProp,QGridLayout* layout,QWidget *parent) +{ + if(vProp->name() != "note") + return; + + QString txt=vProp->value().toString(); + txt.replace("%SERVER%",(serverName_.isEmpty())?"?":"" + serverName_ + ""); + + //QLabel *empty=new QLabel(" ",parent); + //layout->addWidget(empty,layout->rowCount(),0,1,-1,Qt::AlignVCenter); + //QLabel *label=new QLabel("   Note: " + txt,parent); + + //QFrame* fr=new QFrame(parent); + //fr->setFrameShape(QFrame::HLine); + //layout->addWidget(fr,layout->rowCount(),0,1,-1,Qt::AlignVCenter); + + QLabel *label=new QLabel("
       Note: " + txt + "
    ",parent); + label->setWordWrap(true); + layout->addWidget(label,layout->rowCount(),0,1,-1,Qt::AlignVCenter); +} + + +bool PropertyEditor::applyChange() +{ + bool changed=false; + //Loop over the top level properties (groups) in the browser + Q_FOREACH(PropertyLine* item, lineItems_) + { + //Sync the changes to VConfig + if(item->applyChange()) + { + changed=true; + } + } + + return changed; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,67 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef PROPERTYEDITOR_INC_ +#define PROPERTYEDITOR_INC_ + +#include + +#include "ui_PropertyEditor.h" + +class QGridLayout; +class QTabWidget; + +class PropertyLine; +class VProperty; + +class PropertyEditor : public QWidget, protected Ui::PropertyEditor +{ +Q_OBJECT + +public: + explicit PropertyEditor(QWidget *parent=0); + ~PropertyEditor(); + + void edit(VProperty*,QPixmap pixmap); + void edit(VProperty*,QString label); + bool applyChange(); + VProperty* property() const {return group_;} + +Q_SIGNALS: + void changed(); + +private: + void clear(); + void build(); + void addRules(); + void addHelpers(); + + void addItem(VProperty*,QVBoxLayout*,QWidget*); + PropertyLine* addLine(VProperty* vProp,QGridLayout* grid,QWidget*); + void addGroup(VProperty*,QVBoxLayout*,QWidget*); + void addGrid(VProperty*,QVBoxLayout*,QWidget*); + void addGridRow(VProperty* prop,QGridLayout *grid,QWidget*); + void addNotification(VProperty* prop,QVBoxLayout*,QWidget*); + void addTabs(VProperty*,QVBoxLayout*,QWidget*); + void addTab(VProperty*,QTabWidget*); + void addNote(VProperty* vProp,QVBoxLayout*,QWidget*); + void addNote(VProperty* vProp,QGridLayout* layout,QWidget*); + + VProperty* group_; + QGridLayout* currentGrid_; + QList lineItems_; + QString serverName_; + QWidget* holder_; + int lineLabelLen_; +}; + + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.ui ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyEditor.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyEditor.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,132 @@ + + + PropertyEditor + + + + 0 + 0 + 502 + 506 + + + + Form + + + + 2 + + + 0 + + + + + + 1 + + + + + false + + + QFrame::StyledPanel + + + TextLabel + + + 4 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + TextLabel + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 5 + 20 + + + + + + + + + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + 0 + 0 + 498 + 472 + + + + true + + + + QLayout::SetMinAndMaxSize + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1011 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "PropertyLine.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ComboMulti.hpp" +#include "Sound.hpp" +#include "UiLog.hpp" + +#include + +static std::map* makers = 0; + +FontSizeSpin::FontSizeSpin(QWidget *parent) : QSpinBox(parent) +{ +} + +void FontSizeSpin::setFamily(QString family) +{ + QFontDatabase db; + vals_=db.pointSizes(family); + setRange(0,vals_.count()-1); +} + +QString FontSizeSpin::textFromValue(int value) const +{ + if(value >=0 && value < vals_.count()) + return QString::number(vals_.at(value)); + + return QString(); +} + + + +//========================================================================= +// +// PropertyLineFactory +// +//========================================================================= + +PropertyLineFactory::PropertyLineFactory(VProperty::GuiType type) +{ + if(makers == 0) + makers = new std::map; + + (*makers)[type] = this; +} + +PropertyLineFactory::~PropertyLineFactory() +{ + // Not called +} + +PropertyLine* PropertyLineFactory::create(VProperty* p,bool addLabel,QWidget* parent) +{ + if(!p || !p->link()) + return 0; + + VProperty::GuiType t=p->link()->guiType(); + std::map::iterator j = makers->find(t); + if(j != makers->end()) + return (*j).second->make(p,addLabel,parent); + + return 0; +} + +//========================================================================= +// +// PropertyLine +// +//========================================================================= + +PropertyLine::PropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : + QObject(parent), + prop_(NULL), + guiProp_(guiProp), + label_(0), + suffixLabel_(0), + defaultTb_(0), + masterTb_(0), + enabled_(true), + doNotEmitChange_(false), + ruleLine_(0) +{ + prop_=guiProp_->link(); + assert(prop_); + + setObjectName(guiProp->name()); + + oriVal_=prop_->value(); + + if(addLabel) + { + label_=new QLabel(prop_->param("label"),parent); + label_->setToolTip(prop_->param("tooltip")); + } + + QString suffixText=prop_->param("suffix"); + if(!suffixText.isEmpty()) + { + suffixLabel_=new QLabel(suffixText,parent); + } + + defaultTb_= new QToolButton(parent); + defaultTb_->setObjectName("default_" + prop_->name()); + defaultTb_->setToolTip(tr("Reset to default value")); + defaultTb_->setIcon(QPixmap(":/viewer/reset_to_default.svg")); + defaultTb_->setAutoRaise(true); + + connect(defaultTb_,SIGNAL(clicked(bool)), + this,SLOT(slotResetToDefault(bool))); + + if(prop_->master()) + { + masterTb_=new QToolButton(parent); + masterTb_->setObjectName("master_" + prop_->name()); + masterTb_->setCheckable(true); + masterTb_->setText("Use global"); + masterTb_->setToolTip(tr("Use global server settings")); + masterTb_->setIcon(QPixmap(":/viewer/chain.svg")); + masterTb_->setAutoRaise(true); + masterTb_->setChecked(prop_->useMaster()); + + connect(masterTb_,SIGNAL(toggled(bool)), + this,SLOT(slotMaster(bool))); + } +} + +PropertyLine::~PropertyLine() +{ +} + +void PropertyLine::init() +{ + doNotEmitChange_=true; + if(prop_->master()) + { + if(masterTb_->isChecked() != prop_->useMaster()) + masterTb_->setChecked(prop_->useMaster()); + else + slotMaster(prop_->useMaster()); + } + else + { + slotReset(prop_->value()); + } + doNotEmitChange_=false; + + if(item()) + item()->setToolTip(prop_->param("tooltip")); +} + +void PropertyLine::slotResetToDefault(bool) +{ + slotReset(prop_->defaultValue()); + checkState(); +} + +void PropertyLine::slotEnabled(QVariant v) +{ + if(enabled_ != v.toBool()) + { + if(!masterTb_ || !masterTb_->isChecked()) + { + enabled_=v.toBool(); + checkState(); + } + } +} + +void PropertyLine::checkState() +{ + if(label_) + { + label_->setEnabled(enabled_); + } + if(masterTb_) + { + masterTb_->setEnabled(enabled_); + } + if(suffixLabel_) + { + suffixLabel_->setEnabled(enabled_); + } + + defaultTb_->setEnabled(enabled_); + + setEnabledEditable(enabled_); + + if(masterTb_ && masterTb_->isChecked()) + return; + + if(enabled_) + { + if(prop_->defaultValue() != currentValue()) + defaultTb_->setEnabled(true); + else + defaultTb_->setEnabled(false); + } +} + +bool PropertyLine::applyMaster() +{ + if(masterTb_ && prop_->useMaster() != masterTb_->isChecked()) + { + prop_->setUseMaster(masterTb_->isChecked()); + return true; + } + return false; +} + + +void PropertyLine::slotMaster(bool b) +{ + if(b) + { + slotReset(prop_->master()->value()); + defaultTb_->setEnabled(false); + setEnabledEditable(false); + } + else + { + slotReset(prop_->value()); + defaultTb_->setEnabled(true); + checkState(); + setEnabledEditable(true); + } + + Q_EMIT masterChanged(b); + + valueChanged(); +} + +void PropertyLine::slotReset(VProperty* prop,QVariant v) +{ + if(prop == prop_) + slotReset(v); +} + +void PropertyLine::valueChanged() +{ + if(!doNotEmitChange_) + Q_EMIT changed(); +} + +void PropertyLine::addHelper(PropertyLine* line) +{ + if(line) + helpers_[line->property()->name()]=line; +} + +//A simple dependency on other properties' values + +VProperty* PropertyLine::ruleProperty() +{ + if(!prop_->master()) + { + QStringList disabledFor=prop_->param("disabledRule").split("="); + if(disabledFor.count() == 2) + { + QString key=disabledFor[0].simplified(); + QString val=disabledFor[1].simplified(); + if(key.isEmpty() == false && prop_->parent()) + { + if(VProperty* rp=prop_->parent()->findChild(key)) + { + ruleValue_=val; + return rp; + } + } + } + } + return 0; +} + +void PropertyLine::addRuleLine(PropertyLine *r) +{ + ruleLine_=r; + Q_ASSERT(ruleLine_); + connect(ruleLine_,SIGNAL(changed()), + this,SLOT(slotRule())); + + //init + slotRule(); +} + +void PropertyLine::slotRule() +{ + Q_ASSERT(ruleLine_); + slotEnabled(ruleLine_->currentValue().toString() != ruleValue_); +} + + +//========================================================================= +// +// StringPropertyLine +// +//========================================================================= + +StringPropertyLine::StringPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + le_=new QLineEdit(parent); + le_->setObjectName(prop_->name()); + + connect(le_,SIGNAL(textEdited(QString)), + this,SLOT(slotEdited(QString))); +} + +QWidget* StringPropertyLine::item() +{ + return le_; +} + +QWidget* StringPropertyLine::button() +{ + return NULL; +} + +void StringPropertyLine::slotReset(QVariant v) +{ + le_->setText(v.toString()); + PropertyLine::checkState(); + valueChanged(); +} + +bool StringPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + QString v=oriVal_.toString(); + if(v != le_->text()) + { + prop_->setValue(le_->text()); + oriVal_=prop_->value(); + return true; + } + return false; +} + +QVariant StringPropertyLine::currentValue() +{ + return le_->text(); +} + +void StringPropertyLine::slotEdited(QString) +{ + PropertyLine::checkState(); + valueChanged(); +} + +void StringPropertyLine::setEnabledEditable(bool b) +{ + le_->setEnabled(b); +} + +//========================================================================= +// +// ColourPropertyLine +// +//========================================================================= + +ColourPropertyLine::ColourPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + QFont f; + QFontMetrics fm(f); + int height=fm.height(); + int width=fm.width("AAAAAAA"); + + cb_=new QToolButton(parent); + cb_->setObjectName(prop_->name()); + cb_->setFixedWidth(width); + cb_->setFixedHeight(height+2); + cb_->setToolTip(tr("Click to select a colour")); + + styleSheet_="QToolButton { background: BG; border: 1px solid rgb(120,120,120); border-radius: 2px;}"; + + connect(cb_,SIGNAL(clicked(bool)), + this,SLOT(slotEdit(bool))); +} + +QWidget* ColourPropertyLine::item() +{ + return cb_; +} + +QWidget* ColourPropertyLine::button() +{ + return NULL; +} + +void ColourPropertyLine::slotReset(QVariant v) +{ + QColor c=v.value(); + + QString st=styleSheet_; + st.replace("BG","rgb(" + QString::number(c.red()) + "," + + QString::number(c.green()) + "," + QString::number(c.blue()) + ")"); + + cb_->setStyleSheet(st); + + currentCol_=c; + + PropertyLine::checkState(); + valueChanged(); +} + +void ColourPropertyLine::slotEdit(bool) +{ + QColor currentCol=currentValue().value(); + QColor col=QColorDialog::getColor(currentCol,cb_->parentWidget()); + + if(col.isValid()) + { + slotReset(col); + } +} + +bool ColourPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + QColor v=oriVal_.value(); + QColor c=currentValue().value(); + + if(v != c) + { + prop_->setValue(c); + oriVal_=prop_->value(); + return true; + } + + return false; +} + +QVariant ColourPropertyLine::currentValue() +{ + return currentCol_; +} + +void ColourPropertyLine::setEnabledEditable(bool b) +{ + cb_->setEnabled(b); + + QColor col; + if(b) + { + col=currentCol_; + } + else + { + if(label_) + { + QPalette pal=label_->palette(); + col=pal.color(QPalette::Disabled,QPalette::Window); + } + else + { + col=QColor(200,200,200); + } + } + + QString st=styleSheet_; + st.replace("BG","rgb(" + QString::number(col.red()) + "," + + QString::number(col.green()) + "," + QString::number(col.blue()) + ")"); + + cb_->setStyleSheet(st); +} + +//========================================================================= +// +// FontPropertyLine +// +//========================================================================= + +FontPropertyLine::FontPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + holderW_=new QWidget(parent); + + QHBoxLayout* hb=new QHBoxLayout(holderW_); + hb->setContentsMargins(0,0,0,0); + + QFontDatabase db; + + familyCb_=new QComboBox(parent); + familyCb_->setObjectName(prop_->name()); + + hb->addWidget(familyCb_); + Q_FOREACH(QString s,db.families(QFontDatabase::Latin)) + familyCb_->addItem(s); + + sizeSpin_=new QSpinBox(parent); + sizeSpin_->setRange(1,200); + hb->addWidget(sizeSpin_); + + QLabel *sizeLabel=new QLabel("pt",parent); + hb->addWidget(sizeLabel); + + lName_=new QLabel(parent); + + connect(familyCb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotFamilyChanged(int))); + + connect(sizeSpin_,SIGNAL(valueChanged(int)), + this,SLOT(slotSizeChanged(int))); + + /*tbEdit_=new QToolButton(parent); + tbEdit_->setToolTip(tr("Edit")); + + connect(tbEdit_,SIGNAL(clicked(bool)), + this,SLOT(slotEdit(bool)));*/ +} + +QWidget* FontPropertyLine::item() +{ + return holderW_; +} + +QWidget* FontPropertyLine::button() +{ + return NULL; //tbEdit_; +} + +void FontPropertyLine::slotReset(QVariant v) +{ + font_=v.value(); + + for(int i=0; i < familyCb_->count(); i++) + if(familyCb_->itemText(i) == font_.family()) + familyCb_->setCurrentIndex(i); + + sizeSpin_->setValue(font_.pointSize()); + + PropertyLine::checkState(); + valueChanged(); +} + +void FontPropertyLine::slotEdit(bool) +{ + QFont c; + + bool ok; + QFont f = QFontDialog::getFont(&ok,c,lName_->parentWidget()); + + if(ok) + { + lName_->setText(f.toString()); + font_=f; + } + valueChanged(); +} + +void FontPropertyLine::slotFamilyChanged(int idx) +{ + if(idx != -1) + { + QString family=familyCb_->itemText(idx); + if(font_.family() != family) + { + font_.setFamily(family); + PropertyLine::checkState(); + valueChanged(); + } + } +} + +void FontPropertyLine::slotSizeChanged(int val) +{ + if(val != font_.pointSize()) + { + font_.setPointSize(val); + PropertyLine::checkState(); + valueChanged(); + } +} + + +bool FontPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + if(oriVal_.value() != font_) + { + prop_->setValue(font_); + oriVal_=prop_->value(); + return true; + } + return false; +} + +QVariant FontPropertyLine::currentValue() +{ + return font_; +} + +void FontPropertyLine::setEnabledEditable(bool b) +{ + //tbEdit_->setEnabled(b); +} + +//========================================================================= +// +// IntPropertyLine +// +//========================================================================= + +IntPropertyLine::IntPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + le_=new QLineEdit(parent); + le_->setObjectName(prop_->name()); + + QIntValidator* validator=new QIntValidator(le_); + + QString s=guiProp->param("max"); + if(!s.isEmpty()) + { + validator->setTop(s.toInt()); + } + + s=guiProp->param("min"); + if(!s.isEmpty()) + { + validator->setBottom(s.toInt()); + } + + le_->setValidator(validator); + + connect(le_,SIGNAL(textEdited(QString)), + this,SLOT(slotEdited(QString))); +} + +QWidget* IntPropertyLine::item() +{ + return le_; +} + +QWidget* IntPropertyLine::button() +{ + return NULL; +} + +void IntPropertyLine::slotReset(QVariant v) +{ + le_->setText(QString::number(v.toInt())); + PropertyLine::checkState(); + valueChanged(); +} + +bool IntPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + int cv=le_->text().toInt(); + if(oriVal_.toInt() != cv) + { + prop_->setValue(cv); + oriVal_=prop_->value(); + return true; + } + return false; +} + +QVariant IntPropertyLine::currentValue() +{ + return le_->text().toInt(); +} + +void IntPropertyLine::slotEdited(QString) +{ + PropertyLine::checkState(); + valueChanged(); +} + +void IntPropertyLine::setEnabledEditable(bool b) +{ + le_->setEnabled(b); +} + +//========================================================================= +// +// BoolPropertyLine +// +//========================================================================= + +BoolPropertyLine::BoolPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,false,parent) +{ + cb_=new QCheckBox(prop_->param("label"),parent); + cb_->setObjectName(prop_->name()); + + connect(cb_,SIGNAL(stateChanged(int)), + this,SLOT(slotStateChanged(int))); +} + +QWidget* BoolPropertyLine::item() +{ + return cb_; +} + +QWidget* BoolPropertyLine::button() +{ + return NULL; +} + +void BoolPropertyLine::slotReset(QVariant v) +{ + cb_->setChecked(v.toBool()); + PropertyLine::checkState(); + valueChanged(); + + //BoolLines emit this signal because they might control + //other lines' status + Q_EMIT changed(currentValue()); +} + +bool BoolPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + if(oriVal_.toBool() != cb_->isChecked()) + { + prop_->setValue(cb_->isChecked()); + oriVal_=prop_->value(); + return true; + } + return false; +} + +QVariant BoolPropertyLine::currentValue() +{ + return cb_->isChecked(); +} + +void BoolPropertyLine::slotStateChanged(int) +{ + PropertyLine::checkState(); + valueChanged(); + + //BoolLines emit this signal because they might control + //other lines' status + Q_EMIT changed(currentValue()); +} + +void BoolPropertyLine::setEnabledEditable(bool b) +{ + cb_->setEnabled(b); +} + +//========================================================================= +// +// ComboPropertyLine +// +//========================================================================= + +ComboPropertyLine::ComboPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + cb_=new QComboBox(parent);//(vProp->param("label")); + cb_->setObjectName(prop_->name()); + + connect(cb_,SIGNAL(currentIndexChanged(int)), + this,SLOT(slotCurrentChanged(int))); + + QStringList lst=prop_->param("values_label").split("/"); + QStringList lstData=prop_->param("values").split("/"); + if(prop_->param("values_label").simplified().isEmpty()) + lst=lstData; + + assert(lst.count() == lstData.count()); + for(int i=0; i < lst.count(); i++) + cb_->addItem(lst[i],lstData[i]); +} + +QWidget* ComboPropertyLine::item() +{ + return cb_; +} + +QWidget* ComboPropertyLine::button() +{ + return NULL; +} + +void ComboPropertyLine::slotReset(QVariant v) +{ + QStringList lst=prop_->param("values").split("/"); + int idx=lst.indexOf(v.toString()); + if(idx != -1) + cb_->setCurrentIndex(idx); + + PropertyLine::checkState(); + valueChanged(); +} + +bool ComboPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + int idx=cb_->currentIndex(); + + if(idx != -1) + { + QString currentDataVal=cb_->itemData(idx).toString(); + if(oriVal_.toString() != currentDataVal) + { + prop_->setValue(currentDataVal); + oriVal_=prop_->value(); + return true; + } + } + + return false; +} + +QVariant ComboPropertyLine::currentValue() +{ + int idx=cb_->currentIndex(); + + if(idx != -1) + { + return cb_->itemData(idx).toString(); + } + + return QString(); +} + +void ComboPropertyLine::slotCurrentChanged(int) +{ + PropertyLine::checkState(); + valueChanged(); +} + +void ComboPropertyLine::setEnabledEditable(bool b) +{ + cb_->setEnabled(b); +} + +//========================================================================= +// +// ComboMultiPropertyLine +// +//========================================================================= + +ComboMultiPropertyLine::ComboMultiPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) +{ + if(label_) + label_->setText(label_->text() + ":"); + + cb_=new ComboMulti(parent);//(vProp->param("label")); + cb_->setObjectName(prop_->name()); + + cb_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); + + connect(cb_,SIGNAL(selectionChanged()), + this,SLOT(slotSelectionChanged())); + + QStringList lst=prop_->param("values_label").split("/"); + QStringList lstData=prop_->param("values").split("/"); + if(prop_->param("values_label").simplified().isEmpty()) + lst=lstData; + + assert(lst.count() == lstData.count()); + for(int i=0; i < lst.count(); i++) + { + cb_->addItem(lst[i],lstData[i]); + } +} + +QWidget* ComboMultiPropertyLine::item() +{ + return cb_; +} + +QWidget* ComboMultiPropertyLine::button() +{ + return NULL; +} + +void ComboMultiPropertyLine::slotReset(QVariant v) +{ + QStringList vals=v.toString().split("/"); + + cb_->setSelectionByData(vals); + + PropertyLine::checkState(); + valueChanged(); +} + +bool ComboMultiPropertyLine::applyChange() +{ + PropertyLine::applyMaster(); + + QString currentVal=cb_->selectionData().join("/"); + + if(oriVal_.toString() != currentVal) + { + prop_->setValue(currentVal); + oriVal_=prop_->value(); + return true; + } + + return false; +} + +QVariant ComboMultiPropertyLine::currentValue() +{ + QStringList lst=cb_->selectionData(); + + return lst.join("/"); +} + +void ComboMultiPropertyLine::slotSelectionChanged() +{ + PropertyLine::checkState(); + valueChanged(); +} + +void ComboMultiPropertyLine::setEnabledEditable(bool b) +{ + cb_->setEnabled(b); +} + + +//========================================================================= +// +// SoundComboPropertyLine +// +//========================================================================= + +SoundComboPropertyLine::SoundComboPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : + ComboPropertyLine(guiProp,addLabel,parent), + playTb_(NULL) +{ + playTb_=new QToolButton(parent); + playTb_->setObjectName(prop_->name()); + + playTb_->setText("play"); + playTb_->setToolTip(tr("Play sound")); + + connect(playTb_,SIGNAL(clicked(bool)), + this,SLOT(slotPlay(bool))); +} + +QWidget* SoundComboPropertyLine::item() +{ + return cb_; +} + +QWidget* SoundComboPropertyLine::button() +{ + return playTb_; +} + +void SoundComboPropertyLine::setEnabledEditable(bool b) +{ + cb_->setEnabled(b); + playTb_->setEnabled(b); +} + +void SoundComboPropertyLine::slotPlay(bool) +{ + int loopCount=1; + if(PropertyLine* line=helpers_.value("sound_loop",NULL)) + loopCount=line->currentValue().toInt(); + + Sound::instance()->playSystem(currentValue().toString().toStdString(),loopCount); +} + + +static PropertyLineMaker makerStr(VProperty::StringGui); +static PropertyLineMaker makerCol(VProperty::ColourGui); +static PropertyLineMaker makerFont(VProperty::FontGui); +static PropertyLineMaker makerInt(VProperty::IntGui); +static PropertyLineMaker makerBool(VProperty::BoolGui); +static PropertyLineMaker makerCombo(VProperty::StringComboGui); +static PropertyLineMaker makerComboMulti(VProperty::MultiStringComboGui); +static PropertyLineMaker makerSoundCombo(VProperty::SoundComboGui); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,363 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef PROPERTYLINE_INC_ +#define PROPERTYLINE_INC_ + +#include + +#include +#include +#include +#include + +class QComboBox; +class QCheckBox; +class QLabel; +class QLineEdit; +class QSpinBox; +class QToolButton; +class QWidget; +class ComboMulti; + +#include "VProperty.hpp" + +class PropertyLine; + +class FontSizeSpin : public QSpinBox +{ +public: + FontSizeSpin(QWidget* parent=0); + void setFamily(QString); + +protected: + QString textFromValue(int value) const; + + QList vals_; + +}; + + + +//------------------------------------- +// Factory +//------------------------------------ + +class PropertyLineFactory +{ +public: + explicit PropertyLineFactory(VProperty::GuiType); + virtual ~PropertyLineFactory(); + + virtual PropertyLine* make(VProperty* p,bool,QWidget* w) = 0; + static PropertyLine* create(VProperty* p,bool,QWidget* w); + +private: + explicit PropertyLineFactory(const PropertyLineFactory&); + PropertyLineFactory& operator=(const PropertyLineFactory&); + +}; + +template +class PropertyLineMaker : public PropertyLineFactory +{ + PropertyLine* make(VProperty* p,bool addLabel,QWidget* w) { return new T(p,addLabel,w); } +public: + explicit PropertyLineMaker(VProperty::GuiType t) : PropertyLineFactory(t) {} +}; + + +//------------------------------------- +// Abstract property line editor +//------------------------------------ + +class PropertyLine: public QObject +{ + Q_OBJECT + +public: + PropertyLine(VProperty*,bool addLabel,QWidget* parent=0); + virtual ~PropertyLine(); + + QLabel* label() {return label_;} + QLabel* suffixLabel() {return suffixLabel_;} + virtual QWidget* item()=0; + virtual QWidget* button()=0; + QToolButton* defaultTb() {return defaultTb_;} + QToolButton* masterTb() {return masterTb_;} + VProperty* property() const {return prop_;} + VProperty* guiProperty() const {return guiProp_;} + VProperty* ruleProperty(); + void addRuleLine(PropertyLine*); + virtual bool canExpand() const {return false;} + + void addHelper(PropertyLine*); + + void init(); + virtual bool applyChange()=0; + virtual QVariant currentValue()=0; + +public Q_SLOTS: + virtual void slotReset(QVariant)=0; + virtual void slotReset(VProperty*,QVariant); + virtual void slotEnabled(QVariant); + +protected Q_SLOTS: + void slotResetToDefault(bool); + void slotMaster(bool b); + void checkState(); + void slotRule(); + +Q_SIGNALS: + void changed(QVariant); + void masterChanged(bool); + void changed(); + +protected: + virtual void setEnabledEditable(bool)=0; + bool applyMaster(); + void valueChanged(); + + VProperty* prop_; + VProperty* guiProp_; + QLabel* label_; + QLabel* suffixLabel_; + QToolButton* defaultTb_; + QToolButton* masterTb_; + bool enabled_; + QVariant oriVal_; + bool doNotEmitChange_; + QMap helpers_; + PropertyLine* ruleLine_; + QString ruleValue_; +}; + +//------------------------------------- +// String editor +//------------------------------------ + +class StringPropertyLine : public PropertyLine +{ + Q_OBJECT + +public: + StringPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + bool canExpand() const {return true;} + +public Q_SLOTS: + void slotEdited(QString); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +private: + QLineEdit* le_; +}; + +//------------------------------------- +// Colour editor +//------------------------------------ + +class ColourPropertyLine : public PropertyLine +{ +Q_OBJECT + +public: + ColourPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + +private Q_SLOTS: + void slotEdit(bool); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +private: + QToolButton* cb_; + QColor currentCol_; + QString styleSheet_; +}; + +//------------------------------------- +// Font editor +//------------------------------------ + +class FontPropertyLine : public PropertyLine +{ +Q_OBJECT + +public: + FontPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + +private Q_SLOTS: + void slotEdit(bool); + void slotReset(QVariant); + void slotFamilyChanged(int); + void slotSizeChanged(int); + +protected: + void setEnabledEditable(bool); + +private: + QWidget* holderW_; + QComboBox* familyCb_; + QSpinBox* sizeSpin_; + QLabel* lName_; + QToolButton *tbEdit_; + QFont font_; +}; + +//------------------------------------- +// Int editor +//------------------------------------ + +class IntPropertyLine : public PropertyLine +{ + Q_OBJECT + +public: + IntPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + +public Q_SLOTS: + void slotEdited(QString); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +private: + QLineEdit* le_; +}; + +//------------------------------------- +// Boolean editor +//------------------------------------ + +class BoolPropertyLine : public PropertyLine +{ + Q_OBJECT + +public: + BoolPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + +public Q_SLOTS: + void slotStateChanged(int); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +private: + QCheckBox* cb_; +}; + +//------------------------------------- +// Combo box editor +//------------------------------------ + +class ComboPropertyLine : public PropertyLine +{ + Q_OBJECT + +public: + ComboPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + +public Q_SLOTS: + void slotCurrentChanged(int); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +protected: + QComboBox* cb_; +}; + + +//------------------------------------- +// Combo box editor +//------------------------------------ + +class ComboMultiPropertyLine : public PropertyLine +{ + Q_OBJECT + +public: + ComboMultiPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + bool applyChange(); + QVariant currentValue(); + bool canExpand() const {return true;} + +public Q_SLOTS: + void slotSelectionChanged(); + void slotReset(QVariant); + +protected: + void setEnabledEditable(bool); + +protected: + ComboMulti* cb_; +}; + +//------------------------------------- +// Combo box editor +//------------------------------------ + +class SoundComboPropertyLine : public ComboPropertyLine +{ + Q_OBJECT + +public: + SoundComboPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); + QWidget* item(); + QWidget* button(); + +public Q_SLOTS: + void slotPlay(bool); + +protected: + void setEnabledEditable(bool); + +private: + QToolButton* playTb_; + +}; + + + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyMapper.cpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyMapper.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyMapper.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyMapper.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,57 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "PropertyMapper.hpp" + +#include "UIDebug.hpp" +#include "VConfig.hpp" + +PropertyMapper::PropertyMapper(const std::vector& names,VPropertyObserver* obs) : obs_(obs) +{ + for(std::vector::const_iterator it=names.begin(); it != names.end(); ++it) + { + if(VProperty* p=VConfig::instance()->find(*it)) + { + p->addObserver(obs); + props_.push_back(p); + } + } +} + +PropertyMapper::~PropertyMapper() +{ + for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) + { + (*it)->removeObserver(obs_); + } +} + +VProperty* PropertyMapper::find(const std::string& path,bool failOnError) const +{ + for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) + { + if((*it)->path() == path) + return *it; + } + + if(failOnError) + UI_ASSERT(0,"Could not find property=" + path); + + return 0; +} + +void PropertyMapper::initObserver(VPropertyObserver *obs) const +{ + for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) + { + obs->notifyChange(*it); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/PropertyMapper.hpp ecflow-4.11.1/Viewer/ecflowUI/src/PropertyMapper.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/PropertyMapper.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/PropertyMapper.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,30 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef PROPERTYMAPPER_INC_ +#define PROPERTYMAPPER_INC_ + +#include "VProperty.hpp" + +class PropertyMapper +{ +public: + PropertyMapper(const std::vector&,VPropertyObserver* obs); + ~PropertyMapper(); + VProperty* find(const std::string& path,bool failOnError=false) const; + void initObserver(VPropertyObserver *obs) const; + +private: + VPropertyObserver* obs_; + std::vector props_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RectMetrics.cpp ecflow-4.11.1/Viewer/ecflowUI/src/RectMetrics.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RectMetrics.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RectMetrics.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,60 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "RectMetrics.hpp" + +#include +#include + +RectMetrics::RectMetrics(int penWidth) : + topOffset_(0), + bottomOffset_(0) +{ + if(penWidth >=0 && penWidth <=4) + compute(penWidth); +} + +void RectMetrics::compute(int penWidth) +{ + QImage img(24,24,QImage::Format_ARGB32_Premultiplied); + img.fill(Qt::white); + QPainter p(&img); + p.setPen(QPen(Qt::black,penWidth)); + + int top=6; + int bottom=img.height()-top; + int left=6; + int right=img.width()-left; + p.drawRect(QRect(left,top,right-left,bottom-top)); + + int j=12; + + //top + for(int i=0; i < img.height(); i++) + { + QRgb c=img.pixel(j,i); + if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) + { + topOffset_=i-top; + break; + } + } + + //bottom + for(int i=img.height()-1; i >=0; i--) + { + QRgb c=img.pixel(j,i); + if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) + { + bottomOffset_=i-bottom; + break; + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RectMetrics.hpp ecflow-4.11.1/Viewer/ecflowUI/src/RectMetrics.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RectMetrics.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RectMetrics.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef RECTMETRICS_HPP +#define RECTMETRICS_HPP + +class RectMetrics +{ +public: + RectMetrics(int penWidth); + int topOffset() const {return topOffset_;} + int bottomOffset() const {return bottomOffset_;} + +protected: + int topOffset_; + int bottomOffset_; + +private: + void compute(int); +}; + +#endif // RECTMETRICS_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,429 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "RepeatEditor.hpp" + +#include +#include +#include +#include +#include + +#include "Node.hpp" +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "SessionHandler.hpp" +#include "UiLog.hpp" +#include "VInfo.hpp" +#include "VNode.hpp" +#include "VRepeatAttr.hpp" + +RepeatEditorWidget::RepeatEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); +} + +void RepeatEditorWidget::hideRow(QWidget* w) +{ + w->hide(); + QWidget* item=formLayout_->labelForField(w); + Q_ASSERT(item); + item->hide(); +} + +//================================================================ +// +// RepeatEditor +// +//================================================================ + +RepeatEditor::RepeatEditor(VInfo_ptr info,QWidget* parent) : + AttributeEditor(info,"repeat",parent), + model_(0) +{ + w_=new RepeatEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "repeat"); + + VRepeatAttr *rep=static_cast(a); + +#if 0 + if(a->data().count() < 7) + return; + + VNode *vnode=info_->node(); + Q_ASSERT(vnode); + node_ptr node=vnode->node(); + Q_ASSERT(node); + const Repeat& r=node->repeat(); + repeat_=VRepeat::make(r); +#endif + + + oriVal_=a->data().at(3); + + w_->nameLabel_->setText(a->data().at(2)); + //w_->valueLe_->setText(a->data().at(3)); + w_->startLabel_->setText(a->data().at(4)); + w_->endLabel_->setText(a->data().at(5)); + w_->stepLabel_->setText(a->data().at(6)); + + //Value will be initailised in the subclasses + oriVal_=a->data().at(3); + + buildList(rep); + +#if 0 + QIntValidator *validator=new QIntValidator(this); + if(!a->data().at(3).isEmpty() && !a->data().at(4).isEmpty()) + { + validator->setRange(a->data().at(3).toInt(), + a->data().at(4).toInt()); + } + valueLe_->setValidator(validator); +#endif + + header_->setInfo(QString::fromStdString(info_->nodePath()),"Repeat " + QString::fromStdString(rep->subType())); + + readSettings(); +} + +RepeatEditor::~RepeatEditor() +{ + writeSettings(); +} + +void RepeatEditor::buildList(VRepeatAttr *rep) +{ + int start=rep->startIndex(); + int end=rep->endIndex(); + int step=rep->step(); + int current=rep->currentIndex(); + + if(step<=0 || end <= start) + { + return; + } + + modelData_.clear(); + int cnt=end-start; + if(cnt >1) + { + for(int i=start; i <= end; i++) + modelData_ << QString::fromStdString(rep->value(i)); + + model_=new QStringListModel(this); + model_->setStringList(modelData_); + w_->valueView_->setModel(model_); + w_->valueView_->setCurrentIndex(model_->index(current,0)); + + connect(w_->valueView_->selectionModel(), + SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this, SLOT(slotSelectedInView(QModelIndex,QModelIndex))); + + w_->valueView_->setFocus(Qt::MouseFocusReason); + } + else + { + w_->valueView_->hide(); + } +} + +void RepeatEditor::slotSelectedInView(const QModelIndex ¤t, const QModelIndex &previous) +{ + setValue(current.data().toString()); + checkButtonStatus(); +} + +bool RepeatEditor::isListMode() const +{ + return (model_)?true:false; +} + +void RepeatEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("RepeatEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void RepeatEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("RepeatEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(310,340)); + } + + settings.endGroup(); +} + +//================================================================ +// +// RepeatIntEditor +// +//================================================================ + +RepeatIntEditor::RepeatIntEditor(VInfo_ptr info,QWidget* parent) : + RepeatEditor(info,parent) +{ + //if(!repeat_) + // return; + + w_->hideRow(w_->valueLe_); + + initSpinner(); + + connect(w_->valueSpin_,SIGNAL(valueChanged(int)), + this,SLOT(slotValueChanged(int))); + + checkButtonStatus(); +} + +void RepeatIntEditor::initSpinner() +{ + w_->valueSpin_->setValue(oriVal_.toInt()); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "repeat"); + + VRepeatAttr *rep=static_cast(a); + + int startIndex=rep->startIndex(); + int endIndex=rep->endIndex(); + int step=rep->step(); + + if(step<=0 || endIndex <= startIndex) + { + return; + } + + int minVal=QString::fromStdString(rep->value(startIndex)).toInt(); + int maxVal=QString::fromStdString(rep->value(endIndex)).toInt(); + +#if 0 + UiLog().dbg() << "min=" << minVal << " max=" << maxVal << " step=" << step; +#endif + w_->valueSpin_->setMinimum(minVal); + w_->valueSpin_->setMaximum(maxVal); + w_->valueSpin_->setSingleStep(step); +} + +void RepeatIntEditor::setValue(QString val) +{ + w_->valueSpin_->setValue(val.toInt()); +} + +void RepeatIntEditor::slotValueChanged(int val) +{ + if(isListMode()) + { + QString txt=QString::number(val); + int row=modelData_.indexOf(txt); + if(row != -1) + { + w_->valueView_->setCurrentIndex(model_->index(row,0)); + } + else + { + w_->valueView_->clearSelection(); + w_->valueView_->setCurrentIndex(QModelIndex()); + } + } + checkButtonStatus(); +} + +void RepeatIntEditor::resetValue() +{ + w_->valueSpin_->setValue(oriVal_.toInt()); + slotValueChanged(oriVal_.toInt()); + checkButtonStatus(); +} + +bool RepeatIntEditor::isValueChanged() +{ + return(oriVal_.toInt() != w_->valueSpin_->value()); +} + +void RepeatIntEditor::apply() +{ + std::string val=QString::number(w_->valueSpin_->value()).toStdString(); + //std::string name=w_->nameLabel_->text().toStdString(); + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","repeat",val); + CommandHandler::run(info_,cmd); +} + +//================================================================ +// +// RepeatStringEditor +// +//================================================================ + +RepeatStringEditor::RepeatStringEditor(VInfo_ptr info,QWidget* parent) : + RepeatEditor(info,parent) +{ + //if(!repeat_) + // return; + + w_->hideRow(w_->valueSpin_); + w_->hideRow(w_->stepLabel_); + w_->valueLe_->setText(oriVal_); + + connect(w_->valueLe_,SIGNAL(textEdited(QString)), + this,SLOT(slotValueEdited(QString))); + + checkButtonStatus(); +} + +void RepeatStringEditor::setValue(QString val) +{ + w_->valueLe_->setText(val); +} + +void RepeatStringEditor::slotValueEdited(QString txt) +{ + if(isListMode()) + { + int row=modelData_.indexOf(txt); + if(row != -1) + { + w_->valueView_->setCurrentIndex(model_->index(row,0)); + } + else + { + w_->valueView_->clearSelection(); + w_->valueView_->setCurrentIndex(QModelIndex()); + } + } + checkButtonStatus(); +} + +void RepeatStringEditor::resetValue() +{ + w_->valueLe_->setText(oriVal_); + slotValueEdited(oriVal_); + checkButtonStatus(); +} + +bool RepeatStringEditor::isValueChanged() +{ + return(oriVal_ != w_->valueLe_->text()); +} + +void RepeatStringEditor::apply() +{ + std::string val=w_->valueLe_->text().toStdString(); + //std::string name=w_->nameLabel_->text().toStdString(); + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","repeat",val); + CommandHandler::run(info_,cmd); +} + +//================================================================ +// +// RepeatDateEditor +// +//================================================================ + +RepeatDateEditor::RepeatDateEditor(VInfo_ptr info,QWidget* parent) : + RepeatEditor(info,parent) +{ + //if(!repeat_) + // return; + + w_->hideRow(w_->valueSpin_); + w_->valueLe_->setText(oriVal_); + + connect(w_->valueLe_,SIGNAL(textEdited(QString)), + this,SLOT(slotValueEdited(QString))); + + checkButtonStatus(); +} + +void RepeatDateEditor::setValue(QString val) +{ + w_->valueLe_->setText(val); +} + +void RepeatDateEditor::slotValueEdited(QString txt) +{ + if(isListMode()) + { + int row=modelData_.indexOf(txt); + if(row != -1) + { + w_->valueView_->setCurrentIndex(model_->index(row,0)); + } + else + { + w_->valueView_->clearSelection(); + w_->valueView_->setCurrentIndex(QModelIndex()); + } + } + checkButtonStatus(); +} + +void RepeatDateEditor::resetValue() +{ + w_->valueLe_->setText(oriVal_); + slotValueEdited(oriVal_); + checkButtonStatus(); +} + +bool RepeatDateEditor::isValueChanged() +{ + return(oriVal_ != w_->valueLe_->text()); +} + +void RepeatDateEditor::apply() +{ + std::string val=w_->valueLe_->text().toStdString(); + //std::string name=w_->nameLabel_->text().toStdString(); + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","repeat",val); + CommandHandler::run(info_,cmd); +} + + +static AttributeEditorMaker makerStr1("repeat_integer"); +static AttributeEditorMaker makerStr2("repeat_string"); +static AttributeEditorMaker makerStr3("repeat_enumerated"); +static AttributeEditorMaker makerStr4("repeat_date"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef REPEATEDITOR_HPP +#define REPEATEDITOR_HPP + +#include "ui_RepeatEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +class QModelIndex; +class QStringList; +class QStringListModel; +class VRepeatAttr; +class RepeatEditor; + +class RepeatEditorWidget : public QWidget, protected Ui::RepeatEditorWidget +{ +friend class RepeatEditor; +friend class RepeatIntEditor; +friend class RepeatStringEditor; +friend class RepeatDateEditor; +public: + RepeatEditorWidget(QWidget *parent=0); +protected: + void hideRow(QWidget* w); +}; + +class RepeatEditor : public AttributeEditor +{ +Q_OBJECT + +public: + RepeatEditor(VInfo_ptr,QWidget* parent=0); + ~RepeatEditor(); + +protected Q_SLOTS: + void slotSelectedInView(const QModelIndex&,const QModelIndex&); + +protected: + void buildList(VRepeatAttr *rep); + bool isListMode() const; + virtual void setValue(QString)=0; + void readSettings(); + void writeSettings(); + + RepeatEditorWidget* w_; + //VRepeat* repeat_; + QStringListModel* model_; + QStringList modelData_; + QString oriVal_; +}; + +class RepeatIntEditor : public RepeatEditor +{ +Q_OBJECT +public: + RepeatIntEditor(VInfo_ptr,QWidget* parent=0); + +protected Q_SLOTS: + void slotValueChanged(int); + +protected: + void initSpinner(); + void apply(); + void setValue(QString val); + void resetValue(); + bool isValueChanged(); +}; + +class RepeatStringEditor : public RepeatEditor +{ +Q_OBJECT +public: + RepeatStringEditor(VInfo_ptr,QWidget* parent=0); + +protected Q_SLOTS: + void slotValueEdited(QString); + +protected: + void apply(); + void setValue(QString val); + void resetValue(); + bool isValueChanged(); +}; + +class RepeatDateEditor : public RepeatEditor +{ +Q_OBJECT +public: + RepeatDateEditor(VInfo_ptr,QWidget* parent=0); + +protected Q_SLOTS: + void slotValueEdited(QString); + +protected: + void apply(); + void setValue(QString val); + void resetValue(); + bool isValueChanged(); +}; + +#endif // REPEATEDITOR_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/RepeatEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RepeatEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,122 @@ + + + RepeatEditorWidget + + + + 0 + 0 + 467 + 390 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Name: + + + + + + + TextLabel + + + + + + + Start: + + + + + + + TextLabel + + + + + + + End: + + + + + + + TextLabel + + + + + + + Step: + + + + + + + TextLabel + + + + + + + Value: + + + + + + + + + + + 0 + 1 + + + + QListView::Fixed + + + + + + + Value: + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,89 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "RichTextEdit.hpp" + +#include +#include +#include +#include +#include +#include + +#include "VConfig.hpp" +#include "UiLog.hpp" + +RichTextEdit::RichTextEdit(QWidget * parent) : + QTextBrowser(parent), + fontProp_(0) +{ + QFont f("Courier"); + //QFont f("Monospace"); + //f.setStyleHint(QFont::TypeWriter); + f.setFixedPitch(true); + f.setPointSize(10); + //f.setStyleStrategy(QFont::PreferAntialias); + setFont(f); +} + +RichTextEdit::~RichTextEdit() +{ + if(fontProp_) + fontProp_->removeObserver(this); +} + +//--------------------------------------------- +// Fontsize management +//--------------------------------------------- + +void RichTextEdit::setFontProperty(VProperty* p) +{ + fontProp_=p; + fontProp_->addObserver(this); + updateFont(); +} + +void RichTextEdit::slotZoomIn() +{ + zoomIn(); + fontSizeChangedByZoom(); +} + +void RichTextEdit::slotZoomOut() +{ + int oriSize=font().pointSize(); + zoomOut(); + + if(font().pointSize() != oriSize) + fontSizeChangedByZoom(); +} + +void RichTextEdit::fontSizeChangedByZoom() +{ + if(fontProp_) + fontProp_->setValue(font()); +} + +void RichTextEdit::updateFont() +{ + if(fontProp_) + { + QFont f=fontProp_->value().value(); + if(font() != f) + setFont(f); + } +} + +void RichTextEdit::notifyChange(VProperty* p) +{ + if(fontProp_ ==p) + { + setFont(p->value().value()); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,43 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef RICHTEXTEDIT_HPP +#define RICHTEXTEDIT_HPP + +#include + +#include "VProperty.hpp" + +class RichTextEdit : public QTextBrowser, public VPropertyObserver +{ +Q_OBJECT + +public: + explicit RichTextEdit(QWidget* parent = 0); + ~RichTextEdit(); + + void setFontProperty(VProperty* p); + void updateFont(); + void notifyChange(VProperty* p); + +public Q_SLOTS: + void slotZoomIn(); + void slotZoomOut(); + +Q_SIGNALS: + void fontSizeChangedByWheel(); + +private: + void fontSizeChangedByZoom(); + + VProperty *fontProp_; +}; + + +#endif // RICHTEXTEDIT_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchInterface.cpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchInterface.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchInterface.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,231 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "RichTextSearchInterface.hpp" + +#include + +RichTextSearchInterface::RichTextSearchInterface() : editor_(NULL) +{ +} + + +bool RichTextSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode) +{ + if(!editor_) + return false; + + if(editor_->document()->isEmpty()) + return false; + + QTextCursor cursor(editor_->textCursor()); + + if (highlightAll) // if highlighting all matches, start from the start of the document + cursor.movePosition(QTextCursor::Start); + + else // move the cursor? + cursor.movePosition(move); + + + QList extraSelections; + bool found = false; + bool keepGoing = true; + int numMatches = 0; + + Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; + + while (keepGoing) + { + switch (matchMode) + { + case StringMatchMode::ContainsMatch: + { + cursor = editor_->document()->find(str, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + case StringMatchMode::WildcardMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + regexp.setPatternSyntax(QRegExp::Wildcard); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + case StringMatchMode::RegexpMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + + cursor = editor_->document()->find(regexp, cursor, flags); // perform the search + found = (!cursor.isNull()); + break; + } + + default: + { + break; + } + } + + + if (found) + { + if (highlightAll) + { + QTextEdit::ExtraSelection highlight; + highlight.cursor = cursor; + highlight.format.setBackground(highlightColour_); + extraSelections << highlight; + numMatches++; + } + else + { + editor_->setTextCursor(cursor); // mark the selection of the match + } + } + + + if (found && !highlightAll) // found a match and we only want one - stop here and select it + keepGoing = false; + + else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around + keepGoing = false; + + if (!found && highlightAll) // want to highlight all, but no more matches found + keepGoing = false; + + + + // not found, and we only want one match, then we need to wrap around and keep going + if (keepGoing) + { + if (!highlightAll) + { + cursor=editor_->textCursor(); + if (flags & QTextDocument::FindBackward) + cursor.movePosition(QTextCursor::End); + else + cursor.movePosition(QTextCursor::Start); + iteration = 1; // iteration=1 to avoid infinite wraparound! + } + } + } + + + if (highlightAll) + { + //char num[64]; + //sprintf(num, "%d", numMatches); + //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); + + editor_->setExtraSelections( extraSelections ); + } + + return (found); +} + +void RichTextSearchInterface::automaticSearchForKeywords(bool userClickedReload) +{ + if(editor_->document()->isEmpty()) + return; + + bool performSearch = vpPerformAutomaticSearch_->value().toBool(); + + if (performSearch) + { + // search direction + QTextDocument::FindFlags findFlags; + QTextCursor cursor(editor_->textCursor()); + std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); + QTextCursor::MoveOperation move; + if (searchFrom == "bottom") + { + findFlags = QTextDocument::FindBackward; + move = QTextCursor::End; + } + else + { + move = QTextCursor::Start; + } + + // case sensitivity + bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); + if (caseSensitive) + findFlags = findFlags | QTextDocument::FindCaseSensitively; + + // string match mode + std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); + StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); + + // the term to be searched for + std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); + QString searchTerm = QString::fromStdString(searchTerm_s); + + // perform the search + bool found = findString (searchTerm, false, findFlags, move, 1, mode); + + if(!found) + { + if(userClickedReload) + { + // move the cursor to the start of the last line + gotoLastLine(); + } + } + } + else + { + // move the cursor to the start of the last line + gotoLastLine(); + } +} + +void RichTextSearchInterface::refreshSearch() +{ + if(!editor_) + return; + + QTextCursor cursor(editor_->textCursor()); + if (cursor.hasSelection()) + { + cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); + editor_->setTextCursor(cursor); + } +} + + +void RichTextSearchInterface::clearHighlights() +{ + if(!editor_) + return; + + QList empty; + editor_->setExtraSelections(empty); +} + +void RichTextSearchInterface::disableHighlights() +{ + clearHighlights(); +} + + +void RichTextSearchInterface::gotoLastLine() +{ + // move the cursor to the start of the last line + QTextCursor cursor = editor_->textCursor(); + cursor.movePosition(QTextCursor::End); + cursor.movePosition(QTextCursor::StartOfLine); + editor_->setTextCursor(cursor); + editor_->ensureCursorVisible(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchInterface.hpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchInterface.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchInterface.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,40 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef RichTextSearchInterface_HPP +#define RichTextSearchInterface_HPP + +#include "AbstractTextEditSearchInterface.hpp" + +class QTextBrowser; + +class RichTextSearchInterface : public AbstractTextEditSearchInterface +{ +public: + RichTextSearchInterface(); + void setEditor(QTextBrowser* e) {editor_=e;} + + bool findString(QString str, bool highlightAll, QTextDocument::FindFlags findFlags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); + + void automaticSearchForKeywords(bool); + void refreshSearch(); + void clearHighlights(); + void disableHighlights(); + void enableHighlights() {} + bool highlightsNeedSearch() {return true;} + void gotoLastLine(); + +protected: + + QTextBrowser *editor_; +}; + +#endif // RichTextSearchInterface_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "RichTextSearchLine.hpp" +#include "RichTextSearchInterface.hpp" + +#include + +RichTextSearchLine::RichTextSearchLine(QWidget *parent) : + TextEditSearchLine(parent) +{ + interface_=new RichTextSearchInterface; + TextEditSearchLine::setSearchInterface(interface_); +} + +RichTextSearchLine::~RichTextSearchLine() +{ + delete interface_; +} + +void RichTextSearchLine::setEditor(QTextBrowser *e) +{ + RichTextSearchInterface *pti=static_cast(interface_); + assert(pti); + pti->setEditor(e); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/RichTextSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/RichTextSearchLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,32 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef RICHTEXTSEARCHLINE_HPP +#define RICHTEXTSEARCHLINE_HPP + +#include + +#include "TextEditSearchLine.hpp" + +class AbstractTextSearchInterface; + +class RichTextSearchLine : public TextEditSearchLine +{ +public: + explicit RichTextSearchLine(QWidget *parent=0); + ~RichTextSearchLine(); + void setEditor(QTextBrowser*); + +private: + //The interface is set internally + void setSearchInterface(AbstractTextSearchInterface*) {} + +}; + +#endif // RICHTEXTSEARCHLINE_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,120 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include + + +#include "SaveSessionAsDialog.hpp" +#include "ui_SaveSessionAsDialog.h" + +#include "DirectoryHandler.hpp" + + +SaveSessionAsDialog::SaveSessionAsDialog(QWidget *parent) : QDialog(parent) +{ + //ui->setupUi(this); + setupUi(this); + + refreshListOfSavedSessions(); + + // ensure the correct state of the Save button + on_sessionNameEdit__textChanged(); +} + +SaveSessionAsDialog::~SaveSessionAsDialog() +{ + //delete ui; +} + +void SaveSessionAsDialog::refreshListOfSavedSessions() +{ + //sessionsTable_->clearContents(); + savedSessionsList_->clear(); + + // get the list of existing sessions + int numSessions = SessionHandler::instance()->numSessions(); + for (int i = 0; i < numSessions; i++) + { + SessionItem *s = SessionHandler::instance()->sessionFromIndex(i); + addSessionToTable(s); + } +} + +void SaveSessionAsDialog::addSessionToTable(SessionItem *s) +{ + + savedSessionsList_->addItem(QString::fromStdString(s->name())); +/* + int lastRow = sessionsTable_->rowCount()-1; + sessionsTable_->insertRow(lastRow+1); + + QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(s->name())); + sessionsTable_->setItem(lastRow+1, 0, nameItem); +*/ +} + +bool SaveSessionAsDialog::validSaveName(const std::string &name) +{ + QString boxName(QObject::tr("Save session")); + // name empty? + if (name.empty()) + { + QMessageBox::critical(0,boxName, tr("Please enter a name for the session")); + return false; + } + + + // is there already a session with this name? + bool sessionWithThisName = (SessionHandler::instance()->find(name) != NULL); + + if (sessionWithThisName) + { + QMessageBox::critical(0,boxName, tr("A session with that name already exists - please choose another name")); + return false; + } + else + { + return true; + } +} + +void SaveSessionAsDialog::on_sessionNameEdit__textChanged() +{ + // only allow to save a non-empty session name + saveButton_->setEnabled(!sessionNameEdit_->text().isEmpty()); +} + + +void SaveSessionAsDialog::on_saveButton__clicked() +{ + std::string name = sessionNameEdit_->text().toStdString(); + + if (validSaveName(name)) + { + SessionItem* newSession = SessionHandler::instance()->copySession(SessionHandler::instance()->current(), name); + if (newSession) + { + //SessionHandler::instance()->add(name); + refreshListOfSavedSessions(); + SessionHandler::instance()->current(newSession); + QMessageBox::information(0,tr("Session"), tr("Session saved")); + } + else + { + QMessageBox::critical(0,tr("Session"), tr("Failed to save session")); + } + close(); + } +} + +// called when the user clicks the Save button +//void SaveSessionAsDialog::accept() +//{ +// +//} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SAVESESSIONASDIALOG_HPP +#define SAVESESSIONASDIALOG_HPP + +#include +#include "ui_SaveSessionAsDialog.h" + +#include "SessionHandler.hpp" + +namespace Ui { +class SaveSessionAsDialog; +} + +class SaveSessionAsDialog : public QDialog, protected Ui::SaveSessionAsDialog +{ + Q_OBJECT + +public: + explicit SaveSessionAsDialog(QWidget *parent = 0); + ~SaveSessionAsDialog(); + +public Q_SLOTS: + void on_saveButton__clicked(); + void on_sessionNameEdit__textChanged(); + +private: + //Ui::SaveSessionAsDialog *ui; + void addSessionToTable(SessionItem *s); + bool validSaveName(const std::string &name); + void refreshListOfSavedSessions(); +}; + +#endif // SAVESESSIONASDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/SaveSessionAsDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SaveSessionAsDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,111 @@ + + + SaveSessionAsDialog + + + + 0 + 0 + 411 + 292 + + + + Save session + + + + + + Existing sessions + + + + + + false + + + QAbstractItemView::NoSelection + + + + + + + New session: + + + + + + + + + Name: + + + + + + + + + + &Save + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + buttonBox_ + accepted() + SaveSessionAsDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox_ + rejected() + SaveSessionAsDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ScriptItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ScriptItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ScriptItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ScriptItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,146 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ScriptItemWidget.hpp" + +#include "Highlighter.hpp" +#include "InfoProvider.hpp" +#include "MessageLabel.hpp" +#include "VConfig.hpp" +#include "VNode.hpp" +#include "VReply.hpp" + +//======================================================== +// +// ScriptItemWidget +// +//======================================================== + +ScriptItemWidget::ScriptItemWidget(QWidget *parent) : CodeItemWidget(parent) +{ + messageLabel_->setShowTypeTitle(false); + messageLabel_->hide(); + + //Remove the first spacer item!! + removeSpacer(); + + //The document becomes the owner of the highlighter + new Highlighter(textEdit_->document(),"script"); + + infoProvider_=new ScriptProvider(this); + + //Editor font + textEdit_->setFontProperty(VConfig::instance()->find("panel.script.font")); +} + +ScriptItemWidget::~ScriptItemWidget() +{ +} + +QWidget* ScriptItemWidget::realWidget() +{ + return this; +} + +void ScriptItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + info_=info; + messageLabel_->hide(); + + //Info must be a node + if(info_ && info_->isNode() && info_->node()) + { + reloadTb_->setEnabled(false); + infoProvider_->info(info_); + } +} + +void ScriptItemWidget::clearContents() +{ + InfoPanelItem::clear(); + fileLabel_->clear(); + textEdit_->clear(); + messageLabel_->hide(); + reloadTb_->setEnabled(true); + clearCurrentFileName(); +} + +void ScriptItemWidget::infoReady(VReply* reply) +{ + Q_ASSERT(reply); + + messageLabel_->hide(); + + QString s=QString::fromStdString(reply->text()); + textEdit_->setPlainText(s); + + if(reply->hasWarning()) + { + messageLabel_->showWarning(QString::fromStdString(reply->warningText())); + } + else if(reply->hasInfo()) + { + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); + } + + fileLabel_->update(reply); + reloadTb_->setEnabled(true); + setCurrentFileName(reply->fileName()); +} + +void ScriptItemWidget::infoProgress(VReply* reply) +{ + QString s=QString::fromStdString(reply->text()); + messageLabel_->showInfo(QString::fromStdString(reply->infoText())); +} + +void ScriptItemWidget::infoFailed(VReply* reply) +{ + QString s=QString::fromStdString(reply->errorText()); + //textEdit_->setPlainText(s); + messageLabel_->showError(s); + reloadTb_->setEnabled(true); +} + +void ScriptItemWidget::reloadRequested() +{ + reload(info_); +} + +void ScriptItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + //Suspend + if(suspended_) + { + reloadTb_->setEnabled(false); + } + //Resume + else + { + if(info_ && info_->node()) + { + reloadTb_->setEnabled(true); + } + else + { + clearContents(); + } + } + } +} + +static InfoPanelItemMaker maker1("script"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ScriptItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ScriptItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ScriptItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ScriptItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SCRIPTITEMWIDGET_HPP_ +#define SCRIPTITEMWIDGET_HPP_ + +#include "InfoPanelItem.hpp" +#include "CodeItemWidget.hpp" + +class ScriptItemWidget : public CodeItemWidget, public InfoPanelItem +{ +public: + explicit ScriptItemWidget(QWidget *parent=0); + ~ScriptItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + //From VInfoPresenter + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected: + void updateState(const ChangeFlags&); + void reloadRequested(); +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SearchLineWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/SearchLineWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/SearchLineWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SearchLineWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,217 @@ + + + SearchLineWidget + + + + 0 + 0 + 744 + 266 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + &Find: + + + searchLine_ + + + + + + + + + + + + + + + + + 0 + 0 + + + + Qt::ToolButtonIconOnly + + + true + + + + + + + + + + Qt::ToolButtonIconOnly + + + true + + + + + + + Search options + + + Options + + + + :/viewer/images/configure.svg:/viewer/images/configure.svg + + + QToolButton::InstantPopup + + + true + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close search bar + + + ... + + + true + + + + + + + true + + + Case sensitive + + + Case sensitive search + + + Alt+C + + + + + true + + + Whole words + + + Search for whole words only + + + Alt+O + + + + + + :/viewer/images/arrow_down.svg:/viewer/images/arrow_down.svg + + + Find next + + + Jump to next match <code>F3</code> + + + F3 + + + + + + :/viewer/images/arrow_up.svg:/viewer/images/arrow_up.svg + + + Find prev + + + Jump to previous match <code>Shift+F3</code> + + + Shift+F3 + + + + + true + + + Highlight All + + + Highlight all occurrences of the text + + + Alt+A + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    + + StringMatchCombo + QComboBox +
    StringMatchCombo.hpp
    +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerAddDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerAddDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerAddDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,120 @@ + + + ServerAddDialog + + + + 0 + 0 + 294 + 172 + + + + Add new server + + + true + + + + + + + + &Name: + + + label + + + + + + + + + + + + + &Host: + + + hostEdit + + + + + + + &Port: + + + portEdit + + + + + + + + + + Add server to current &view + + + true + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ServerAddDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox + rejected() + ServerAddDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComInfoWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComInfoWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComInfoWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComInfoWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1232 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ServerComInfoWidget.hpp" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "FontMetrics.hpp" +#include "PropertyMapper.hpp" +#include "ServerHandler.hpp" +#include "TextFormat.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "VNode.hpp" + +QIcon* ServerRefreshInfoWidget::icon_=0; +QBrush ServerRefreshInfoWidget::buttonBgBrush_(QColor(229,228,227)); +QBrush ServerRefreshInfoWidget::serverBgBrush_(QColor(241,242,243)); +QPen ServerRefreshInfoWidget::borderPen_(QColor(197,197,197)); +QPen ServerRefreshInfoWidget::buttonBorderPen_(QColor(167,167,167)); +QPen ServerRefreshInfoWidget::disabledBorderPen_(QColor(182,182,182)); +QBrush ServerRefreshInfoWidget::buttonBgHoverBrush_(QColor(249,248,248)); +QPen ServerRefreshInfoWidget::buttonHoverPen_(QColor(160,160,160)); +QBrush ServerRefreshInfoWidget::buttonBgRefreshBrush_(QColor(214,227,213)); +QBrush ServerRefreshInfoWidget::periodBgBrush_(QColor(241,242,243)); +QBrush ServerRefreshInfoWidget::progBrush_(QColor(117,165,230)); +QBrush ServerRefreshInfoWidget::progBgBrush_(QColor(255,255,255)); +QBrush ServerRefreshInfoWidget::lastBgBrush_(QColor(238,238,238)); +QPen ServerRefreshInfoWidget::buttonRefreshPen_(QColor(79,179,100),2); +QPen ServerRefreshInfoWidget::serverTextPen_(QColor(80,80,80)); +QPen ServerRefreshInfoWidget::periodTextPen_(QColor(45,45,45)); +QPen ServerRefreshInfoWidget::driftTextPen_(QColor(120,120,120)); +QPen ServerRefreshInfoWidget::lastTextPen_(QColor(45,45,45)); +QPen ServerRefreshInfoWidget::disabledTextPen_(QColor(180,180,180)); + +//#define _UI_SERVERCOMINFOWIDGET_DEBUG + +#if 0 +ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent) : + QWidget(parent), + refreshAction_(refreshAction) +{ + Q_ASSERT(refreshAction_); + + QHBoxLayout *hb=new QHBoxLayout(this); + hb->setContentsMargins(0,0,0,0); + hb->setSpacing(0); + + //QToolButton* refreshTb=new QToolButton(this); + //refreshTb->setAutoRaise(true); + //refreshTb->setDefaultAction(refreshAction_); + //hb->addWidget(refreshTb); + + infoW_=new ServerComLineDisplay(this); + hb->addWidget(infoW_); + + IconProvider::add(":/viewer/reload_one.svg","reload_one"); + + +} +#endif + +ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent) : + QWidget(parent), + refreshAction_(refreshAction), + server_(0), + fontServer_(QFont()), + fontPeriod_(QFont()), + fontLast_(QFont()), + fmServer_(QFont()), + fmServerReal_(QFont()), + fmPeriod_(QFont()), + fmLast_(QFont()), + periodTextWidth_(0), + periodTextWidthMin_(0), + periodDummyText_(" D=300s "), + periodDummyFullText_(" D=300s d=99s"), + currentComponent_(NoComponent), + progRectHeight_(2), + serverRectHeight_(10), + serverYPadding_(2), + prop_(0), + mode_(NoMode), + noBlinkLimit_(15), + hasInfo_(false), + inRefresh_(true), + userInitiatedRefresh_(false), + showLastAutoRefresh_(true), + total_(-1), + period_(-1), + toNext_(-1), + drift_(-1), + needBorder_(true) +{ + Q_ASSERT(refreshAction_); + + //The icon for the round refresh button + if(!icon_) + icon_=new QIcon(QPixmap(":/viewer/reload_green.svg")); + + //Init fonts + fontServer_=QFont(); + fontServer_.setPointSize(fontServer_.pointSize()-1); + fontServer_.setBold(true); + fmServer_=QFontMetrics(fontServer_); + fmServerReal_=FontMetrics(fontServer_); + + fontPeriod_=QFont(); + fontPeriod_.setPointSize(fontPeriod_.pointSize()-2); + fmPeriod_=QFontMetrics(fontPeriod_); + + fontLast_=QFont(); + fontLast_.setPointSize(fontLast_.pointSize()-2); + fmLast_=QFontMetrics(fontLast_); + + int w=200; + serverRectHeight_=fmServerReal_.realHeight()+2*serverYPadding_; + int h=serverRectHeight_+2*4; + + //timer + timer_=new QTimer(this); + + connect(timer_,SIGNAL(timeout()), + this,SLOT(slotTimeOut())); + + //set size + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); + setMinimumSize(w,h); + + buttonRect_=QRect(1,1,h-2,h-2); + buttonRadius2_=pow(buttonRect_.width()/2,2); + + adjustGeometry(false); + + //we need this for the mousemove event + setMouseTracking(true); + + //properties to use + std::vector propVec; + propVec.push_back("server.update.blinkUpdateButtonLimit"); + propVec.push_back("server.update.showLastRefreshTimeInAutoMode"); + prop_=new PropertyMapper(propVec,this); + updateSettings(); +} + +ServerRefreshInfoWidget::~ServerRefreshInfoWidget() +{ + //Detach from the server + if(server_) + { + server_->removeServerObserver(this); + server_->removeServerComObserver(this); + } + delete prop_; +} + +bool ServerRefreshInfoWidget::isActionEnabled() const +{ + return refreshAction_->isEnabled(); +} + +void ServerRefreshInfoWidget::notifyChange(VProperty* p) +{ + updateSettings(); +} + +void ServerRefreshInfoWidget::updateSettings() +{ + bool fetched=false; + bool changed=false; + if(VProperty* p=prop_->find("server.update.showLastRefreshTimeInAutoMode")) + { + bool v=p->value().toBool(); + if(showLastAutoRefresh_!= v) + { + showLastAutoRefresh_=v; + adjustGeometry(true); + fetched=true; + changed=true; + } + } + + if(VProperty* p=prop_->find("server.update.blinkUpdateButtonLimit")) + { + int v=p->value().toInt(); + if(noBlinkLimit_ != v) + { + noBlinkLimit_=v; + changed=true; + } + } + + if(changed) + { + if(!fetched) + { + fetchInfo(); + } + update(); + } +} + +void ServerRefreshInfoWidget::setServer(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + + if(server_ != server && server_) + { + server_->removeServerObserver(this); + server_->removeServerComObserver(this); + } + + server_=server; + + if(server_) + { + server_->addServerObserver(this); + server_->addServerComObserver(this); + } + else + { + hasInfo_=0; + } + + refreshAction_->setEnabled(true); + inRefresh_=false; + userInitiatedRefresh_=false; + mode_=NoMode; + + //Cache some data + serverName_.clear(); + serverText_.clear(); + if(server_) + { + serverName_=QString::fromStdString(server_->name()); + serverText_=" " + serverName_ + " "; + } + + periodText_.clear(); + driftText_.clear(); + periodTextWidthMin_=0; + periodTextWidth_=0; + lastTextWidth_=0; + + //Adjust width + get info + adjustGeometry(true); + + //rerender + update(); +} + +//------------------------------- +// Server observer notifications +//------------------------------- + +void ServerRefreshInfoWidget::notifyServerDelete(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + + Q_ASSERT(server_ == server); + if(server_ == server) + { + server_->removeServerObserver(this); + server_->removeServerComObserver(this); + server_=0; + serverName_.clear(); + serverText_.clear(); + hasInfo_=false; + mode_=NoMode; + inRefresh_=false; + userInitiatedRefresh_=false; + periodText_.clear(); + driftText_.clear(); + periodTextWidthMin_=0; + periodTextWidth_=0; + lastTextWidth_=0; + + refreshAction_->setEnabled(false); + + //get info and rerender + reloadAll(); + + adjustGeometry(false); + } +} + +//While the server is being reloaded the refresh button is disabled. It must be followed by a +//notifyEndServerScan() call! +void ServerRefreshInfoWidget::notifyBeginServerClear(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + timer_->stop(); + refreshAction_->setEnabled(false); + hasInfo_=false; + inRefresh_=false; + userInitiatedRefresh_=false; + mode_=NoMode; + periodText_.clear(); + driftText_.clear(); + periodTextWidthMin_=0; + periodTextWidth_=0; + lastTextWidth_=0; + + update(); +} + +//The server has been reloaded. We must get the current state. +void ServerRefreshInfoWidget::notifyEndServerScan(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + refreshAction_->setEnabled(true); + reloadAll(); +} + +//virtual void notifyServerConnectState(ServerHandler* server) {} +void ServerRefreshInfoWidget::notifyServerActivityChanged(ServerHandler* /*server*/) +{ + +} + +//----------------------------------- +// Server com observer notifications +//----------------------------------- + +void ServerRefreshInfoWidget::notifyRefreshTimerStarted(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + reloadAll(); //get info and rerender +} + +void ServerRefreshInfoWidget::notifyRefreshTimerStopped(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + reloadAll(); //get info and rerender +} + +void ServerRefreshInfoWidget::notifyRefreshTimerChanged(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG + printStatus(); +#endif + Q_ASSERT(server_ == server); + + //if we are in refresh we do not want to fetch any new information unless we are in + //NoMode + if(!inRefresh_ || mode_==NoMode) + { + reloadAll(); //get info and rerender + } +} + +//While the refresh is being executed the the refresh button +void ServerRefreshInfoWidget::notifyRefreshScheduled(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + inRefresh_=true; + inRefreshElapsed_.restart(); + if(mode_ == NormalMode) + { + timer_->stop(); + +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + printStatus(); +#endif + //redraw + update(); + } +} + +void ServerRefreshInfoWidget::notifyRefreshFinished(ServerHandler* server) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(server_ == server); + int elapsed=inRefreshElapsed_.elapsed(); + + if((mode_ == NormalMode) && elapsed < 450) + { + Q_ASSERT(500-elapsed > 0); + //We keep the button in inRefresh state for 0.5 sec (the timer is stopped now!!) + QTimer::singleShot(500-elapsed,this,SLOT(slotTimeOutRefreshFinished())); +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + printStatus(); +#endif + } + else + { + inRefresh_=false; + userInitiatedRefresh_=false; + } +} + +void ServerRefreshInfoWidget::reloadAll() +{ + fetchInfo(); //get info + update(); //renrender +} + +void ServerRefreshInfoWidget::slotTimeOut() +{ + reloadAll(); +} + +void ServerRefreshInfoWidget::slotTimeOutRefreshFinished() +{ + inRefresh_=false; + userInitiatedRefresh_=false; + reloadAll(); +} + +void ServerRefreshInfoWidget::fetchInfo() +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG + printStatus(); +#endif + + if(!server_) + { + mode_=NoMode; + hasInfo_=false; + timer_->stop(); + } + else + { + QDateTime currentTime=QDateTime::currentDateTime(); + bool v=server_->updateInfo(period_,total_,drift_,toNext_); + + bool geoUpdateNeeded=(v != hasInfo_); + hasInfo_=v; + +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UiLog().dbg() << " period=" << period_ << " total=" << total_ << + " toNext=" << toNext_; +#endif + + //the server has an automatic update + if(hasInfo_) + { + lastRefresh_=server_->lastRefresh().time().toString(); //currentTime.addSecs(-total_+toNext_).time().toString(); + nextRefresh_=currentTime.addSecs(toNext_).time().toString(); + + mode_=NormalMode; + if(!inRefresh_) + refreshAction_->setEnabled(true); + } + //the server's automatic refresh is switched off + else + { + lastRefresh_=server_->lastRefresh().time().toString(); + mode_=ManualMode; + if(!inRefresh_) + refreshAction_->setEnabled(true); + } + + //Determine period text + determinePeriodText(); + + if(geoUpdateNeeded || periodTextWidthAboutToChange()) + adjustGeometry(false); + + //if(currentComponent_ == TextComponent) + adjustToolTip(); + + adjustTimer(toNext_); + } + +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + printStatus(); +#endif +} + +//Adjust timer interval +void ServerRefreshInfoWidget::adjustTimer(int toNext) +{ + //we stop the timer when: + // -the action is disabled + // -we are in fast mode + // -the countdown is not shown + if(!refreshAction_->isEnabled() || mode_ == NoMode || mode_ == ManualMode) + { + timer_->stop(); + + } + else if(hasInfo_) + { + if(total_ <= 1) + { + timer_->stop(); + return; + } + else if(total_==2) + timer_->setInterval(750); + else + { + Q_ASSERT(total_ > 0); + int progWidth=periodTextWidth_; + Q_ASSERT(progWidth > 0); + float secPerPix=static_cast(total_)/static_cast(progWidth); + float r=ceil(secPerPix); + Q_ASSERT(r >= 1.); + + if(toNext > 135) + { + if(r > 30) + r=60; + else if(r > 15) + r=30; + else + r=15; + } + else if(toNext > 60) + { + if(r < 10) + r=10; + } + else if(toNext > 30) + { + if(r < 5) + r=5; + } + else if(toNext > 5) + { + if(r < 2.5) + r=2.5; + } + else + { + r=1; + } + + timer_->setInterval(static_cast(r*1000.)); + } + + if(!timer_->isActive()) + timer_->start(); + } + else + { + timer_->stop(); + } +} + +//Check if a point is inside the (round) button +bool ServerRefreshInfoWidget::isInButton(const QPoint& pos) const +{ + QPoint d=pos-buttonRect_.center(); + return d.x()*d.x()+d.y()*d.y() <= buttonRadius2_; +} + +bool ServerRefreshInfoWidget::isInText(const QPoint& pos) const +{ + return (pos.x() > buttonRect_.right()); +} + +void ServerRefreshInfoWidget::resizeEvent(QResizeEvent* event) +{ + buttonRect_=QRect(1,1,height()-2,height()-2); + buttonRadius2_=pow(buttonRect_.width()/2,2); +} + +void ServerRefreshInfoWidget::mouseDoubleClickEvent(QMouseEvent* event) +{ + if(server_ && !isInButton(event->pos())) + { + Q_EMIT serverSettingsEditRequested(server_); + } +} + +void ServerRefreshInfoWidget::mousePressEvent(QMouseEvent* event) +{ + if(!refreshAction_->isEnabled()) + return; + + //We are in the button + if(isInButton(event->pos())) + { +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG +// UiLog().dbg() << "pressed"; +#endif + if(currentComponent_ != ButtonComponent) + { + currentComponent_ = ButtonComponent; + } + userInitiatedRefresh_=true; + refreshAction_->trigger(); + } +} + +void ServerRefreshInfoWidget::mouseMoveEvent(QMouseEvent* event) +{ + //We are in the button + if(isInButton(event->pos())) + { +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG +// UiLog().dbg() << "inButton"; +#endif + //we just entered the button + if(currentComponent_ != ButtonComponent) + { + currentComponent_=ButtonComponent; + adjustToolTip(); + if(refreshAction_->isEnabled()) + { + update(); //rerender + } + } + } + //We are in the progress part + else if(isInText(event->pos())) + { +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG +// UiLog().dbg() << "inText"; +#endif + //we came from the button + if(currentComponent_ == ButtonComponent) + { + currentComponent_=TextComponent; + adjustToolTip(); + if(refreshAction_->isEnabled()) + { + update(); //rerender + } + } + //we came from outside + else if(currentComponent_ != TextComponent) + { + currentComponent_=TextComponent; + adjustToolTip(); + } + } +} + +void ServerRefreshInfoWidget::leaveEvent(QEvent*) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + //UI_FUNCTION_LOG +#endif + currentComponent_=NoComponent; + update(); //rerender +} + + +void ServerRefreshInfoWidget::paintEvent(QPaintEvent*) +{ + QPainter painter(this); + drawProgress(&painter); + drawButton(&painter); +} + + +QString ServerRefreshInfoWidget::formatPeriodTime(int timeInSec) const +{ + int h=timeInSec/3600; + int r=timeInSec%3600; + int m=r/60; + int s=r%60; + + QTime t(h,m,s); + if(h > 0) + return QString::number(h) + QString(":%1h").arg(m, 2, 10, QChar('0')); + else if(m >= 5) + return QString::number(m) + QString(":%1s").arg(s, 2, 10, QChar('0')); + else if(m> 0) + return QString::number(m*60+s) + "s"; + else + return QString::number(s) + "s"; + + return QString(); +} + +void ServerRefreshInfoWidget::determinePeriodText() +{ + periodText_.clear(); + driftText_.clear(); + if(hasInfo_) + { + //Unicode 916=Greek capital delta + periodText_=QString(" ") + QChar(916) + QString("=") + formatPeriodTime(total_) + " "; + if(drift_ > 0) + { + driftText_="d=" + formatPeriodTime(total_-period_) + " "; + } + } +} + +QString ServerRefreshInfoWidget::fullPeriodText() const +{ + return periodText_+driftText_; +} + +int ServerRefreshInfoWidget::determinePeriodTextWidthMin() const +{ + QString s=periodDummyText_; + if(hasInfo_ && drift_ > 0) + { + s=periodDummyFullText_; + } + return fmPeriod_.width(s); +} + +//Indicate if the full period text's size will change in such a way that the +//geometry needs to be adjusted +bool ServerRefreshInfoWidget::periodTextWidthAboutToChange() const +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + + int mval=determinePeriodTextWidthMin(); + + QString pt=fullPeriodText(); +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UiLog().dbg() << " full=" << pt << " pt.size=" << pt.size() << + " mval=" << mval << " periodTextWidthMin=" << periodTextWidthMin_ << + " periodTextSize " << periodTextWidth_; +#endif + bool changed=false; + if(periodTextWidthMin_ == mval) + { + int w=fmPeriod_.width(pt); + if(w > periodTextWidth_) + { + changed=w > periodTextWidthMin_; + } + else if(w < periodTextWidth_) + { + changed=periodTextWidth_ >= periodTextWidthMin_; + } + } + else + { + changed=true; + } +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UiLog().dbg() << " changed=" << changed; +#endif + return changed; +} + +void ServerRefreshInfoWidget::adjustGeometry(bool doFetchInfo) +{ +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + + if(server_) + { + //timeTextLen_=qMax(fmTime_.width(" <9:59m"),fmUpdate_.width("updating")); + + //Define server rect + int h=serverRectHeight_; //fmServer_.height()+progRectHeight_; //;2*yPadding; + int currentRight=0; + + serverRect_=QRect(buttonRect_.center().x()+4,(height()-h)/2, + buttonRect_.width()/2-4+4+fmServer_.width(serverText_), + h); + currentRight=serverRect_.x()+serverRect_.width(); + + if(doFetchInfo) + fetchInfo(); + + //Determine the minimum size for the period text + periodTextWidthMin_=determinePeriodTextWidthMin(); + + //Compute physical width of the period text + QString pt=fullPeriodText(); + int w=fmPeriod_.width(pt); + if(w <= periodTextWidthMin_) + { + periodTextWidth_=periodTextWidthMin_; + } + else + { +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UiLog().dbg() << " width changed before=" << periodTextWidth_; +#endif + periodTextWidth_=w; +#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG + UiLog().dbg() << " width changed after=" << periodTextWidth_; +#endif + + } + periodRect_=QRect(); + progRect_=QRect(); + lastRect_=QRect(); + + if(hasInfo_) + { + periodRect_ = serverRect_; + periodRect_.setX(serverRect_.x()+serverRect_.width()); + periodRect_.setWidth(periodTextWidth_); + //periodRect_.setHeight(fmPeriod_.height()-2); + periodRect_.setHeight(serverRect_.height()-progRectHeight_); + + currentRight+=periodRect_.width(); + + progRect_ = periodRect_; + progRect_.setY(periodRect_.y()+periodRect_.height()); + progRect_.setHeight(serverRect_.height()-periodRect_.height()); + } + + lastTextWidth_=0; + if((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) + { + //Compute physical width of the last refresh text + lastTextWidth_=fmLast_.width(" last: 22:22:22 "); + + lastRect_ = QRect(currentRight,serverRect_.y(),lastTextWidth_,h); + currentRight+=lastRect_.width()+6; + } + else + currentRight+=6; + + setFixedWidth(currentRight); + } + else + { + periodTextWidthMin_=0; + periodTextWidth_=0; + lastTextWidth_=0; + setFixedWidth(buttonRect_.x()+buttonRect_.width()+4); + } +} + + +void ServerRefreshInfoWidget::drawButton(QPainter* painter) +{ + painter->setRenderHint(QPainter::Antialiasing,true); + + if(server_) + { + //blink + if(inRefresh_ && + ((mode_ == NormalMode && period_ >= noBlinkLimit_) || + userInitiatedRefresh_ || mode_ == ManualMode )) + { + painter->setBrush(buttonBgRefreshBrush_); + painter->setPen(buttonRefreshPen_); + } + else + { + painter->setBrush((currentComponent_ == ButtonComponent)?buttonBgHoverBrush_:buttonBgBrush_); + + if(!refreshAction_->isEnabled()) + painter->setPen(disabledBorderPen_); + else + painter->setPen((currentComponent_ == ButtonComponent)?buttonHoverPen_:buttonBorderPen_); + } + } + else + { + painter->setBrush(buttonBgBrush_); + painter->setPen(buttonBorderPen_); + } + + //The filled circle + painter->drawEllipse(buttonRect_); + + //The reload icon + QRect r1=buttonRect_.adjusted(2,2,-2,-2); + QRect r2=r1.adjusted(3,3,-3,-3); + QPixmap pix=icon_->pixmap(QSize(r2.width(),r2.width()), + refreshAction_->isEnabled()? QIcon::Normal: QIcon::Disabled); + painter->drawPixmap(r2,pix); +} + +void ServerRefreshInfoWidget::drawProgress(QPainter* painter) +{ + if(!server_) + return; + + //server bg + painter->setBrush(serverBgBrush_); + painter->drawRect(serverRect_); + + if(needBorder_) + { + painter->setBrush(Qt::NoBrush); + painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); + painter->drawRect(serverRect_); + } + + //Server text + QRect serverTextRect=serverRect_.adjusted(buttonRect_.width()/2-4+4,0,0,0); + painter->setFont(fontServer_); + painter->setPen((refreshAction_->isEnabled())?serverTextPen_:disabledTextPen_); + painter->drawText(serverTextRect,Qt::AlignHCenter | Qt::AlignVCenter,serverText_); + + //The time rects and texts + if(hasInfo_) + { + //backgrounds + painter->setPen(Qt::NoPen); + painter->setBrush(periodBgBrush_); + painter->drawRect(periodRect_); + painter->setBrush(progBgBrush_); + painter->drawRect(progRect_); + + //border + if(needBorder_) + { + painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); + painter->setBrush(Qt::NoBrush); + painter->drawRect(periodRect_.adjusted(0,0,0,progRect_.height())); + } + + painter->setFont(fontPeriod_); + + //period + drift text + if(drift_ > 0) + { + int tw=fmPeriod_.width(fullPeriodText()); + int w=periodRect_.width(); + QRect rr=periodRect_.adjusted((w-tw)/2,0,-(w-tw)/2,0); + + painter->setPen(periodTextPen_); + painter->drawText(rr,Qt::AlignLeft | Qt::AlignVCenter,periodText_); + painter->setPen(driftTextPen_); + painter->drawText(rr,Qt::AlignRight| Qt::AlignVCenter,driftText_); + } + else + { + painter->setPen(periodTextPen_); + painter->drawText(periodRect_,Qt::AlignHCenter | Qt::AlignVCenter,periodText_); + } + + //Progress + float progress; + QRect actProgRect=progRect_; + + if(total_ < 2 || inRefresh_) + progress=1; + else + { + UI_ASSERT(total_ != 0, "total_=" << total_); + progress=(static_cast(total_-toNext_)/static_cast(total_)); + UI_ASSERT(progress >= 0. && progress <= 1.0001, "progress=" << progress); + if(progress >= 1.) progress=1; + + int progressW=static_cast(static_cast(actProgRect.width())*progress); + if(progressW <0) progressW=0; + else if(progressW > progRect_.width()) progressW=progRect_.width(); + actProgRect.setWidth(progressW); + } + + painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); + painter->drawLine(progRect_.topLeft(),progRect_.topRight()); + + painter->fillRect(actProgRect,progBrush_); + } + + //last refresh time + if((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) + { + painter->setBrush(lastBgBrush_); + painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); + painter->drawRect(lastRect_); + + painter->setFont(fontLast_); + painter->setPen(lastTextPen_); + painter->drawText(lastRect_,Qt::AlignHCenter | Qt::AlignVCenter,"last: " + lastRefresh_); + } +} + +void ServerRefreshInfoWidget::adjustToolTip() +{ + QString txt; + + /* + if(mode_ == ContMode) + { + Q_ASSERT(server_); + Q_ASSERT(hasInfo_); + txt=tr("Refresh period is too short! Manual refreshing is disabled for server ") + + serverName_ + + "
    --------------------------------------------"+ + tr("
    Refresh period: ") + QString::number(total_) + "s" + + " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_-period_) +"s)"; + + } + */ + if(1) + { + if(currentComponent_ == ButtonComponent) + { + if(!server_) + { + txt=tr("Refresh selected server "); + } + else + { + txt=tr("Refresh server ") + serverName_ +" "; + } + + txt+=Viewer::formatShortCut(refreshAction_); + } + + if(hasInfo_) + { + Q_ASSERT(server_); + if(!txt.isEmpty()) + txt+="
    --------------------------------------------"; + else + txt+="Server: " + serverName_; + + txt+="
    Last refresh: " + lastRefresh_ + + "
    Next refresh: " + nextRefresh_ + + "
    Total refresh period: " + QString::number(total_) + "s" + + " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_-period_) +"s)"; + + /*if(drift_ > 0) + { + txt+="
    --------------------------------------------
    "; + txt+="When drift is enabled the server refresh period is increased at every automatic refresh until \ + the maximum period is reached. The drift is reset to zero when the user interacts with the server."; + + }*/ + + + } + else if(server_) + { + if(!txt.isEmpty()) + txt+="
    --------------------------------------------"; + else + txt+="Server: " + serverName_; + + txt+="
    Last refresh: " + lastRefresh_ + "
    " + + "Automatic refresh is disabled!"; + + } + + if(currentComponent_ != ButtonComponent && server_) + { + txt+="
    " + Viewer::formatShortCut("Double click to change refresh settings for server"); + } + } + + setToolTip(txt); +} + +#if 0 +QString ServerRefreshInfoWidget::formatTime(int timeInSec) const +{ + int h=timeInSec/3600; + int r=timeInSec%3600; + int m=r/60; + int s=r%60; + + QTime t(h,m,s); + if(h > 0) + return QString::number(h) + "h"; + else if(m > 2) + { + if(s >= 30) + return QString::number(m+1) + "m"; + else + return QString::number(m) + "m"; + } + else if(m >= 1) + { + if(s >= 45) + return "1:45m"; + else if(s >= 30) + return "1:30m"; + else if(s >= 15) + return "1:15m"; + else + return "1m"; + } + else if(s > 50) + return "<1m"; + else if(s > 45) + return "<50s"; + else if(s > 40) + return "<45s"; + else if(s > 35) + return "<40s"; + else if(s > 30) + return "<35s"; + else if(s > 25) + return "<30s"; + else if(s > 20) + return "<25s"; + else if(s > 15) + return "<20s"; + else if(s > 10) + return "<15s"; + else if(s > 5) + return "<10s"; + else + return "<5s"; //return QString("%1s").arg(s, 2, 10, QChar('0')); + + return QString(); +} +#endif + + + +void ServerRefreshInfoWidget::printStatus() const +{ + UiLog().dbg() << " server=" << server_ << " action=" << refreshAction_->isEnabled() << " hasInfo=" << hasInfo_ << + " mode=" << mode_ << " inRefresh=" << inRefresh_ << " timer=" << timer_->isActive() << + " timeout=" << timer_->interval()/1000. << "s"; +} + +ServerComActivityLine::ServerComActivityLine(QWidget *parent) : + QWidget(parent), + font_(QFont()), + fm_(font_), + server_(0) +{ + font_.setPointSize(font_.pointSize()-1); + fm_=QFontMetrics(font_); + + int width_=200; + int height_=fm_.height()+4+6; + + timer_=new QTimer(this); + + connect(timer_,SIGNAL(timeout()), + this,SLOT(update())); + + timer_->setInterval(1000); + timer_->start(); + + setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); + setMinimumSize(width_,height_); +} + +void ServerComActivityLine::setServer(ServerHandler* server) +{ + server_=server; +} + +void ServerComActivityLine::paintEvent(QPaintEvent*) +{ +#if 0 + if(!server_) + return; + + int currentRight=0; + int offset=4; + int yPadding=2; + + int period,total,drift,toNext; + bool hasUpdate=server_->updateInfo(period,total,drift,toNext); + bool hasDrift=(hasUpdate && drift > 0); + int h=height()-2*yPadding; + int progHeight=h; + int driftHeight=0; + + + if(hasDrift) + { + progHeight=fm_.height()+2; + driftHeight=h-progHeight; + } + + //progress rect - with the server name in it + QRect progRect=QRect(offset,yPadding, + fm_.width("ABCDEABDCEABCD"), + progHeight); + + QString progText=fm_.elidedText(QString::fromStdString(server_->name()), + Qt::ElideRight,progRect.width()); + + //drif rect + QRect driftRect; + if(hasDrift) + { + driftRect=progRect; + driftRect.setY(yPadding+progHeight); + driftRect.setHeight(driftHeight); + } + + //toNext rect + currentRight=progRect.x()+progRect.width()+offset; + + QString toNextText=QString::number(total) + "s"; + if(hasUpdate) + toNextText=formatTime(toNext); + + QRect toNextRect=progRect; + toNextRect.setX(currentRight); + toNextRect.setWidth(fm_.wi + +#endif + + +} + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComInfoWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComInfoWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComInfoWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComInfoWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,205 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERCOMLINE_HPP +#define SERVERCOMLINE_HPP + +#include +#include +#include +#include +#include +#include + +#include "FontMetrics.hpp" +#include "ServerObserver.hpp" +#include "ServerComObserver.hpp" +#include "VProperty.hpp" + +class QAction; +class QPainter; +class QTimer; + +class PropertyMapper; +class ServerHandler; +class ServerUpdateData; + + +class ServerRefreshInfoWidget : public QWidget, public ServerObserver, public ServerComObserver, + public VPropertyObserver +{ +Q_OBJECT + +public: + explicit ServerRefreshInfoWidget(QAction* refreshAction,QWidget* parent=0); + ~ServerRefreshInfoWidget(); + + void setServer(ServerHandler* server); + + void notifyChange(VProperty* p); + + void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler*); + void notifyEndServerScan(ServerHandler*); + void notifyServerActivityChanged(ServerHandler*); + + void notifyRefreshTimerStarted(ServerHandler* server); + void notifyRefreshTimerStopped(ServerHandler* server); + void notifyRefreshTimerChanged(ServerHandler* server); + void notifyRefreshScheduled(ServerHandler* server); + void notifyRefreshFinished(ServerHandler* server); + +protected Q_SLOTS: + void slotTimeOut(); + void slotTimeOutRefreshFinished(); + +Q_SIGNALS: + void serverSettingsEditRequested(ServerHandler*); + +protected: + void resizeEvent(QResizeEvent* event); + void mouseDoubleClickEvent(QMouseEvent* event); + void mousePressEvent(QMouseEvent* event); + void mouseMoveEvent(QMouseEvent* e); + void leaveEvent(QEvent*); + void paintEvent(QPaintEvent*); + + void updateSettings(); + void reloadAll(); + void fetchInfo(); + void drawButton(QPainter*); + void drawProgress(QPainter*); + + QString formatPeriodTime(int timeInSec) const; + void determinePeriodText(); + QString fullPeriodText() const; + int determinePeriodTextWidthMin() const; + bool periodTextWidthAboutToChange() const; + void adjustGeometry(bool); + void adjustTimer(int toNext); + void adjustToolTip(); + + QString formatTime(int timeInSec) const; + bool isInButton(const QPoint& pos) const; + bool isInText(const QPoint& pos) const; + void printStatus() const; + bool isActionEnabled() const; + + enum Component {ButtonComponent,TextComponent,ConfigComponent,NoComponent}; + enum Mode {NormalMode,ManualMode,NoMode}; + + QAction* refreshAction_; + ServerHandler* server_; + QString serverName_; + QString serverText_; + QTimer *timer_; + QTime inRefreshElapsed_; + + QFont fontServer_; + QFont fontPeriod_; + QFont fontLast_; + QFontMetrics fmServer_; + FontMetrics fmServerReal_; + QFontMetrics fmPeriod_; + QFontMetrics fmLast_; + + static QIcon *icon_; + static QPen borderPen_; + static QPen buttonBorderPen_; + static QPen disabledBorderPen_; + static QBrush serverBgBrush_; + static QBrush buttonBgBrush_; + static QBrush buttonBgHoverBrush_; + static QPen buttonHoverPen_; + static QBrush buttonBgRefreshBrush_; + static QPen buttonRefreshPen_; + static QBrush periodBgBrush_; + static QBrush progBrush_; + static QBrush progBgBrush_; + static QBrush lastBgBrush_; + static QPen serverTextPen_; + static QPen periodTextPen_; + static QPen driftTextPen_; + static QPen lastTextPen_; + static QPen disabledTextPen_; + + QRect buttonRect_; + int buttonRadius2_; + QString periodText_; + QString driftText_; + int periodTextWidth_; + int periodTextWidthMin_; + QString periodDummyText_; + QString periodDummyFullText_; + int lastTextWidth_; + QRect serverRect_; + QRect periodRect_; + QRect progRect_; + QRect lastRect_; + Component currentComponent_; + int progRectHeight_; + int serverRectHeight_; + int serverYPadding_; + + PropertyMapper* prop_; + + Mode mode_; + int noBlinkLimit_; + bool hasInfo_; + bool inRefresh_; + bool userInitiatedRefresh_; + bool showLastAutoRefresh_; + QString lastRefresh_; + QString nextRefresh_; + int total_; + int period_; + int toNext_; + int drift_; + bool needBorder_; + +}; + +class ServerComActivityLine : public QWidget + +{ +//Q_OBJECT + +public: + explicit ServerComActivityLine(QWidget* parent=0); + + void setServer(ServerHandler* server); + +protected: + void paintEvent(QPaintEvent*); + + QFont font_; + QFontMetrics fm_; + ServerHandler* server_; + QPixmap pix_; + QTimer *timer_; +}; + +#if 0 +class ServerRefreshInfoWidget : public QWidget +{ +public: + explicit ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent=0); + + void setServer(ServerHandler* server); + +protected: + QAction* refreshAction_; + ServerComLineDisplay* infoW_; +}; +#endif + + +#endif // SERVERCOMLINE_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComObserver.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComObserver.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComObserver.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComObserver.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERCOMOBSERVER_HPP +#define SERVERCOMOBSERVER_HPP + +class ServerHandler; + +class ServerComObserver +{ +public: + ServerComObserver() {} + virtual ~ServerComObserver() {} + + virtual void notifyRefreshTimerStarted(ServerHandler* server) {} + virtual void notifyRefreshTimerStopped(ServerHandler* server) {} + virtual void notifyRefreshTimerChanged(ServerHandler* server) {} + virtual void notifyRefreshScheduled(ServerHandler* server) {} + virtual void notifyRefreshFinished(ServerHandler* server) {} +}; + +#endif // SERVERCOMOBSERVER_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComQueue.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComQueue.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComQueue.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComQueue.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,625 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerComQueue.hpp" + +#include "ClientInvoker.hpp" +#include "ServerComThread.hpp" +#include "ServerHandler.hpp" +#include "UIDebug.hpp" +#include "UiLogS.hpp" + +#include "Log.hpp" + +#define _UI_SERVERCOMQUEUE_DEBUG + +// This class manages the tasks to be sent to the ServerComThread, which controls +// the communication with the ClientInvoker. The ClientInvoker is hidden from the +// ServerHandler. The ServerHandler defines a task and sends it to +// ServerComQueue, which then passes it on to the ClientInvoker. When a task is finished +// ServerComQueue notifies the ServerHandler about it. + +ServerComQueue::ServerComQueue(ServerHandler *server,ClientInvoker *client) : + QObject(server), + server_(server), + client_(client), + comThread_(0), + timeout_(5), + ctStartTimeout_(1000), + ctStartWaitTimeout_(500), //wait() is a blocking call, so it should be short + startTimeoutTryCnt_(0), + ctMaxStartTimeoutTryCnt_(4), + state_(NoState), //the queue is enabled but not running + taskStarted_(false), + taskIsBeingFinished_(false), + taskIsBeingFailed_(false) +{ + timer_=new QTimer(this); + timer_->setInterval(timeout_); + + connect(timer_,SIGNAL(timeout()), + this,SLOT(slotRun())); + + + createThread(); +} + +ServerComQueue::~ServerComQueue() +{ + state_=DisabledState; + + //Stop the timer + timer_->stop(); + + //Empty the tasks + tasks_.clear(); + + //Disconnects all the signals from the thread + comThread_->disconnect(0,this); + + //If the comthread is running we need to wait + //until it finishes its task. + if(comThread_->wait(5000)) + { + //Send a logout task + VTask_ptr task=VTask::create(VTask::LogOutTask); + comThread_->task(task); + + //Wait until the logout finishes + comThread_->wait(3000); + } + + delete comThread_; +} + +void ServerComQueue::createThread() +{ + if(comThread_) + delete comThread_; + + //We create a ServerComThread here. At this point the thread is not doing anything. + comThread_=new ServerComThread(server_,client_); + + //When the ServerComThread starts it emits a signal that + //is connected to the queue. + connect(comThread_, SIGNAL(started()), + this, SLOT(slotTaskStarted())); + + //When the ServerComThread finishes it emits a signal that + //is connected to the queue. + connect(comThread_, SIGNAL(finished()), + this, SLOT(slotTaskFinished())); + + //When there is an error in ServerComThread it emits the + //failed() signal that is connected to the queue. + connect(comThread_, SIGNAL(failed(std::string)), + this, SLOT(slotTaskFailed(std::string))); + + //The ServerComThread is observing the actual server and its nodes. When there is a change it + //emits a signal to notify the ServerHandler about it. + connect(comThread_,SIGNAL(nodeChanged(const Node*, std::vector)), + server_,SLOT(slotNodeChanged(const Node*, std::vector))); + + connect(comThread_,SIGNAL(defsChanged(std::vector)), + server_,SLOT(slotDefsChanged(std::vector))); + + connect(comThread_,SIGNAL(rescanNeed()), + server_,SLOT(slotRescanNeed())); +} + + +void ServerComQueue::enable() +{ + state_=NoState; + start(); +} + +void ServerComQueue::disable() +{ + if(state_ == DisabledState) + return; + + UiLogS(server_).dbg() << "ComQueue::disable -->"; + + state_=DisabledState; + + //Remove all tasks + tasks_.clear(); + + //Stop the timer + stopTimer(); + + //If the comthread is running we need to wait + //until it finishes its task. + comThread_->wait(); + + UiLogS(server_).dbg() << " queue is disabled"; + + //Clear the current task + if(current_) + current_.reset(); + + taskStarted_=false; +} + + +//This is a special mode to reload the whole ClientInvoker +bool ServerComQueue::prepareReset() +{ + if(state_ == DisabledState || state_ == ResetState || state_ == SuspendedState) + return false; + + //Stop the timer + stopTimer(); + + //Remove all tasks + tasks_.clear(); + + taskStarted_=false; + + state_=ResetState; + + //If the comthread is running we need to wait + //until it finishes its task. + if(comThread_->wait(5000)) + { + //The thread cannot be running + assert(comThread_->isRunning() == false); + //assert(taskIsBeingFinished_==false); + //assert(taskIsBeingFailed_==false); + //assert(!current_); + return true; + } + + return false; +} + +//This is a special mode to reload the whole ClientInvoker. Must be called after prepareReset returned true; +void ServerComQueue::reset() +{ + assert(state_ == ResetState); + + //The thread cannot be running + assert(comThread_->isRunning() == false); + + //We send a Reset command to the thread!! This is the only task that is allowed + //during the reset!! + VTask_ptr task=VTask::create(VTask::ResetTask); + tasks_.push_back(task); + + //TODO: why do we not run it directly + //We start the timer with a shorter interval + timer_->start(100); +} + +void ServerComQueue::endReset() +{ + if(state_ == ResetState) + { + state_=SuspendedState; + start(); + } +} + + +//When the queue is started: +// -it is ready to accept tasks +// -its timer is running +void ServerComQueue::start() +{ + if(state_ != DisabledState && state_ != ResetState) + { + UiLogS(server_).dbg() << "ComQueue::start -->"; + + //assert(taskIsBeingFinished_==false); + //assert(taskIsBeingFailed_==false); + //assert(!current_); + + startTimeoutTryCnt_=0; + taskStarted_=false; + + //If the comthread is running we need to wait + //until it finishes its task. + comThread_->wait(); + + state_=RunningState; + + UiLogS(server_).dbg() << " thread finished"; + + //Starts the timer + timer_->start(timeout_); + + UiLogS(server_).dbg() << " timer started"; + UiLogS(server_).dbg() << "<-- ComQueue::start"; + } +} + +//The queue contents remains the same but the timer is stopped. Until start() is +//called nothing will be submitted to the queue. +void ServerComQueue::suspend(bool wait) +{ + if(state_ != DisabledState && state_ != ResetState && + state_ != SuspendedState) + { + state_=SuspendedState; + stopTimer(); + if(wait) + { + comThread_->wait(); + } + + //assert(taskIsBeingFinished_==false); + //assert(taskIsBeingFailed_==false); + //assert(!current_); + } +} + +void ServerComQueue::stopTimer() +{ + timer_->stop(); + startTimeoutTryCnt_=0; +} + +bool ServerComQueue::hasTask(VTask::Type t) const +{ + for(std::deque::const_iterator it=tasks_.begin(); it != tasks_.end(); ++it) + { + if(*it && (*it)->type() == t && (*it)->status() != VTask::CANCELLED && + (*it)->status() != VTask::ABORTED ) + return true; + + } + return false; +} + +bool ServerComQueue::isNextTask(VTask::Type t) const +{ + return (!tasks_.empty() && tasks_.back()->type() == t); +} + + +void ServerComQueue::addTask(VTask_ptr task) +{ + if(!task) + return; + + if(isNextTask(VTask::ZombieListTask) && tasks_.back()->type() == task->type()) + return; + + if(state_ == DisabledState || state_ == ResetState || + (task && task->type() ==VTask::ResetTask) ) + return; + + tasks_.push_back(task); + if(state_ != SuspendedState) + { + if(!timer_->isActive()) + { + //we immediately execute the "current" task + slotRun(); + //and only start the timer after it + timer_->start(timeout_); + } + } +} + +void ServerComQueue::addNewsTask() +{ + if(state_ == DisabledState || state_ == ResetState) + return; + + if(isNextTask(VTask::NewsTask)) + return; + + VTask_ptr task=VTask::create(VTask::NewsTask); + addTask(task); + server_->refreshScheduled(); +} + +void ServerComQueue::addSyncTask() +{ + if(state_ == DisabledState || state_ == ResetState) + return; + + if(isNextTask(VTask::SyncTask)) + return; + + VTask_ptr task=VTask::create(VTask::SyncTask); + addTask(task); +} + +void ServerComQueue::addSuiteListTask() +{ + if(state_ == DisabledState || state_ == ResetState) + return; + + if(isNextTask(VTask::SuiteListTask)) + return; + + VTask_ptr task=VTask::create(VTask::SuiteListTask); + addTask(task); +} + +void ServerComQueue::addSuiteAutoRegisterTask() +{ + if(state_ == DisabledState || state_ == ResetState) + return; + + if(isNextTask(VTask::SuiteAutoRegisterTask)) + return; + + VTask_ptr task=VTask::create(VTask::SuiteAutoRegisterTask); + addTask(task); +} + +void ServerComQueue::startCurrentTask() +{ + taskStarted_=false; + ctStartTime_.start(); + comThread_->task(current_); +} + +void ServerComQueue::slotRun() +{ + if(state_ == DisabledState ||state_ == SuspendedState ) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UI_FUNCTION_LOG_S(server_); + UiLogS(server_).dbg() << " queue is either disabled or suspended"; +#endif + return; + } + + if(taskIsBeingFinished_ || taskIsBeingFailed_) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UI_FUNCTION_LOG_S(server_); + UiLogS(server_).dbg() << " task is either being finished or failed"; +#endif + return; + } + + if(tasks_.empty() && !current_) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UI_FUNCTION_LOG_S(server_); + UiLogS(server_).dbg() << " there are no tasks! Stop timer!"; +#endif + timer_->stop(); + return; + } + +#if 0 +#ifdef _UI_SERVERCOMQUEUE_DEBUG + if(tasks_.size() > 0) + { + UI_FUNCTION_LOG_S(server_); + UiLog(server_).dbg() << " number of tasks: " << tasks_.size(); + for(std::deque::const_iterator it=tasks_.begin(); it != tasks_.end(); it++) + { + UiLog(server_).dbg() << " task: " << (*it)->typeString(); + } + } +#endif +#endif + + //If a task was sent to the thread but the queue did not get the + //notification about the thread's start there is a PROBLEM! + //Note: when the thread starts it emits a signal to the queue + //and the queue sets taskStarted_ to true. Since this is a queued communication + //there can be a time lag between the two events. We set a timeout for it! + //If we pass the timeout we stop the thread and try to resend the task! + if(current_ && !taskStarted_ && ctStartTime_.elapsed() > ctStartTimeout_) + { + UI_FUNCTION_LOG_S(server_); + if(startTimeoutTryCnt_ < ctMaxStartTimeoutTryCnt_) + { + UiLogS(server_).warn() << " ServerCom thread does not seem to have started within the allocated timeout. \ + startTimeoutTryCnt_=" << startTimeoutTryCnt_; + + startTimeoutTryCnt_++; + } + else + { + startTimeoutTryCnt_=0; + + //So if we are here: + //-the thread did not emit a notification about its start + //-the elapsed time since we sent the task to the thread past the timeout. + //-since the timeout was passed slotRun() has been was called at least startTimeoutTryCnt_ times + + bool running=comThread_->isRunning(); + + //Problem 1: + //-the thread is running + if(running) + { + UiLogS(server_).warn() << " It seems that the ServerCom thread started but it is in a bad state. Try to run task again."; + } + + //Problem 2: + //-the thread is not running + else + { + UiLogS(server_).warn() << " It seems that the ServerCom thread could not start. Try to run task again."; + } + + if(comThread_->wait(ctStartWaitTimeout_) == false) + { + UiLogS(server_).warn() << " Calling wait() on the ServerCom thread failed."; + + //The thread started to run in the meantime. We check its status again at the next slotRun call. + if(!running && comThread_->isRunning()) + { + UiLogS(server_).warn() << " It seems that in the meantime the thread started to run.\ + We will check its state again"; + return; + } + + //Otherwise there is nothing to do!! + //We exit here because when we tried to call terminate() it just hung!! + UI_ASSERT(0,"Cannot stop ServerCom thread, which is in a bad state"); + exit(1); +#if 0 + comThread_->terminate(); + if(comThread_->wait(taskTimeout_) == false) + { + UiLog(server_).err() << " Calling wait() after terminate() on the ServerCom thread failed"; + UiLog(server_).dbg() << "Delete the current ComThread and create a new one."; + createThread(); + UI_ASSERT(0,"Cannot stop ServerCom thread that is in a bad state"); + + } + else + { + UiLog(server_).dbg() << " Terminating ServerCom thread succeeded."; + } +#endif + } + + UiLogS(server_).warn() << " Calling wait() on the ServerCom thread succeeded."; + + Q_ASSERT(comThread_->isRunning() == false); + + if(current_->status() != VTask::CANCELLED && + current_->status() != VTask::ABORTED ) + { + startCurrentTask(); + return; + } + else + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UiLogS(server_).dbg() << " current_ aborted or cancelled. Reset current_ !"; +#endif + current_.reset(); + } + } + } + + if(current_) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + //UiLog(server_).dbg() << " still processing reply from previous task"; +#endif + return; + } + + if(comThread_->isRunning()) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + //UiLog(server_).dbg() << " thread is active"; +#endif + return; + } + + //We search for the first non-cancelled/aborted task + while(!tasks_.empty()) + { + current_=tasks_.front(); + tasks_.pop_front(); + if(current_->status() != VTask::CANCELLED && + current_->status() != VTask::ABORTED ) + { + break; + } + current_.reset(); + } + + if(!current_) + { + timer_->stop(); + return; + } + +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UiLogS(server_).dbg() << " run task: " << current_->typeString(); +#endif + + //Send it to the thread + startCurrentTask(); +} + +//This slot is called when ComThread finishes its task. At this point the +//thread is not running so it is safe to access the ClientInvoker! +void ServerComQueue::slotTaskStarted() +{ + taskStarted_=true; +} + +//This slot is called when ComThread finishes its task. At this point the +//thread is not running so it is safe to access the ClientInvoker! +void ServerComQueue::slotTaskFinished() +{ + taskStarted_=false; + taskIsBeingFinished_=true; + startTimeoutTryCnt_=0; + + UiLogS(server_).dbg() << "ComQueue::slotTaskFinished -->"; + + //We need to leave the load mode + endReset(); + + //If the current task is empty there must have been an error that was + //handled by the sloTaskFailed slot. + if(current_) + { +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UiLogS(server_).dbg() << " reset current_"; +#endif + + VTask_ptr task=current_; + current_.reset(); + + //We notify the server that the task has finished and the results can be accessed. + server_->clientTaskFinished(task,client_->server_reply()); + } + + taskIsBeingFinished_=false; +} + +//This slot is called when the task failed in the ComThread. Right after this signal is emitted +//the thread will finish and and emits the finished() signal that is connected +//to the slotTaskFinished slot. +void ServerComQueue::slotTaskFailed(std::string msg) +{ + taskStarted_=false; + taskIsBeingFailed_=true; + startTimeoutTryCnt_=0; + + UiLogS(server_).dbg() << "ComQueue::slotTaskFailed -->"; +#ifdef _UI_SERVERCOMQUEUE_DEBUG + if(current_) + UiLogS(server_).dbg() << " current_ exists"; + else + UiLogS(server_).dbg() << " current_ is null"; +#endif + + //We need to leave the load mode + endReset(); + +#ifdef _UI_SERVERCOMQUEUE_DEBUG + if(current_) + UiLogS(server_).dbg() << " current_ exists"; + else + UiLogS(server_).dbg() << " current_ is null"; +#endif + +#ifdef _UI_SERVERCOMQUEUE_DEBUG + UiLogS(server_).dbg() << " reset current_"; +#endif + assert(current_); + VTask_ptr task=current_; + current_.reset(); + + //We notify the server that the task has failed + server_->clientTaskFailed(task,msg); + + taskIsBeingFailed_=false; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComQueue.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComQueue.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComQueue.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComQueue.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,89 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERCOMQUEUE_HPP_ +#define SERVERCOMQUEUE_HPP_ + +#include +#include + +#include "VTask.hpp" + +#include +#include +#include + +class ClientInvoker; +class NodeObserver; +class ServerHandler; +class ServerComThread; + +// -------------------------------------------------------------- +// ServerComQueue - a class to provide a queueing system for +// sending tasks to the ClientIvoker via the ServerComThread. +// -------------------------------------------------------------- + +class ServerComQueue : public QObject +{ +Q_OBJECT + +public: + ServerComQueue(ServerHandler *server,ClientInvoker* client); + ~ServerComQueue(); + + enum State {NoState,RunningState,SuspendedState,ResetState,DisabledState}; + State state() const {return state_;} + + void addTask(VTask_ptr); + void addNewsTask(); + void addSyncTask(); + void addSuiteListTask(); + void addSuiteAutoRegisterTask(); + + void enable(); + void disable(); + void start(); + void suspend(bool); + bool prepareReset(); + void reset(); + bool isSuspended() const {return state_==SuspendedState;} + +protected Q_SLOTS: + void slotRun(); + void slotTaskStarted(); + void slotTaskFinished(); + void slotTaskFailed(std::string); + +protected: + void createThread(); + void stopTimer(); + void startCurrentTask(); + void endReset(); + bool hasTask(VTask::Type t) const; + bool isNextTask(VTask::Type t) const; + + ServerHandler *server_; + ClientInvoker* client_; + ServerComThread *comThread_; + QTimer* timer_; + int timeout_; + QTime ctStartTime_; + int ctStartTimeout_; + int ctStartWaitTimeout_; + int startTimeoutTryCnt_; + int ctMaxStartTimeoutTryCnt_; + std::deque tasks_; + VTask_ptr current_; + State state_; + bool taskStarted_; + bool taskIsBeingFinished_; + bool taskIsBeingFailed_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComThread.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComThread.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComThread.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComThread.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,569 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerComThread.hpp" + +#include "Defs.hpp" +#include "ClientInvoker.hpp" +#include "ArgvCreator.hpp" + +#include "ServerDefsAccess.hpp" +#include "ServerComQueue.hpp" +#include "ServerHandler.hpp" +#include "SuiteFilter.hpp" +#include "UiLog.hpp" + +#include + +#define _UI_SERVERCOMTHREAD_DEBUG + +ServerComThread::ServerComThread(ServerHandler *server, ClientInvoker *ci) : + server_(server), + ci_(ci), + taskType_(VTask::NoTask), + rescanNeed_(false), + hasSuiteFilter_(false), + autoAddNewSuites_(false), + maxLineNum_(-1) +{ + assert(server_); +} + +ServerComThread::~ServerComThread() +{ + detach(); +} + +void ServerComThread::task(VTask_ptr task) +{ + //do not execute thread if already running + + if(isRunning()) + { + UiLog(serverName_).err() << "ComThread::task - thread already running, will not execute command"; + } + else + { + //if(!server_ && server) + // initObserver(server); + + //We set the parameters needed to run the task. These members are not protected by + //a mutex, because apart from this function only run() can access them!! + serverName_=server_->longName(); + command_=task->command(); + params_=task->params(); + contents_=task->contents(); + vars_=task->vars(); + nodePath_.clear(); + taskType_=task->type(); + nodePath_=task->targetPath(); + zombie_=task->zombie(); + + //Suite filter + hasSuiteFilter_=server_->suiteFilter()->isEnabled(); + autoAddNewSuites_=server_->suiteFilter()->autoAddNewSuites(); + if(hasSuiteFilter_) + filteredSuites_=server_->suiteFilter()->filter(); + else + filteredSuites_.clear(); + + maxLineNum_=server_->conf()->intValue(VServerSettings::MaxOutputFileLines); + + //Start the thread execution + start(); + } +} + +void ServerComThread::run() +{ + UiLog(serverName_).dbg() << "ComThread::run --> path=" << nodePath_; + + //Init flags + rescanNeed_=false; + bool isMessage = false; + std::string errorString; + + try + { + switch (taskType_) + { + case VTask::CommandTask: + { + // call the client invoker with the saved command + UiLog(serverName_).dbg() << " COMMAND"; + ArgvCreator argvCreator(command_); +#ifdef _UI_SERVERCOMTHREAD_DEBUG + UiLog(serverName_).dbg() << " args=" << argvCreator.toString(); +#endif + ci_->invoke(argvCreator.argc(), argvCreator.argv()); + + /*ci_->news_local(); + switch (ci_->server_reply().get_news()) + { + case ServerReply::NO_NEWS: + case ServerReply::NEWS: + ci_->sync_local(); + break; + case ServerReply::DO_FULL_SYNC: + + break; + }*/ + break; + } + + case VTask::NewsTask: + { + UiLog(serverName_).dbg() << " NEWS"; + ci_->news_local(); // call the server + break; + } + + case VTask::SyncTask: + { + UiLog(serverName_).dbg() << " SYNC"; + sync_local(); + break; + } + + //This is called during reset + case VTask::ResetTask: + { + UiLog(serverName_).dbg() << " SYNC"; + reset(); + break; + } + + case VTask::JobTask: + case VTask::ManualTask: + case VTask::ScriptTask: + case VTask::OutputTask: + { + UiLog(serverName_).dbg() << " FILE" << " " << params_["clientPar"]; + if(maxLineNum_ < 0) + ci_->file(nodePath_,params_["clientPar"]); + else + ci_->file(nodePath_,params_["clientPar"],boost::lexical_cast(maxLineNum_)); + + break; + } + + case VTask::MessageTask: + { + UiLog(serverName_).dbg() << " EDIT HISTORY"; + ci_->edit_history(nodePath_); + break; + } + + case VTask::StatsTask: + { + UiLog(serverName_).dbg() << " STATS"; + ci_->stats(); + break; + } + + case VTask::HistoryTask: + { + UiLog(serverName_).dbg() << " SERVER LOG"; + ci_->getLog(100); + break; + } + + case VTask::ScriptPreprocTask: + UiLog(serverName_).dbg() << " SCRIP PREPROCESS"; + ci_->edit_script_preprocess(nodePath_); + break; + + case VTask::ScriptEditTask: + UiLog(serverName_).dbg() << " SCRIP EDIT"; + ci_->edit_script_edit(nodePath_); + break; + + case VTask::ScriptSubmitTask: + UiLog(serverName_).dbg() << " SCRIP SUBMIT"; + ci_->edit_script_submit(nodePath_, vars_, contents_, + (params_["alias"]=="1")?true:false, + (params_["run"] == "1")?true:false); + break; + + case VTask::SuiteListTask: + UiLog(serverName_).dbg() << " SUITES"; + ci_->suites(); + break; + + case VTask::SuiteAutoRegisterTask: + UiLog(serverName_).dbg() << " SUITE AUTO REGISTER"; + if(hasSuiteFilter_) + { + ci_->ch1_auto_add(autoAddNewSuites_); + } + break; + + case VTask::ZombieCommandTask: + { + std::string cmd=params_["command"]; + UiLog(serverName_).dbg() << " ZOMBIE COMMAND " << cmd << " path=" << zombie_.path_to_task(); + if(cmd == "zombie_fob") + ci_->zombieFob(zombie_); + else if(cmd == "zombie_fail") + ci_->zombieFail(zombie_); + else if(cmd == "zombie_adopt") + ci_->zombieAdopt(zombie_); + else if(cmd == "zombie_block") + ci_->zombieBlock(zombie_); + else if(cmd == "zombie_remove") + ci_->zombieRemove(zombie_); + else if(cmd == "zombie_kill") + ci_->zombieKill(zombie_); + + break; + } + + case VTask::ZombieListTask: + UiLog(serverName_).dbg() << " ZOMBIES"; + ci_->zombieGet(); + break; + + case VTask::LogOutTask: + UiLog(serverName_).dbg() << " LOGOUT"; + detach(); + if(ci_->client_handle() > 0) + { + ci_->ch1_drop(); + } + break; + default: + break; + } + } + catch(std::exception& e) + { + isMessage = true; + errorString = e.what(); + } + + // we can get an error string in one of two ways - either an exception is raised, or + // the get_string() of the server reply is non-empty. + if (!isMessage && (taskType_ == VTask::CommandTask) && !(ci_->server_reply().get_string().empty())) + { + isMessage = true; + errorString = ci_->server_reply().get_string(); + } + + if (isMessage) + { + // note that we need to emit a signal rather than directly call a message function + // because we can't call Qt widgets from a worker thread + + UiLog(serverName_).dbg() << " thread failed: " << errorString; + Q_EMIT failed(errorString); + + //Reset flags + rescanNeed_=false; + + //This will stop the thread. + return; + } + + //Reset flags + rescanNeed_=false; + + //Can we use it? We are in the thread!!! + //UserMessage::message(UserMessage::DBG, false, std::string(" ServerComThread::run finished")); +} + + +void ServerComThread::sync_local() +{ + //For this part we need to lock the mutex on defs + { + ServerDefsAccess defsAccess(server_); + + UiLog(serverName_).dbg() << "ComThread::sync_local --> sync begin"; + ci_->sync_local(); + UiLog(serverName_).dbg() << " sync end"; + + //If a rescan or fullscan is needed we have either added/remove nodes or deleted the defs. + //So there were significant changes. + + //We detach the nodes currently available in defs, then we attach them again. We can still have nodes + //that were removed from the defs but are still attached. These will be detached when in ServerHandler we + //clear the tree. This tree contains shared pointers to the nodes, so when the tree is cleared + //the shared pointer are reset, the node descturctor is called and finally update_delete is called and + //we can detach the node. + + if(rescanNeed_ || ci_->server_reply().full_sync()) + { + UiLog(serverName_).dbg() << " rescan needed!"; + detach(defsAccess.defs()); + attach(defsAccess.defs()); + } + } +} + +void ServerComThread::reset() +{ + UiLog(serverName_).dbg() << "ComThread::reset -->"; + + //Lock the mutex on defs + ServerDefsAccess defsAccess(server_); + + //Detach the defs and the nodes from the observer + detach(defsAccess.defs()); + + //We drop the handle belonging to the current client! + if(ci_->client_handle() > 0) + { + try + { + ci_->ch1_drop(); + } + catch (std::exception &e) + { + UiLog(serverName_).warn() << " cannot drop handle for client: " << e.what(); + } + } + + if(hasSuiteFilter_) + { + //reset client handle + defs + ci_->reset(); + + if(!filteredSuites_.empty()) + { + UiLog(serverName_).dbg() << " register suites=" << filteredSuites_; + + //This will add a new handle to the client + ci_->ch_register(autoAddNewSuites_, filteredSuites_); + } + //If the suite filter is empty + else + { + //Registering with empty set would lead to retrieve all server content, + //opposite of expected result. So we just register a dummy suite + //to achive the our goal: for an empty suite filter no suites are retrieved. + UiLog(serverName_).dbg() << " register empty suite list"; + + std::vector fsl; + fsl.push_back(SuiteFilter::dummySuite()); + ci_->ch_register(autoAddNewSuites_, fsl); + } + } + else + { + // reset client handle + defs + ci_->reset(); + } + + UiLog(serverName_).dbg() << " sync begin"; + ci_->sync_local(); + UiLog(serverName_).dbg() << " sync end"; + + //Attach the nodes to the observer + attach(defsAccess.defs()); + + UiLog(serverName_).dbg() << "<-- ComThread::reset"; +} + +//This is an observer notification method!! +void ServerComThread::update(const Node* node, const std::vector& types) +{ + //This function can only be called during a SYNC_LOCAl task!!!! + assert(taskType_ == VTask::SyncTask); + + std::vector typesCopy=types; + + UiLog(serverName_).dbg() << "ComThread::update --> node: " << node->name(); + std::stringstream ss; + aspectToStr(ss,types); + UiLog(serverName_).dbg() << " aspects: " << ss.str(); + + //If a node was already requested to be added/deleted in the thread we do not go further. At the end of the sync + //we will regenerate everything (the tree as well in ServerHandle). + if(rescanNeed_) + { + UiLog(serverName_).dbg() << " rescanNeed already set"; + return; + } + + //This is a radical change + if((std::find(types.begin(),types.end(),ecf::Aspect::ADD_REMOVE_NODE) != types.end()) || + (std::find(types.begin(),types.end(),ecf::Aspect::ORDER) != types.end())) + { + UiLog(serverName_).dbg() << " emit rescanNeed()"; + rescanNeed_=true; + + //We notify ServerHandler about the radical changes. When ServerHandler receives this signal + //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are + //reset update_delete() might be called, so it should not write any shared variables! + Q_EMIT rescanNeed(); + + return; + } + + //This will notify SeverHandler + UiLog(serverName_).dbg() << " emit nodeChanged()"; + Q_EMIT nodeChanged(node,types); +} + + +void ServerComThread::update(const Defs* dc, const std::vector& types) +{ + std::vector typesCopy=types; + + UiLog(serverName_).dbg() << "ComThread::update --> defs"; + std::stringstream ss; + aspectToStr(ss,types); + UiLog(serverName_).dbg() << " aspects: " << ss.str(); + + //If anything was requested to be deleted in the thread we do not go further + //because it will trigger a full rescan in ServerHandler! + if(rescanNeed_) + { + UiLog(serverName_).dbg() << " rescanNeed already set"; + return; + } + + //This is a radical change + if(std::find(types.begin(),types.end(),ecf::Aspect::ORDER) != types.end()) + { + UiLog(serverName_).dbg() << " emit rescanNeed()"; + rescanNeed_=true; + + //We notify ServerHandler about the radical changes. When ServerHandler receives this signal + //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are + //reset update_delete() might be called, so it should not write any shared variables! + Q_EMIT rescanNeed(); + + return; + } + + //This will notify SeverHandler + UiLog(serverName_).dbg() << " emit defsChanged()"; + Q_EMIT defsChanged(typesCopy); +} + +void ServerComThread::update_delete(const Node* nc) +{ + Node *n=const_cast(nc); + n->detach(this); +} + +//This only be called when ComThread is running or from the ComThread desctructor. So it is safe to set +//rescanNeed in it. +void ServerComThread::update_delete(const Defs* dc) +{ + UiLog(serverName_).dbg() << "ServerComThread::update_delete -->"; + + Defs *d=const_cast(dc); + d->detach(this); + + //If we are in a SYNC_LOCAl task!!!! + if(taskType_ == VTask::SyncTask) + { + //We notify ServerHandler about the radical changes. When ServerHandler receives this signal + //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are + //reset update_delete() might be called, so it should not write any shared variables! + Q_EMIT rescanNeed(); + + rescanNeed_=true; + } +} + +//Attach each node and the defs to the observer +void ServerComThread::attach() +{ + ServerDefsAccess defsAccess(server_); // will reliquish its resources on destruction + defs_ptr d = defsAccess.defs(); + if(d == NULL) + return; + + attach(d); +} + +//Attach each node and the defs to the observer. The access to +//the defs is safe so we do not need to set a mutex on it. +void ServerComThread::attach(defs_ptr d) +{ + if(d == NULL) + return; + + d->attach(this); + + const std::vector &suites = d->suiteVec(); + for(unsigned int i=0; i < suites.size();i++) + { + attach(suites.at(i).get()); + } +} + +//Attach a node to the observer +void ServerComThread::attach(Node *node) +{ + std::vector nodes; + node->immediateChildren(nodes); + + node->attach(this); + + for(std::vector::const_iterator it=nodes.begin(); it != nodes.end(); ++it) + { + attach((*it).get()); + } +} + +//Detach each node and the defs from the observer +void ServerComThread::detach() +{ + ServerDefsAccess defsAccess(server_); // will reliquish its resources on destruction + defs_ptr d = defsAccess.defs(); + if(d == NULL) + return; + + detach(d); +} + + +//Detach each node and the defs from the observer. The access to +//the defs is safe so we do not need to set a mutex on it. +void ServerComThread::detach(defs_ptr d) +{ + if(d == NULL) + return; + + d->detach(this); + + const std::vector &suites = d->suiteVec(); + for(unsigned int i=0; i < suites.size();i++) + { + detach(suites.at(i).get()); + } +} + +//Detach a node from the observer +void ServerComThread::detach(Node *node) +{ + std::vector nodes; + node->immediateChildren(nodes); + + node->detach(this); + + for(std::vector::const_iterator it=nodes.begin(); it != nodes.end(); ++it) + { + detach((*it).get()); + } +} + +void ServerComThread::aspectToStr(std::stringstream& ss,const std::vector& t) const +{ + for(std::vector::const_iterator it=t.begin(); it != t.end(); ++it) + { + if(!ss.str().empty()) + ss << ","; + ss << *it; + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerComThread.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerComThread.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerComThread.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerComThread.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,93 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERCOMTHREAD_HPP_ +#define SERVERCOMTHREAD_HPP_ + +#include +#include +#include +#include +#include + +#include "Defs.hpp" +#include "AbstractObserver.hpp" + +#include "VTask.hpp" + +#include + +class ChangeMgrSingleton; +class ClientInvoker; +class ServerComQueue; +class ServerHandler; + +// ------------------------------------------------------- +// ServerComThread - a class to handler communication with +// an ecflow server. +// ------------------------------------------------------- + +class ServerComThread : public QThread, public AbstractObserver +{ + Q_OBJECT + +public: + ServerComThread(ServerHandler *server, ClientInvoker *ci); + ~ServerComThread(); + + void task(VTask_ptr); + + //From AbstractObserver + virtual void update_start(const Node*, const std::vector&) {} + virtual void update_start(const Defs*, const std::vector&) {} + void update(const Node*, const std::vector&); + void update(const Defs*, const std::vector&); + void update_delete(const Node*); + void update_delete(const Defs*); + +Q_SIGNALS: + void nodeChanged(const Node*, std::vector); + void defsChanged(std::vector); + void rescanNeed(); + void failed(std::string message); + void suiteListChanged(const std::vector&,const std::vector&); + +protected: + void run(); + void reset(); + void sync_local(); + +private: + void attach(); + void attach(defs_ptr d); + void attach(Node *node); + void detach(); + void detach(defs_ptr d); + void detach(Node *node); + void aspectToStr(std::stringstream& s,const std::vector& t) const; + + ServerHandler *server_; + std::string serverName_; + ClientInvoker *ci_; + VTask::Type taskType_; + std::vector command_; + std::map params_; + std::vector contents_; + NameValueVec vars_; + Zombie zombie_; + std::string nodePath_; + bool rescanNeed_; + bool hasSuiteFilter_; + std::vector filteredSuites_; + bool autoAddNewSuites_; + int maxLineNum_; + bool initialResetDone_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerDefsAccess.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerDefsAccess.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerDefsAccess.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerDefsAccess.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerDefsAccess.hpp" + +#include "ServerHandler.hpp" + +ServerDefsAccess::ServerDefsAccess(ServerHandler *server) : + server_(server) +{ + server_->defsMutex_.lock(); // lock the resource on construction +} + + +ServerDefsAccess::~ServerDefsAccess() +{ + server_->defsMutex_.unlock(); // unlock the resource on destruction +} + +defs_ptr ServerDefsAccess::defs() +{ + return server_->defs(); // the resource will always be locked when we use it +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerDefsAccess.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerDefsAccess.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerDefsAccess.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerDefsAccess.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,34 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERDEFSACCESS_HPP_ +#define SERVERDEFSACCESS_HPP_ + +#include "Defs.hpp" + +class ServerHandler; + +// ------------------------------------------------------------------------- +// ServerDefsAccess - a class to manage access to the server definition tree +// - required for multi-threaded access +// ------------------------------------------------------------------------- + +class ServerDefsAccess +{ +public: + explicit ServerDefsAccess(ServerHandler *server); + ~ServerDefsAccess(); + + defs_ptr defs(); + +private: + ServerHandler *server_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerEditDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerEditDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerEditDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerEditDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,130 @@ + + + ServerEditDialog + + + + 0 + 0 + 284 + 228 + + + + Edit server properties + + + true + + + + + + QFormLayout::ExpandingFieldsGrow + + + + + &Name: + + + label + + + + + + + + + + &Host: + + + hostEdit + + + + + + + + + + &Port: + + + portEdit + + + + + + + + + + &Favourite: + + + favCh + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + ServerEditDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox + rejected() + ServerEditDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerFilter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerFilter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerFilter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,216 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerFilter.hpp" + +#include "ServerHandler.hpp" +#include "ServerItem.hpp" +#include "ServerList.hpp" +#include "VSettings.hpp" +#include "SessionHandler.hpp" + +//============================================== +// +// ServerFilter +// +//============================================== + +ServerFilter::ServerFilter() +{ +} + +ServerFilter::~ServerFilter() +{ + std::vector obsCopy=observers_; + for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) + { + (*it)->notifyServerFilterDelete(); + } + + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + (*it)->removeObserver(this); + } +} + +void ServerFilter::serverNames(std::vector& vec) const +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + vec.push_back((*it)->name()); + } +} + +void ServerFilter::addServer(ServerItem *item,bool broadcast) +{ + if(item && ServerList::instance()->find(item->name()) == item) + { + //ServerFilterItem* s=new ServerFilterItem(item->name(),item->host(),item->port()); + //ServerItem* s=new ServerFilterItem(item->name()); + + items_.push_back(item); + + item->addObserver(this); + + if(broadcast) + broadcastAdd(item); + } +} + +void ServerFilter::removeServer(ServerItem *server) +{ + if(!server) return; + + std::vector::iterator it=std::find(items_.begin(),items_.end(),server); + if(it != items_.end()) + { + //Remove the item from the filter. This should come + //first because the observers update themselves according to the + //contents of items_!!!! + items_.erase(it); + + //Notifies the view about the changes + broadcastRemove(server); + + //Remove the filter from the observers + server->removeObserver(this); + } +} + +void ServerFilter::notifyServerItemChanged(ServerItem *server) +{ + if(isFiltered(server)) + broadcastChange(server); +} + +//Do not remove the observer in this method!! +void ServerFilter::notifyServerItemDeletion(ServerItem *server) +{ + if(!server) return; + + std::vector::iterator it=std::find(items_.begin(),items_.end(),server); + if(it != items_.end()) + { + items_.erase(it); + + //Notifies the view about the changes + broadcastRemove(server); + } +} + +bool ServerFilter::isFiltered(ServerItem* item) const +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it) == item) + return true; + } + return false; +} + +bool ServerFilter::isFiltered(ServerHandler* server) const +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->serverHandler() == server) + return true; + } + return false; +} + +void ServerFilter::writeSettings(VSettings* vs) const +{ + std::vector array; + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + array.push_back((*it)->name()); + } + + vs->put("server",array); +} + +void ServerFilter::readSettings(VSettings* vs) +{ + items_.clear(); + + std::vector array; + vs->get("server",array); + + for(std::vector::const_iterator it = array.begin(); it != array.end(); ++it) + { + std::string name=*it; + if(ServerItem* s=ServerList::instance()->find(name)) + { + addServer(s,true); + } + // special case - if we're starting a temporary session for looking at one + // particular server, then we need to replace the placeholder server alias + // with the one we actually want to look at + else if (name == "SERVER_ALIAS_PLACEHOLDER") + { + SessionItem *session = SessionHandler::instance()->current(); + if (session->temporary()) + { + std::string alias = session->temporaryServerAlias(); + if (ServerItem* s=ServerList::instance()->find(alias)) + { + addServer(s,true); + } + } + } + } +} + +//=========================================================== +// Observers +//=========================================================== + +void ServerFilter::broadcastAdd(ServerItem *server) +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + { + (*it)->notifyServerFilterAdded(server); + } +} + +void ServerFilter::broadcastRemove(ServerItem *server) +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + { + (*it)->notifyServerFilterRemoved(server); + } +} + +void ServerFilter::broadcastChange(ServerItem *server) +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + { + (*it)->notifyServerFilterChanged(server); + } +} + +void ServerFilter::addObserver(ServerFilterObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + { + observers_.push_back(o); + } +} + +void ServerFilter::removeObserver(ServerFilterObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + { + observers_.erase(it); + } +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerFilter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerFilter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerFilter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerFilter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,69 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERFILTER_HPP_ +#define SERVERFILTER_HPP_ + +#include + +#include "Node.hpp" +#include "ServerItem.hpp" + +class VSettings; + +#include + +class ServerFilterObserver +{ +public: + virtual ~ServerFilterObserver() {} + virtual void notifyServerFilterAdded(ServerItem*)=0; + virtual void notifyServerFilterRemoved(ServerItem*)=0; + virtual void notifyServerFilterChanged(ServerItem*)=0; + virtual void notifyServerFilterDelete()=0; +}; + +class ServerFilter : public ServerItemObserver +{ +public: + ServerFilter(); + ~ServerFilter(); + + enum ChangeAspect {Reset,Added,Removed}; + + const std::vector& items() const {return items_;} + int itemCount() const {return static_cast(items_.size());} + void serverNames(std::vector&) const; + + void addServer(ServerItem*,bool broadcast=true); + void removeServer(ServerItem*); + bool isFiltered(ServerItem*) const; + bool isFiltered(ServerHandler*) const; + + void writeSettings(VSettings*) const; + void readSettings(VSettings*); + + void addObserver(ServerFilterObserver*); + void removeObserver(ServerFilterObserver*); + + //From ServerItemObserver + void notifyServerItemChanged(ServerItem*); + void notifyServerItemDeletion(ServerItem*); + +protected: + void broadcastAdd(ServerItem*); + void broadcastRemove(ServerItem*); + void broadcastChange(ServerItem*); + +private: + std::vector items_; + std::vector observers_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1645 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerHandler.hpp" + +#include "Defs.hpp" +#include "ClientInvoker.hpp" +#include "File.hpp" +#include "NodeFwd.hpp" +#include "ArgvCreator.hpp" +#include "Str.hpp" + +#include "ChangeNotify.hpp" +#include "ConnectState.hpp" +#include "DirectoryHandler.hpp" +#include "MainWindow.hpp" +#include "NodeObserver.hpp" +#include "SessionHandler.hpp" +#include "ServerComQueue.hpp" +#include "ServerDefsAccess.hpp" +#include "ServerObserver.hpp" +#include "ServerComObserver.hpp" +#include "ShellCommand.hpp" +#include "SuiteFilter.hpp" +#include "UiLogS.hpp" +#include "UIDebug.hpp" +#include "UpdateTimer.hpp" +#include "UserMessage.hpp" +#include "VNode.hpp" +#include "VSettings.hpp" +#include "VTaskObserver.hpp" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +std::vector ServerHandler::servers_; +std::string ServerHandler::localHostName_; + +//#define __UI_SERVEROBSERVER_DEBUG +//#define __UI_SERVERCOMOBSERVER_DEBUG +#define __UI_SERVERUPDATE_DEBUG + +ServerHandler::ServerHandler(const std::string& name,const std::string& host, const std::string& port) : + name_(name), + host_(host), + port_(port), + client_(0), + updating_(false), + communicating_(false), + suiteFilter_(new SuiteFilter()), + comQueue_(0), + activity_(NoActivity), + connectState_(new ConnectState()), + prevServerState_(SState::RUNNING), + conf_(0) +{ + if(localHostName_.empty()) + { + localHostName_=boost::asio::ip::host_name(); + } + + //Create longname + longName_=host_ + "@" + port_; + + conf_=new VServerSettings(this); + + //Create the client invoker. At this point it is empty. + try + { + client_=new ClientInvoker(host,port); + } + catch(std::exception& e) + { + UiLog().err() << "Could not create ClientInvoker for host=" << host << + " port= " << port << ". " << e.what(); + client_=0; + } + + client_->set_retry_connection_period(1); + client_->set_connection_attempts(1); + client_->set_throw_on_error(true); + + //Create the vnode root. This will represent the node tree in the viewer, but + //at this point it is empty. + vRoot_=new VServer(this); + + //Connect up the timer for refreshing the server info. The timer has not + //started yet. + + refreshTimer_=new UpdateTimer(this); + connect(refreshTimer_, SIGNAL(timeout()), + this, SLOT(refreshServerInfo())); + + + //We will need to pass various non-Qt types via signals and slots for error messages. + //So we need to register these types. + if(servers_.empty()) + { + qRegisterMetaType("std::string"); + qRegisterMetaType >("QList"); + qRegisterMetaType >("std::vector"); + } + + //Add this instance to the servers_ list. + servers_.push_back(this); + + //NOTE: we may not always want to create a thread here because of resource + // issues; another strategy would be to create threads on demand, only + // when server communication is about to start. + + //Create the queue for the tasks to be sent to the client (via the ServerComThread)! It will + //create and take ownership of the ServerComThread. At this point the queue has not started yet. + comQueue_=new ServerComQueue (this,client_); + + //Load settings + loadConf(); + + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // At this point nothing is running or active!!!! + + //Indicate that we start an init (initial load) + //activity_=LoadActivity; + + //Try to connect to the server and load the defs etc. This might fail! + reset(); +} + +ServerHandler::~ServerHandler() +{ + //Save setings + saveConf(); + + //Notify the observers + broadcast(&ServerObserver::notifyServerDelete); + + //The queue must be deleted before the client, since the thread might + //be running a job on the client!! + if (comQueue_) + delete comQueue_; + + //Remove itself from the server vector + std::vector::iterator it=std::find(servers_.begin(),servers_.end(),this); + if(it != servers_.end()) + servers_.erase(it); + + delete vRoot_; + delete connectState_; + delete suiteFilter_; + + //The safest is to delete the client in the end + if(client_) + delete client_; + + delete conf_; +} + +//----------------------------------------------- +// Refresh/update +//----------------------------------------------- + +bool ServerHandler::updateInfo(int& basePeriod,int& currentPeriod,int &drift,int& toNext) +{ + if(!refreshTimer_->isActive()) + return false; + + toNext=secsTillNextRefresh(); + basePeriod=conf_->intValue(VServerSettings::UpdateRate); + currentPeriod=refreshTimer_->interval()/1000; + drift=-1; + if(conf_->boolValue(VServerSettings::AdaptiveUpdate)) + { + drift=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement); + } + + return true; +} + +int ServerHandler::secsSinceLastRefresh() const +{ + return static_cast(lastRefresh_.secsTo(QDateTime::currentDateTime())); +} + +int ServerHandler::secsTillNextRefresh() const +{ + if(refreshTimer_->isActive()) +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + return refreshTimer_->remainingTime()/1000; +#else + return 0; +#endif + return -1; +} + +void ServerHandler::stopRefreshTimer() +{ + refreshTimer_->stop(); +#ifdef __UI_SERVERUPDATE_DEBUG + UiLogS(this).dbg() << "ServerHandler::stopRefreshTimer -->"; +#endif + broadcast(&ServerComObserver::notifyRefreshTimerStopped); +} + +void ServerHandler::startRefreshTimer() +{ + UiLogS(this).dbg() << "ServerHandler::startRefreshTimer -->"; + + if(!conf_->boolValue(VServerSettings::AutoUpdate)) + { + return; + } + + //If we are not connected to the server the + //timer should not run. + if(connectState_->state() == ConnectState::Disconnected) + return; + + if(!refreshTimer_->isActive()) + { + int rate=conf_->intValue(VServerSettings::UpdateRate); + if(rate <=0) rate=1; + refreshTimer_->setInterval(rate*1000); + refreshTimer_->start(); + broadcast(&ServerComObserver::notifyRefreshTimerStarted); + } + +#ifdef __UI_SERVERUPDATE_DEBUG + UiLogS(this).dbg() << " refreshTimer interval: " << refreshTimer_->interval(); +#endif +} + +void ServerHandler::updateRefreshTimer() +{ + UiLogS(this).dbg() << "ServerHandler::updateRefreshTimer -->"; + + if(!conf_->boolValue(VServerSettings::AutoUpdate)) + { + stopRefreshTimer(); + return; + } + + //If we are not connected to the server the + //timer should not run. + if(connectState_->state() == ConnectState::Disconnected) + return; + + int rate=conf_->intValue(VServerSettings::UpdateRate); + if(rate <=0) rate=1; + + if(refreshTimer_->isActive()) + { + refreshTimer_->stop(); + } + + refreshTimer_->setInterval(rate*1000); + refreshTimer_->start(); + broadcast(&ServerComObserver::notifyRefreshTimerChanged); + +#ifdef __UI_SERVERUPDATE_DEBUG + UiLogS(this).dbg() << " refreshTimer interval: " << refreshTimer_->interval(); +#endif + +} + +void ServerHandler::driftRefreshTimer() +{ + if(!conf_->boolValue(VServerSettings::AutoUpdate)) + { + return; + } + + //We increase the update frequency + if(activity_ != LoadActivity && + conf_->boolValue(VServerSettings::AdaptiveUpdate)) + { +#ifdef __UI_SERVERUPDATE_DEBUG + UiLogS(this).dbg() << "driftRefreshTimer -->"; +#endif + + int rate=conf_->intValue(VServerSettings::UpdateRate); + int baseDelta=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement); + int delta=baseDelta; //linear mode + + //doubling mode + if(conf_->stringValue(VServerSettings::AdaptiveUpdateMode) == "doubling") + { + delta=refreshTimer_->interval()/1000-rate; + if(delta==0) + delta=baseDelta; + } + //x modes + else + { + float f=conf_->stringValue(VServerSettings::AdaptiveUpdateMode).toFloat(); + if(f >= 1. && f <=5.) + { + delta=(refreshTimer_->interval()/1000-rate)*(f-1); + if(delta==0) + delta=1; + } + } + + refreshTimer_->drift(delta,conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate)); + + broadcast(&ServerComObserver::notifyRefreshTimerChanged); + } + +#ifdef __UI_SERVERUPDATE_DEBUG + UiLogS(this).dbg() << "driftRefreshTimer interval: " << refreshTimer_->interval(); +#endif + +} + +//returns true if the current total (drifted) period is within the maximum allowed +bool ServerHandler::checkRefreshTimerDrift() const +{ + if(!conf_->boolValue(VServerSettings::AutoUpdate)) + { + return true; + } + + if(activity_ != LoadActivity && + conf_->boolValue(VServerSettings::AdaptiveUpdate)) + { + return (refreshTimer_->interval()*1000 < + conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate)*60); + } + return true; +} + +//mark that a refresh request was sent to the queue +void ServerHandler::refreshScheduled() +{ + lastRefresh_=QDateTime::currentDateTime(); + broadcast(&ServerComObserver::notifyRefreshScheduled); +} + +//mark that a refresh request was sent to the queue +void ServerHandler::refreshFinished() +{ + broadcast(&ServerComObserver::notifyRefreshFinished); +} + +void ServerHandler::setActivity(Activity ac) +{ + activity_=ac; + broadcast(&ServerObserver::notifyServerActivityChanged); +} + +ServerHandler* ServerHandler::addServer(const std::string& name,const std::string& host, const std::string& port) +{ + ServerHandler* sh=new ServerHandler(name,host,port); + //Without the clinetinvoker we cannot use the serverhandler + if(!sh->client_) + { + delete sh; + sh=0; + } + return sh; +} + +void ServerHandler::removeServer(ServerHandler* server) +{ + std::vector::iterator it=std::find(servers_.begin(), servers_.end(),server); + if(it != servers_.end()) + { + ServerHandler *s=*it; + servers_.erase(it); + delete s; + } +} + +ServerHandler* ServerHandler::findServer(const std::string &alias) +{ + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + { + ServerHandler *s=*it; + + if (s->name() == alias) + { + return s; + } + } + return NULL; // did not find it +} + + + +//This function can be called many times so we need to avoid locking the mutex. +SState::State ServerHandler::serverState() +{ + if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity) + { + prevServerState_= SState::RUNNING; + + } + //If we are here we can be sure that the defs pointer is not being deleted!! We + //access it without locking the mutex!!! + else + { + //If get the defs can be 100% sure that it is not being deleted!! So we can + //access it without locking the mutex!!! + defs_ptr d=safelyAccessSimpleDefsMembers(); + if(d && d.get()) + { + prevServerState_= d->set_server().get_state(); + return prevServerState_; + } + } + + return prevServerState_; +} + +//This function can be called many times so we need to avoid locking the mutex. +NState::State ServerHandler::state(bool& suspended) +{ + if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity) + return NState::UNKNOWN; + + suspended=false; + + defs_ptr d=safelyAccessSimpleDefsMembers(); + if(d && d.get()) + { + suspended=d->isSuspended(); + return d->state(); + } + + return NState::UNKNOWN; +} + +defs_ptr ServerHandler::defs() +{ + defs_ptr null; + + if(client_) + { + return client_->defs(); + } + else + { + return null; + } +} + +defs_ptr ServerHandler::safelyAccessSimpleDefsMembers() +{ + defs_ptr null; + + //The defs might be deleted during reset so it cannot be accessed. + if(activity_ == LoadActivity) + { + return null; + } + //Otherwise it is safe to access certain non-vector members + else if(client_) + { + return client_->defs(); + } + else + { + return null; + } +} + +//------------------------------------------------------------- +// Run client tasks. +// +// The preferred way to run client tasks is to define and add a task to the queue. The +// queue will manage the task and will send it to the ClientInvoker. When the task +// finishes the ServerHandler::clientTaskFinished method is called where the +// result/reply can be processed. +//-------------------------------------------------------------- + +//It is protected! Practically it means we +//we can only run it through CommandHandler!!! +void ServerHandler::runCommand(const std::vector& cmd) +{ + //Shell command - we should not reach this point + if(cmd.size() > 0 && cmd[0]=="sh") + { + UI_ASSERT(0,"cmd=" << cmd[0]); + return; + } + + //ecflow_client command + if(connectState_->state() == ConnectState::Disconnected) + return; + + VTask_ptr task=VTask::create(VTask::CommandTask); + task->command(cmd); + comQueue_->addTask(task); +} + +void ServerHandler::run(VTask_ptr task) +{ + if(connectState_->state() == ConnectState::Disconnected) + return; + + switch(task->type()) + { + case VTask::ScriptTask: + return script(task); + break; + case VTask::JobTask: + return job(task); + break; + case VTask::OutputTask: + return jobout(task); + break; + case VTask::ManualTask: + return manual(task); + break; + case VTask::HistoryTask: + case VTask::MessageTask: + case VTask::StatsTask: + case VTask::ScriptPreprocTask: + case VTask::ScriptEditTask: + case VTask::ScriptSubmitTask: + case VTask::SuiteListTask: + case VTask::ZombieListTask: + case VTask::ZombieCommandTask: + comQueue_->addTask(task); + break; + default: + //If we are here we have an unhandled task type. + task->status(VTask::REJECTED); + break; + } +} + +void ServerHandler::script(VTask_ptr task) +{ + /*static std::string errText="no script!\n" + "check ECF_FILES or ECF_HOME directories, for read access\n" + "check for file presence and read access below files directory\n" + "or this may be a 'dummy' task.\n";*/ + + task->param("clientPar","script"); + comQueue_->addTask(task); +} + +void ServerHandler::job(VTask_ptr task) +{ + /*static std::string errText="no script!\n" + "check ECF_FILES or ECF_HOME directories, for read access\n" + "check for file presence and read access below files directory\n" + "or this may be a 'dummy' task.\n";*/ + + task->param("clientPar","job"); + comQueue_->addTask(task); +} + +void ServerHandler::jobout(VTask_ptr task) +{ + //static std::string errText="no job output..."; + + task->param("clientPar","jobout"); + comQueue_->addTask(task); +} + +void ServerHandler::manual(VTask_ptr task) +{ + //std::string errText="no manual ..."; + task->param("clientPar","manual"); + comQueue_->addTask(task); +} + +//The core refresh function. That should be the only one directly accessing the queue. +void ServerHandler::refreshInternal() +{ + //We add and refresh task to the queue. On startup this function can be called + //before the comQueue_ was created so we need to check if it exists. + if(comQueue_) + { + comQueue_->addNewsTask(); + } +} + +//The user initiated a refresh - using the toolbar/menu buttons or +//called after a user command was issued +void ServerHandler::refresh() +{ + refreshInternal(); + + //Reset the timer to its original value (i.e. remove the drift) + updateRefreshTimer(); +} + + +//This slot is called by the timer regularly to get news from the server. +void ServerHandler::refreshServerInfo() +{ + UiLogS(this).dbg() << "Auto refreshing server"; + refreshInternal(); + + //We reduce the update frequency + driftRefreshTimer(); +} + +//====================================================================================== +// Manages node changes. +//====================================================================================== + +//This slot is called when a node changes. +void ServerHandler::slotNodeChanged(const Node* nc,std::vector aspect) +{ + UiLogS(this).dbg() << "ServerHandler::slotNodeChanged - node: " + nc->name(); + + //for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + //UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast(*it)); + + //This can happen if we initiated a reset while we sync in the thread + if(vRoot_->isEmpty()) + { + UiLogS(this).dbg() << " tree is empty - no change applied!"; + return; + } + + VNode* vn=vRoot_->toVNode(nc); + + //We must have this VNode + assert(vn != NULL); + + //Begin update for the VNode + VNodeChange change; + vRoot_->beginUpdate(vn,aspect,change); + + //TODO: what about the infopanel or breadcrumbs?????? + if(change.ignore_) + { + UiLogS(this).dbg() << " update ignored"; + return; + } + else + { + //Notify the observers + broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change); + + //End update for the VNode + vRoot_->endUpdate(vn,aspect,change); + + //Notify the observers + broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change); + + UiLogS(this).dbg() << " update applied"; + } +} + +void ServerHandler::addNodeObserver(NodeObserver *obs) +{ + std::vector::iterator it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs); + if(it == nodeObservers_.end()) + { + nodeObservers_.push_back(obs); + } +} + +void ServerHandler::removeNodeObserver(NodeObserver *obs) +{ + std::vector::iterator it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs); + if(it != nodeObservers_.end()) + { + nodeObservers_.erase(it); + } +} + +void ServerHandler::broadcast(NoMethod proc,const VNode* node) +{ + for(std::vector::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it) + ((*it)->*proc)(node); +} + +void ServerHandler::broadcast(NoMethodV1 proc,const VNode* node,const std::vector& aspect,const VNodeChange& change) +{ + //When the observers are being notified (in a loop) they might + //want to remove themselves from the observer list. This will cause a crash. To avoid + //this we create a copy of the observers and use it in the notification loop. + std::vector nObsCopy=nodeObservers_; + + for(std::vector::const_iterator it=nObsCopy.begin(); it != nObsCopy.end(); ++it) + { + ((*it)->*proc)(node,aspect,change); + } + +#if 0 + for(std::vector::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it) + ((*it)->*proc)(node,aspect,change); +#endif +} + +//--------------------------------------------------------------------------- +// Manages Defs changes and desf observers. Defs observers are notified when +// there is a change. +//--------------------------------------------------------------------------- + +//This slot is called when the Defs change. +void ServerHandler::slotDefsChanged(std::vector aspect) +{ + UiLog().dbg() << "ServerHandler::slotDefsChanged -->"; + //for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + // UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast(*it)); + + //Begin update for the VNode + //VNodeChange change; + vRoot_->beginUpdate(aspect); + + //Notify the observers + //broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change); + + //End update for the VNode + //vRoot_->endUpdate(vn,aspect,change); + + //Notify the observers + //broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change); + + //UserMessage::message(UserMessage::DBG, false," --> Update applied"); + + for(std::vector::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it) + (*it)->notifyDefsChanged(this,aspect); +} + +void ServerHandler::addServerObserver(ServerObserver *obs) +{ + std::vector::iterator it=std::find(serverObservers_.begin(),serverObservers_.end(),obs); + if(it == serverObservers_.end()) + { + serverObservers_.push_back(obs); +#ifdef __UI_SERVEROBSERVER_DEBUG + UiLog(this).dbg() << "ServerHandler::addServerObserver --> " << obs; +#endif + } +} + +void ServerHandler::removeServerObserver(ServerObserver *obs) +{ +#ifdef __UI_SERVEROBSERVER_DEBUG + UI_FUNCTION_LOG_S(this) +#endif + std::vector::iterator it=std::find(serverObservers_.begin(),serverObservers_.end(),obs); + if(it != serverObservers_.end()) + { + serverObservers_.erase(it); +#ifdef __UI_SERVEROBSERVER_DEBUG + UiLog(this).dbg() << " remove: " << obs; +#endif + } +} + +void ServerHandler::broadcast(SoMethod proc) +{ +#ifdef __UI_SERVEROBSERVER_DEBUG + UI_FUNCTION_LOG_S(this) +#endif + + bool checkExistence=true; + + //When the observers are being notified (in a loop) they might + //want to remove themselves from the observer list. This will cause a crash. To avoid + //this we create a copy of the observers and use it in the notification loop. + std::vector sObsCopy=serverObservers_; + + for(std::vector::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it) + { + //We need to check if the given observer is still in the original list. When we delete the server, due to + //dependencies it is possible that the observer is already deleted at this point. + if(!checkExistence || std::find(serverObservers_.begin(),serverObservers_.end(),*it) != serverObservers_.end()) + { + ((*it)->*proc)(this); + } + } +} + + +void ServerHandler::broadcast(SoMethodV1 proc,const VServerChange& ch) +{ + for(std::vector::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it) + ((*it)->*proc)(this,ch); +} + +//------------------------------------------------ +// ServerComObserver +//------------------------------------------------ + +void ServerHandler::addServerComObserver(ServerComObserver *obs) +{ + std::vector::iterator it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs); + if(it == serverComObservers_.end()) + { + serverComObservers_.push_back(obs); +#ifdef __UI_SERVERCOMOBSERVER_DEBUG + UiLog(this).dbg() << "ServerHandler::addServerComObserver --> " << obs; +#endif + } +} + +void ServerHandler::removeServerComObserver(ServerComObserver *obs) +{ + std::vector::iterator it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs); + if(it != serverComObservers_.end()) + { + serverComObservers_.erase(it); + #ifdef __UI_SERVERCOMOBSERVER_DEBUG + UiLog(this).dbg() << "ServerHandler::removeServerComObserver --> " << obs; + #endif + } +} + +void ServerHandler::broadcast(SocMethod proc) +{ + bool checkExistence=true; + + //When the observers are being notified (in a loop) they might + //want to remove themselves from the observer list. This will cause a crash. To avoid + //this we create a copy of the observers and use it in the notification loop. + std::vector sObsCopy=serverComObservers_; + + for(std::vector::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it) + { + //We need to check if the given observer is still in the original list. When we delete the server, due to + //dependencies it is possible that the observer is already deleted at this point. + if(!checkExistence || std::find(serverComObservers_.begin(),serverComObservers_.end(),*it) != serverComObservers_.end()) + ((*it)->*proc)(this); + } +} + + +//------------------------------------------------------------------- +// This slot is called when the comThread finished the given task!! +//------------------------------------------------------------------- + +//There was a drastic change during the SYNC! As a safety measure we need to clear +//the tree. We will rebuild it when the SYNC finishes. +void ServerHandler::slotRescanNeed() +{ + clearTree(); +} + +void ServerHandler::clientTaskFinished(VTask_ptr task,const ServerReply& serverReply) +{ + UiLogS(this).dbg() << "ServerHandler::clientTaskFinished -->"; + + //The status of the tasks sent from the info panel must be properly set to + //FINISHED!! Only that will notify the observers about the change!! + + //See which type of task finished. What we do now will depend on that. + switch(task->type()) + { + case VTask::CommandTask: + { + // a command was sent - we should now check whether there have been + // any updates on the server (there should have been, because we + // just did something!) + + UiLogS(this).dbg() << " command finished - send SYNC command"; + //comQueue_->addNewsTask(); + comQueue_->addSyncTask(); + updateRefreshTimer(); + break; + } + case VTask::NewsTask: + { + // we've just asked the server if anything has changed - has it? + + refreshFinished(); + + switch (serverReply.get_news()) + { + case ServerReply::NO_NEWS: + { + // no news, nothing to do + UiLogS(this).dbg() << " NO_NEWS"; + + //If we just regained the connection we need to reset + if(connectionGained()) + { + reset(); + } + break; + } + + case ServerReply::NEWS: + { + // yes, something's changed - synchronise with the server + + //If we just regained the connection we need to reset + UiLogS(this).dbg() << " NEWS - send SYNC command"; + if(connectionGained()) + { + reset(); + } + else + { + comQueue_->addSyncTask(); + } + break; + } + + case ServerReply::DO_FULL_SYNC: + { + // yes, a lot of things have changed - we need to reset!!!!!! + UiLogS(this).dbg() << " DO_FULL_SYNC - will reset"; + connectionGained(); + reset(); + break; + } + + default: + { + break; + } + } + break; + } + case VTask::SyncTask: + { + UiLogS(this).dbg() << " sync finished"; + + //This typically happens when a suite is added/removed + if(serverReply.full_sync() || vRoot_->isEmpty()) + { + UiLogS(this).dbg() << " full sync requested - rescanTree"; + + //This will update the suites + restart the timer + rescanTree(); + +#if 0 + { + ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction + defs_ptr defs = defsAccess.defs(); + if(defs) + { + std::cout << defs; + } + } +#endif + + } + else + { + broadcast(&ServerObserver::notifyEndServerSync); + } + break; + } + + case VTask::ResetTask: + { + //If not yet connected but the sync task was successful + resetFinished(); + break; + } + + case VTask::ScriptTask: + case VTask::ManualTask: + case VTask::HistoryTask: + case VTask::JobTask: + { + task->reply()->fileReadMode(VReply::ServerReadMode); + task->reply()->setText(serverReply.get_string()); + task->reply()->setReadTruncatedTo(truncatedLinesFromServer(task->reply()->text())); + task->status(VTask::FINISHED); + break; + } + + case VTask::OutputTask: + { + task->reply()->fileReadMode(VReply::ServerReadMode); + + std::string err; + VFile_ptr f(VFile::create(false)); + f->setFetchMode(VFile::ServerFetchMode); + f->setFetchModeStr("fetched from server " + name()); + f->setSourcePath(task->reply()->fileName()); + f->setTruncatedTo(truncatedLinesFromServer(serverReply.get_string())); + f->write(serverReply.get_string(),err); + task->reply()->tmpFile(f); + + task->status(VTask::FINISHED); + break; + } + + case VTask::MessageTask: + { + task->reply()->setTextVec(serverReply.get_string_vec()); + task->status(VTask::FINISHED); + break; + } + + case VTask::StatsTask: + { + std::stringstream ss; + serverReply.stats().show(ss); + task->reply()->text(ss.str()); + task->status(VTask::FINISHED); + break; + } + + case VTask::ScriptPreprocTask: + case VTask::ScriptEditTask: + { + task->reply()->text(serverReply.get_string()); + task->status(VTask::FINISHED); + break; + } + case VTask::ScriptSubmitTask: + { + UiLogS(this).dbg() << " script submit finished - send NEWS command"; + + task->reply()->text(serverReply.get_string()); + task->status(VTask::FINISHED); + + //Submitting the task was successful - we should now get updates from the server + refresh(); + break; + } + + case VTask::SuiteListTask: + { + //Update the suite filter with the list of suites actually loaded onto the server. + //If the suitefilter is enabled this might have only a subset of it in our tree + updateSuiteFilterWithLoaded(serverReply.get_string_vec()); + task->status(VTask::FINISHED); + break; + } + + case VTask::ZombieListTask: + { + task->reply()->zombies(serverReply.zombies()); + task->status(VTask::FINISHED); + break; + } + + default: + task->status(VTask::FINISHED); + break; + + } +} + +//------------------------------------------------------------------- +// This slot is called when the comThread failed the given task!! +//------------------------------------------------------------------- + +void ServerHandler::clientTaskFailed(VTask_ptr task,const std::string& errMsg) +{ + UiLogS(this).dbg() << "ServerHandler::clientTaskFailed -->"; + + //TODO: suite filter + ci_ observers + + //See which type of task finished. What we do now will depend on that. + switch(task->type()) + { + case VTask::SyncTask: + connectionLost(errMsg); + break; + + //The initialisation failed + case VTask::ResetTask: + { + resetFailed(errMsg); + break; + } + case VTask::NewsTask: + { + connectionLost(errMsg); + refreshFinished(); + break; + } + case VTask::StatsTask: + { + connectionLost(errMsg); + break; + } + case VTask::CommandTask: + { + comQueue_->addSyncTask(); + task->reply()->setErrorText(errMsg); + task->status(VTask::ABORTED); + UserMessage::message(UserMessage::WARN, true, errMsg); + break; + } + default: + task->reply()->setErrorText(errMsg); + task->status(VTask::ABORTED); + break; + } +} + +void ServerHandler::connectionLost(const std::string& errMsg) +{ + connectState_->state(ConnectState::Lost); + connectState_->errorMessage(errMsg); + broadcast(&ServerObserver::notifyServerConnectState); +} + +bool ServerHandler::connectionGained() +{ + if(connectState_->state() != ConnectState::Normal) + { + connectState_->state(ConnectState::Normal); + broadcast(&ServerObserver::notifyServerConnectState); + return true; + } + return false; + +} + +void ServerHandler::disconnectServer() +{ + if(connectState_->state() != ConnectState::Disconnected) + { + connectState_->state(ConnectState::Disconnected); + broadcast(&ServerObserver::notifyServerConnectState); + + //Stop the queue + comQueue_->disable(); + + //Stop the timer + stopRefreshTimer(); + } +} + +void ServerHandler::connectServer() +{ + if(connectState_->state() == ConnectState::Disconnected) + { + //Start the queue + comQueue_->enable(); + + //Start the timer + startRefreshTimer(); + + //Try to get the news + refreshInternal(); + + //TODO: attach the observers!!!! + } +} + +void ServerHandler::reset() +{ + UiLogS(this).dbg() << "ServerHandler::reset -->"; + + //--------------------------------- + // First part of reset: clearing + //--------------------------------- + + if(comQueue_->prepareReset()) + { + //Stop the timer + stopRefreshTimer(); + + // First part of reset: clear the tree + clearTree(); + + // Second part of reset: loading + + //Indicate that we reload the defs + setActivity(LoadActivity); + + //mark the current moment as last refresh + lastRefresh_=QDateTime::currentDateTime(); + + //NOTE: at this point the queue is not running but reset() will start it. + //While the queue is in reset mode it does not accept tasks. + comQueue_->reset(); + } + else + { + UiLogS(this).dbg() << " skip reset"; + } +} + +//The reset was successful +void ServerHandler::resetFinished() +{ + setActivity(NoActivity); + + //Set server host and port in defs. It is used to find the server of + //a given node in the viewer. + { + ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction + + defs_ptr defs = defsAccess.defs(); + if(defs != NULL) + { + ServerState& st=defs->set_server(); + st.hostPort(std::make_pair(host_,port_)); + } + } + + //Create an object to inform the observers about the change + VServerChange change; + + //Begin the full scan to get the tree. This call does not actually + //run the scan but counts how many suits will be available. + vRoot_->beginScan(change); + + //Notify the observers that the scan has started + broadcast(&ServerObserver::notifyBeginServerScan,change); + + //Finish full scan + vRoot_->endScan(); + + //assert(change.suiteNum_ == vRoot_->numOfChildren()); + + //Notify the observers that scan has ended + broadcast(&ServerObserver::notifyEndServerScan); + + //The suites might have been changed + updateSuiteFilter(); + + //Restart the timer + startRefreshTimer(); + + //Set the connection state + if(connectState_->state() != ConnectState::Normal) + { + connectState_->state(ConnectState::Normal); + broadcast(&ServerObserver::notifyServerConnectState); + } +} + +//The reset failed and we could not connect to the server, e.g. because the the server +//may be down, or there is a network error, or the authorisation is missing. +void ServerHandler::resetFailed(const std::string& errMsg) +{ + //This status is indicated by the connectStat_. Each object needs to be aware of it + //and do its tasks accordingly. + + //Create an object to inform the observers about the change + VServerChange change; + //Notify the observers that the scan has started + broadcast(&ServerObserver::notifyBeginServerScan,change); + //Notify the observers that scan has ended + broadcast(&ServerObserver::notifyEndServerScan); + + connectState_->state(ConnectState::Lost); + connectState_->errorMessage(errMsg); + setActivity(NoActivity); + + broadcast(&ServerObserver::notifyServerConnectState); + + //Restart the timer + startRefreshTimer(); +} + +//This function must be called during a SYNC!!!!!!!! + +void ServerHandler::clearTree() +{ + UiLogS(this).dbg() << "ServerHandler::clearTree -->"; + + if(!vRoot_->isEmpty()) + { + //Notify observers that the clear is about to begin + broadcast(&ServerObserver::notifyBeginServerClear); + + //Clear vnode + vRoot_->clear(); + + //Notify observers that the clear ended + broadcast(&ServerObserver::notifyEndServerClear); + } + + UiLogS(this).dbg() << " <-- clearTree"; +} + + +void ServerHandler::rescanTree() +{ + UiLogS(this).dbg() << "ServerHandler::rescanTree -->"; + + setActivity(RescanActivity); + + //--------------------------------- + // First part of rescan: clearing + //--------------------------------- + + //Stop the timer + stopRefreshTimer(); + + //Stop the queue as a safety measure: we do not want any changes during the rescan + comQueue_->suspend(false); + + //clear the tree + clearTree(); + + //At this point nothing is running and the tree is empty (it only contains + //the root node) + + //-------------------------------------- + // Second part of rescan: loading + //-------------------------------------- + + //Create an object to inform the observers about the change + VServerChange change; + + //Begin the full scan to get the tree. This call does not actually + //run the scan but counts how many suits will be available. + vRoot_->beginScan(change); + + //Notify the observers that the scan has started + broadcast(&ServerObserver::notifyBeginServerScan,change); + + //Finish full scan + vRoot_->endScan(); + + //Notify the observers that scan has ended + broadcast(&ServerObserver::notifyEndServerScan); + + //Restart the queue + comQueue_->start(); + + //The suites might have been changed + updateSuiteFilter(); + + //Start the timer + startRefreshTimer(); + + setActivity(NoActivity); + + UiLogS(this).dbg() << "<-- rescanTree"; +} + +//==================================================== +// Suite filter +//==================================================== + +void ServerHandler::setSuiteFilterWithOne(VNode* n) +{ + if(n) + { + if(VNode* sn=n->suite()) + if(VSuiteNode *suite=sn->isSuite()) + { + if(suiteFilter_->isEnabled() == false) + { + suiteFilter_->setEnabled(true); + suiteFilter_->selectOnlyOne(suite->strName()); + //after reset the loaded suites need to be read again from the server! + suiteFilter_->setLoadedInitialised(false); + reset(); + } + else if(suiteFilter_->isOnlyOneFiltered(suite->strName()) == false) + { + suiteFilter_->selectOnlyOne(suite->strName()); + //after reset the loaded suites need to be read again from the server! + suiteFilter_->setLoadedInitialised(false); + reset(); + } + } + } +} + +void ServerHandler::updateSuiteFilter(SuiteFilter* sf) +{ +UI_FUNCTION_LOG_S(this) + if(suiteFilter_->update(sf)) + { + //If only this flag has changed we exec a custom task for it + if(suiteFilter_->changeFlags().sameAs(SuiteFilter::AutoAddChanged)) + { + comQueue_->addSuiteAutoRegisterTask(); + } + //Otherwise we need a full reset + else + { + reset(); + } + + //Save the settings + conf_->saveSettings(); + } +} + +//Update the suite filter with the list of suites actually loaded onto the server. +//If the suitefilter is enabled we might have only a subset of it in our tree. +void ServerHandler::updateSuiteFilterWithLoaded(const std::vector& loadedSuites) +{ +UI_FUNCTION_LOG_S(this) + if(suiteFilter_->setLoaded(loadedSuites)) + { + //If the filtered state changed and the filter is active we need to reset + if(suiteFilter_->isEnabled()) + reset(); + } +} + +//Update the suite filter with the list of suites stored in the defs (in the tree). It only +//makes sense if the filter is disabled since in this case the defs stores all the loaded servers. +void ServerHandler::updateSuiteFilterWithDefs() +{ +UI_FUNCTION_LOG_S(this) + + if(suiteFilter_->isEnabled()) + return; + + //The filte ris disabled + std::vector defSuites; + vRoot_->suites(defSuites); + suiteFilter_->setLoaded(defSuites); +} + +//Only called internally after reset or serverscan!! +void ServerHandler::updateSuiteFilter() +{ +UI_FUNCTION_LOG_S(this) + //We only fetch the full list of loaded suites from the server + //via the thread when + // -the suiteFilter is enabled! + if(suiteFilter_->isEnabled()) + { + //This will call updateSuiteFilterWithLoaded() + comQueue_->addSuiteListTask(); + } + //Otherwise the defs contains the full list of suites + else + { + std::vector defSuites; + vRoot_->suites(defSuites); + suiteFilter_->setLoaded(defSuites); + } + +#if 0 + //We only fetch the full list of loaded suites from the server + //via the thread when + // -the suiteFilter is not yet initialised + // OR + // -the suiteFilter is observerved and it is enabled! + if(!suiteFilter_->isLoadedInitialised() || + (suiteFilter_->hasObserver() && suiteFilter_->isEnabled())) + { + //This will call updateSuiteFilterWithLoaded() + comQueue_->addSuiteListTask(); + } + else + { + std::vector defSuites; + vRoot_->suites(defSuites); + suiteFilter_->setLoaded(defSuites); + } +#endif +} + +bool ServerHandler::readFromDisk() const +{ + return conf_->boolValue(VServerSettings::ReadFromDisk); +} + +QString ServerHandler::nodeMenuMode() const +{ + return conf_->stringValue(VServerSettings::NodeMenuMode); +} + +QString ServerHandler::defStatusNodeMenuMode() const +{ + return conf_->stringValue(VServerSettings::NodeMenuModeForDefStatus); +} + +void ServerHandler::confChanged(VServerSettings::Param par,VProperty* prop) +{ + switch(par) + { + case VServerSettings::AutoUpdate: + case VServerSettings::UpdateRate: + case VServerSettings::AdaptiveUpdate: + case VServerSettings::AdaptiveUpdateMode: + case VServerSettings::AdaptiveUpdateIncrement: + updateRefreshTimer(); + break; + case VServerSettings::MaxAdaptiveUpdateRate: + { + if(!checkRefreshTimerDrift()) + { + updateRefreshTimer(); + } + break; + } + case VServerSettings::NodeMenuMode: + MainWindow::updateMenuMode(this); + break; + + case VServerSettings::NotifyAbortedEnabled: + case VServerSettings::NotifyRestartedEnabled: + case VServerSettings::NotifyLateEnabled: + case VServerSettings::NotifyZombieEnabled: + case VServerSettings::NotifyAliasEnabled: + checkNotificationState(par); + break; + default: + break; + } +} + +void ServerHandler::checkNotificationState(VServerSettings::Param par) +{ + std::string id=VServerSettings::notificationId(par); + if(id.empty()) + return; + + bool enabled=false; + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + { + ServerHandler *s=*it; + if(s->conf()->boolValue(par)) + { + enabled=true; + break; + } + } + + ChangeNotify::updateNotificationStateFromServer(id,enabled); +} + +//Called from changeNotify +bool ServerHandler::checkNotificationState(const std::string& notifierId) +{ + bool enabled=false; + VServerSettings::Param par=VServerSettings::notificationParam(notifierId); + + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + { + ServerHandler *s=*it; + if(s->conf()->boolValue(par)) + { + enabled=true; + break; + } + } + + return enabled; +} + +void ServerHandler::saveSettings() +{ + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + (*it)->saveConf(); +} + +void ServerHandler::saveConf() +{ + conf_->saveSettings(); +} + +void ServerHandler::loadConf() +{ + //This will call confChanged for any non-default settings + conf_->loadSettings(); +} + +void ServerHandler::writeDefs(const std::string& fileName) +{ + comQueue_->suspend(true); + ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction + defs_ptr defs = defsAccess.defs(); + if(defs) + { + defs->save_as_filename(fileName,PrintStyle::MIGRATE); + } + comQueue_->start(); +} + +void ServerHandler::writeDefs(VInfo_ptr info,const std::string& fileName) +{ + if(!info || !info->node()) + return; + + comQueue_->suspend(true); + ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction + defs_ptr defs = defsAccess.defs(); + if(defs) + { + PrintStyle style(PrintStyle::MIGRATE); + std::ofstream out(fileName.c_str()); + out << "defs_state MIGRATE" << std::endl; + info->node()->node()->print(out); + out << std::endl; + out.close(); + } + comQueue_->start(); +} + + +//-------------------------------------------- +// Other +//-------------------------------------------- + +void ServerHandler::searchBegan() +{ + UiLogS(this).dbg() << "ServerHandler::searchBegan --> suspend queue"; + comQueue_->suspend(true); +} + +void ServerHandler::searchFinished() +{ + UiLogS(this).dbg() << "ServerHandler::searchFinished --> start queue"; + comQueue_->start(); + +} + +int ServerHandler::truncatedLinesFromServer(const std::string& txt) const +{ + //if the text is truncated the following line is added to the bottom of it: + //# >>>>>>>> File truncated down to 15. Truncated from the end of file <<<<<<<<< + //We search for this string and if truncation did happen we indicate it in the reply + size_t txtSize=txt.size(); + if(txt.find(">> File truncated down to", + (txtSize > 200)?(txtSize-100):0) != std::string::npos) + { + return conf_->intValue(VServerSettings::MaxOutputFileLines); + } + + return -1; +} + +//-------------------------------------------------------------- +// +// Find the server for a node. +// TODO: this is just a backup method. We might not want to use it +// at all, since it is not safe. +// +//-------------------------------------------------------------- + +ServerHandler* ServerHandler::find(const std::string& name) +{ + for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) + if((*it)->name() == name) + return *it; + return NULL; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,234 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERHANDLER_HPP_ +#define SERVERHANDLER_HPP_ + +#include +#include +#include +#include +#include + +#include "Defs.hpp" + +#include "VReply.hpp" +#include "VTask.hpp" +#include "VInfo.hpp" +#include "VServerSettings.hpp" + +#include + +class ClientInvoker; +class ServerReply; + +class ConnectState; +class NodeObserver; +class ServerHandler; +class ServerComQueue; +class ServerObserver; +class ServerComObserver; +class SuiteFilter; +class UpdateTimer; +class VNodeChange; +class VServer; +class VServerChange; +class VSettings; +class VSuiteNode; + + +class ServerHandler : public QObject +{ + Q_OBJECT // inherits from QObject in order to gain signal/slots + + friend class ServerDefsAccess; + friend class ServerComQueue; + friend class CommandHandler; + +public: + enum Activity {NoActivity,LoadActivity,RescanActivity}; + + const std::string& name() const {return name_;} + const std::string& host() const {return host_;} + const std::string& longName() const {return longName_;} + const std::string& port() const {return port_;} + + Activity activity() const {return activity_;} + ConnectState* connectState() const {return connectState_;} + bool communicating() {return communicating_;} + bool readFromDisk() const; + SuiteFilter* suiteFilter() const {return suiteFilter_;} + QString nodeMenuMode() const; + QString defStatusNodeMenuMode() const; + + void setSuiteFilterWithOne(VNode*); + void updateSuiteFilter(SuiteFilter*); + void updateSuiteFilterWithDefs(); + + void connectServer(); + void disconnectServer(); + void reset(); + + void refresh(); + void setUpdatingStatus(bool newStatus) {updating_ = newStatus;} + + VServer* vRoot() const {return vRoot_;} + SState::State serverState(); + NState::State state(bool& isSuspended); + + void run(VTask_ptr); + + void addNodeObserver(NodeObserver* obs); + void removeNodeObserver(NodeObserver* obs); + + void addServerObserver(ServerObserver* obs); + void removeServerObserver(ServerObserver* obs); + + void addServerComObserver(ServerComObserver* obs); + void removeServerComObserver(ServerComObserver* obs); + + void confChanged(VServerSettings::Param,VProperty*); + VServerSettings* conf() const {return conf_;} + + bool isLocalHost() {return (localHostName_ == host_ || host_ == "localhost");} + + static void saveSettings(); + + static const std::vector& servers() {return servers_;} + static ServerHandler* addServer(const std::string &name,const std::string &host, const std::string &port); + static void removeServer(ServerHandler*); + static ServerHandler* findServer(const std::string &alias); + + void searchBegan(); + void searchFinished(); + bool updateInfo(int& basePeriod,int& currentPeriod,int &drift,int& toNext); + QDateTime lastRefresh() const {return lastRefresh_;} + int secsSinceLastRefresh() const; + int secsTillNextRefresh() const; + + static bool checkNotificationState(const std::string& notifierId); + + static ServerHandler* find(const std::string& name); + + void writeDefs(const std::string& fileName); + void writeDefs(VInfo_ptr info,const std::string& fileName); + +protected: + ServerHandler(const std::string& name,const std::string& host,const std::string& port); + ~ServerHandler(); + + //Only friend classes can access it. Practically it means we + //we can only run it through CommandHandler!!! + void runCommand(const std::vector& cmd); + + void connectToServer(); + void setCommunicatingStatus(bool c) {communicating_ = c;} + void clientTaskFinished(VTask_ptr task,const ServerReply& serverReply); + void clientTaskFailed(VTask_ptr task,const std::string& errMsg); + + static void checkNotificationState(VServerSettings::Param par); + + bool checkRefreshTimerDrift() const; + void refreshScheduled(); + void refreshFinished(); + + std::string name_; + std::string host_; + std::string port_; + ClientInvoker* client_; + std::string longName_; + bool updating_; + bool communicating_; + std::vector nodeObservers_; + std::vector serverObservers_; + std::vector serverComObservers_; + + VServer* vRoot_; + + //The list of suites the server makes accessible + SuiteFilter* suiteFilter_; + + static std::vector servers_; + +private Q_SLOTS: + void refreshServerInfo(); + void slotNodeChanged(const Node* n, std::vector); + void slotDefsChanged(std::vector); + void slotRescanNeed(); + +private: + //Begin and end the initialisation by connecting to the server and syncing. + void refreshInternal(); + void resetFinished(); + void resetFailed(const std::string& errMsg); + void clearTree(); + void rescanTree(); + void connectionLost(const std::string& errMsg); + bool connectionGained(); + + void updateSuiteFilterWithLoaded(const std::vector&); + void updateSuiteFilter(); + + //Handle the refresh timer + void stopRefreshTimer(); + void startRefreshTimer(); + void updateRefreshTimer(); + void driftRefreshTimer(); + + void script(VTask_ptr req); + void job(VTask_ptr req); + void jobout(VTask_ptr req); + void manual(VTask_ptr req); + + defs_ptr defs(); + defs_ptr safelyAccessSimpleDefsMembers(); + + void setActivity(Activity activity); + + typedef void (ServerObserver::*SoMethod)(ServerHandler*); + typedef void (ServerObserver::*SoMethodV1)(ServerHandler*,const VServerChange&); + void broadcast(SoMethod); + void broadcast(SoMethodV1,const VServerChange&); + + typedef void (NodeObserver::*NoMethod)(const VNode*); + typedef void (NodeObserver::*NoMethodV1)(const VNode*,const std::vector&,const VNodeChange&); + typedef void (NodeObserver::*NoMethodV2)(const VNode*,const VNodeChange&); + void broadcast(NoMethod,const VNode*); + void broadcast(NoMethodV1,const VNode*,const std::vector&,const VNodeChange&); + void broadcast(NoMethodV2,const VNode*,const VNodeChange&); + + typedef void (ServerComObserver::*SocMethod)(ServerHandler*); + void broadcast(SocMethod); + + void saveConf(); + void loadConf(); + + int truncatedLinesFromServer(const std::string& txt) const; + + QMutex defsMutex_; + defs_ptr defs_; + + ServerComQueue* comQueue_; + + //std::string targetNodeNames_; // used when building up a command in ServerHandler::command + //std::string targetNodeFullNames_; // used when building up a command in ServerHandler::command + + UpdateTimer* refreshTimer_; + QDateTime lastRefresh_; + + Activity activity_; + ConnectState* connectState_; + SState::State prevServerState_; + + VServerSettings* conf_; + + static std::string localHostName_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerItem.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerItem.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerItem.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerItem.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,141 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerItem.hpp" +#include "ServerHandler.hpp" + +#include +#include +#include +#include +#include + + +ServerItem::ServerItem(const std::string& name) : + name_(name), + favourite_(false), + system_(false), + useCnt_(0), + handler_(0) +{ +} + +ServerItem::ServerItem(const std::string& name,const std::string& host,const std::string& port, bool favourite) : + name_(name), host_(host), port_(port), + favourite_(favourite), + system_(false), + useCnt_(0), + handler_(0) +{ +} + +ServerItem::~ServerItem() +{ + broadcastDeletion(); + + if(handler_) + ServerHandler::removeServer(handler_); +} + +bool ServerItem::isUsed() const +{ + return (handler_ != NULL); +} + + +void ServerItem::reset(const std::string& name,const std::string& host,const std::string& port) +{ + name_=name; + host_=host; + port_=port; + + broadcastChanged(); +} + +void ServerItem::setFavourite(bool b) +{ + favourite_=b; + broadcastChanged(); +} + +void ServerItem::setSystem(bool b) +{ + system_=b; + //broadcastChanged(); +} + +std::string ServerItem::longName() const +{ + return host_ + "@" + port_; +} + +//=========================================================== +// Register the usage of the server. Create and destroys the +// the ServerHandler. +//=========================================================== + +void ServerItem::registerUsageBegin() +{ + if(!handler_) + { + handler_=ServerHandler::addServer(name_,host_,port_); + } + if(handler_) + useCnt_++; +} + +void ServerItem::registerUsageEnd() +{ + useCnt_--; + if(useCnt_ == 0 && handler_) + { + ServerHandler::removeServer(handler_); + handler_=0; + } +} + +//=========================================================== +// Observers +//=========================================================== + +void ServerItem::addObserver(ServerItemObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + { + registerUsageBegin(); + //We might not be able to create the handle + if(handler_) + observers_.push_back(o); + } +} + +void ServerItem::removeObserver(ServerItemObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + { + observers_.erase(it); + registerUsageEnd(); + } +} + +void ServerItem::broadcastChanged() +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + (*it)->notifyServerItemChanged(this); +} + +void ServerItem::broadcastDeletion() +{ + std::vector obsCopy=observers_; + + for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) + (*it)->notifyServerItemDeletion(this); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerItem.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerItem.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerItem.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerItem.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,84 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERITEM_HPP_ +#define SERVERITEM_HPP_ + +#include +#include +#include + +#include +#include + +class ServerHandler; +class ServerItem; + +class ServerItemObserver +{ +public: + ServerItemObserver() {} + virtual ~ServerItemObserver() {} + virtual void notifyServerItemChanged(ServerItem*)=0; + virtual void notifyServerItemDeletion(ServerItem*)=0; +}; + + +class ServerItem +{ + +friend class ServerList; + +public: + const std::string& name() const {return name_;} + const std::string& host() const {return host_;} + const std::string& port() const {return port_;} + std::string longName() const; + bool isFavourite() const {return favourite_;} + bool isSystem() const {return system_;} + + bool isUsed() const; + int useCnt() const {return useCnt_;} + ServerHandler* serverHandler() const {return handler_;} + + void addObserver(ServerItemObserver*); + void removeObserver(ServerItemObserver*); + +protected: + explicit ServerItem(const std::string&); + ServerItem(const std::string&,const std::string&,const std::string&,bool favourite=false); + ~ServerItem(); + + void name(const std::string& name) {name_=name;} + void host(const std::string& host) {host_=host;} + void port(const std::string& port) {port_=port;} + void reset(const std::string& name,const std::string& host,const std::string& port); + void setFavourite(bool b); + void setSystem(bool b); + + void registerUsageBegin(); + void registerUsageEnd(); + + void broadcastChanged(); + void broadcastDeletion(); + + std::string name_; + std::string host_; + std::string port_; + bool favourite_; + bool system_; + int useCnt_; + ServerHandler* handler_; + + std::vector observers_; +}; + +typedef boost::shared_ptr ServerItem_ptr; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerList.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerList.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerList.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerList.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,583 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include +#include + +#include "ServerList.hpp" + +#include "DirectoryHandler.hpp" +#include "ServerItem.hpp" +#include "UserMessage.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" + +#include +#include +#include +#include +#include + +#include + +ServerList* ServerList::instance_=0; + +#define _UI_SERVERLIST_DEBUG + +ServerListTmpItem::ServerListTmpItem(ServerItem* item) : + name_(item->name()), + host_(item->host()), + port_(item->port()) +{} + +//Singleton instance method +ServerList* ServerList::instance() +{ + if(!instance_) + instance_=new ServerList(); + return instance_; +} + +//=========================================================== +// Managing server items +//=========================================================== + +ServerItem* ServerList::itemAt(int index) +{ + return (index >=0 && index < static_cast(items_.size()))?items_.at(index):0; +} + +ServerItem* ServerList::find(const std::string& name) +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name) + return *it; + } + return 0; +} + +ServerItem* ServerList::find(const std::string& name, const std::string& host, const std::string& port) +{ + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name && (*it)->host() == host && (*it)->port() == port) + return *it; + } + return 0; +} + +ServerItem* ServerList::add(const std::string& name,const std::string& host, + const std::string& port,bool favourite,bool saveIt) +{ + std::string errStr; + if(!checkItemToAdd(name,host,port,true,errStr)) + { + throw std::runtime_error(errStr); + return 0; + } + + ServerItem* item=new ServerItem(name,host,port,favourite); + + items_.push_back(item); + + if(saveIt) + save(); + + broadcastChanged(); + + return item; +} + +void ServerList::remove(ServerItem *item) +{ + std::vector::iterator it=std::find(items_.begin(),items_.end(),item); + if(it != items_.end()) + { + items_.erase(it); + //item->broadcastDeletion(); + delete item; + + save(); + broadcastChanged(); + } +} + +void ServerList::reset(ServerItem* item,const std::string& name,const std::string& host,const std::string& port) +{ + std::vector::iterator it=std::find(items_.begin(),items_.end(),item); + if(it != items_.end()) + { + //Check if there is an item with the same name. names have to be unique! + if(item->name() != name && find(name)) + return; + + item->reset(name,host,port); + + save(); + broadcastChanged(); + } +} + +void ServerList::setFavourite(ServerItem* item,bool b) +{ + std::vector::iterator it=std::find(items_.begin(),items_.end(),item); + if(it != items_.end()) + { + item->setFavourite(b); + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + (*it)->notifyServerListFavouriteChanged(item); + } +} + +std::string ServerList::uniqueName(const std::string& name) +{ + bool hasIt=false; + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == name) + { + hasIt=true; + break; + } + } + if(!hasIt) + { + return name; + } + + for(int i=1; i < 100; i++) + { + std::ostringstream c; + c << i; + std::string currentName=name+"_"+c.str(); + + hasIt=false; + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it)->name() == currentName) + { + hasIt=true; + break; + } + } + if(!hasIt) + { + return currentName; + } + } + + return name; + +} + + +void ServerList::rescan() +{ + +} + +bool ServerList::checkItemToAdd(const std::string& name,const std::string& host,const std::string& port,bool checkDuplicate,std::string& errStr) +{ + if(name.empty()) + { + errStr="Empty server name"; + return false; + } + else if(host.empty()) + { + errStr="Empty server host"; + return false; + } + else if(port.empty()) + { + errStr="Empty server port"; + return false; + } + + try { boost::lexical_cast(port); } + catch ( boost::bad_lexical_cast& e) + { + errStr="Invalid port number: " + port; + return false; + } + + if(checkDuplicate && find(name)) + { + errStr="Duplicated server name: " + name; + return false; + } + + return true; +} + +//=========================================================== +// Initialisation +//=========================================================== + +void ServerList::init() +{ + localFile_ = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "servers.txt"); + systemFile_=DirectoryHandler::concatenate(DirectoryHandler::shareDir(), "servers"); + + if(load() == false) + { + if(readRcFile()) + save(); + } + + syncSystemFile(); +} + +bool ServerList::load() +{ + UiLog().dbg() << "ServerList::load() -->"; + + std::ifstream in(localFile_.c_str()); + if(!in.good()) + return false; + + std::string errStr; + + std::string line; + int lineCnt=1; + while(getline(in,line)) + { + //We ignore comment lines + std::string buf=boost::trim_left_copy(line); + if(buf.size() > 0 && buf.at(0) == '#') + { + lineCnt++; + continue; + } + + std::vector sv; + boost::split(sv,line,boost::is_any_of(",")); + + bool favourite=false; + if(sv.size() >= 4) + favourite=(sv[3]=="1")?true:false; + + bool sys=false; + if(sv.size() >= 5) + sys=(sv[4]=="1")?true:false; + + if(sv.size() >= 3) + { + std::string name=sv[0], host=sv[1], port=sv[2]; + ServerItem* item=0; + try + { + item=add(name,host,port,favourite,false); + UI_ASSERT(item != 0,"name=" << name << " host=" << host << " port=" << port); + item->setSystem(sys); + } + catch(std::exception& e) + { + std::string err=e.what(); + err+=" [name=" + name + ",host=" + host + ",port=" + port + "]"; + errStr+=err + " (in line " + UiLog::toString(lineCnt) + ")
    "; + UiLog().err() << " " << err << " (in line " << lineCnt << ")"; + } + } + + lineCnt++; + } + + in.close(); + + if(!errStr.empty()) + { + errStr="Could not parse the server list file " + localFile_ + ". The \ + following errors occured:

    " + + errStr + "
    Please correct the errors in the server list file and restart ecFlowUI!"; + UserMessage::setEchoToCout(false); + UserMessage::message(UserMessage::ERROR,true,errStr); + UserMessage::setEchoToCout(true); + exit(1); + } + + if(count() == 0) + return false; + + return true; +} + +void ServerList::save() +{ + std::ofstream out; + out.open(localFile_.c_str()); + if(!out.good()) + return; + + out << "#Name Host Port Favourite System" << std::endl; + + for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) + { + std::string fav=((*it)->isFavourite())?"1":"0"; + std::string sys=((*it)->isSystem())?"1":"0"; + out << (*it)->name() << "," << (*it)->host() << "," << (*it)->port() << "," << fav << "," << sys << std::endl; + } + out.close(); +} + +bool ServerList::readRcFile() +{ + UiLog().dbg() << "ServerList::readRcFile -->"; + std::string path(DirectoryHandler::concatenate(DirectoryHandler::rcDir(), "servers")); + + UiLog().dbg() << " Read servers from ecflowview rcfile: " << path; + std::ifstream in(path.c_str()); + + if(in.good()) + { + std::string line; + while(getline(in,line)) + { + std::string buf=boost::trim_left_copy(line); + if(buf.size() > 0 && buf.at(0) == '#') + continue; + + std::stringstream ssdata(line); + std::vector vec; + + while(ssdata >> buf) + { + vec.push_back(buf); + } + + if(vec.size() >= 3) + { + std::string name=vec[0], host=vec[1], port=vec[2]; + try + { + add(name,host,port,false,false); + } + catch(std::exception& e) + { + std::string err=e.what(); + UiLog().err() << " Failed to read server (name=" << name << ",host=" << host << + ",port=" << port << "). " << err; + } + } + } + } + else + return false; + + in.close(); + + return true; +} + +bool ServerList::hasSystemFile() const +{ + boost::filesystem::path p(systemFile_); + return boost::filesystem::exists(p); +} + +void ServerList::syncSystemFile() +{ + UiLog().dbg() << "ServerList::syncSystemFile -->"; + + std::vector sysVec; + std::ifstream in(systemFile_.c_str()); + + syncDate_=QDateTime::currentDateTime(); + clearSyncChange(); + + if(in.good()) + { + std::string line; + while(getline(in,line)) + { + std::string buf=boost::trim_left_copy(line); + if(buf.size() >0 && buf.at(0) == '#') + continue; + + std::stringstream ssdata(line); + std::vector vec; + + while(ssdata >> buf) + { + vec.push_back(buf); + } + + if(vec.size() >= 3) + { + std::string errStr,name=vec[0], host=vec[1], port=vec[2]; + if(checkItemToAdd(name,host,port,false,errStr)) + { + sysVec.push_back(ServerListTmpItem(vec[0],vec[1],vec[2])); + } + } + } + } + else + return; + + in.close(); + +#ifdef _UI_SERVERLIST_DEBUG + for(unsigned int i=0; i < sysVec.size(); i++) + UiLog().dbg() << sysVec[i].name() << "\t" + sysVec[i].host() << "\t" + sysVec[i].port(); +#endif + + bool changed=false; + bool needBrodcast=false; + + //See what changed or was added + for(unsigned int i=0; i < sysVec.size(); i++) + { +#ifdef _UI_SERVERLIST_DEBUG + UiLog().dbg() << sysVec[i].name() << "\t" + sysVec[i].host() << "\t" + sysVec[i].port(); +#endif + ServerItem *item=0; + + //There is a server with same name, host and port as in the local list. We + //mark it as system + item=find(sysVec[i].name(),sysVec[i].host(),sysVec[i].port()); + if(item) + { + if(!item->isSystem()) + { +#ifdef _UI_SERVERLIST_DEBUG + UiLog().dbg() << " already in list (same name, host, port) -> mark as system"; +#endif + changed=true; + syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],sysVec[i], + ServerListSyncChangeItem::SetSysChange)); + item->setSystem(true); + } + continue; + } + + //There is no server with the same name in the local list + item=find(sysVec[i].name()); + if(!item) + { +#ifdef _UI_SERVERLIST_DEBUG + UiLog().dbg() << " name not in list -> import as system"; +#endif + changed=true; + std::string name=sysVec[i].name(),host=sysVec[i].host(), port=sysVec[i].port(); + try + { + item=add(name,host,port,false,false); + UI_ASSERT(item != 0,"name=" << name << " host=" << host + << " port=" << port); + item->setSystem(true); + syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],sysVec[i], + ServerListSyncChangeItem::AddedChange)); + } + catch(std::exception& e) + { + std::string err=e.what(); + UiLog().err() << " Could not sync server (name=" << name << ",host=" << host << + "port=" << port << "). " << err; + } + continue; + } + //There is a server with the same name but with different host or/and port + else + { +#ifdef _UI_SERVERLIST_DEBUG + UiLog().dbg() << " name in list with different port or/and host"; +#endif + changed=true; + needBrodcast=true; + assert(item->name() == sysVec[i].name()); + + ServerListTmpItem localTmp(item); + syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],localTmp, + ServerListSyncChangeItem::MatchChange)); + + item->reset(sysVec[i].name(),sysVec[i].host(),sysVec[i].port()); + item->setSystem(true); + broadcastChanged(); + continue; + } + } + + std::vector itemsCopy=items_; + + //See what needs to be removed + for(std::vector::const_iterator it=itemsCopy.begin(); it != itemsCopy.end(); ++it) + { + if((*it)->isSystem()) + { + bool found=false; + for(unsigned int i=0; i < sysVec.size(); i++) + { + if(sysVec[i].name() == (*it)->name()) + { + found=true; + break; + } + } + if(!found) + { + changed=true; + ServerListTmpItem localTmp(*it); + syncChange_.push_back(new ServerListSyncChangeItem(localTmp,localTmp, + ServerListSyncChangeItem::UnsetSysChange)); + //remove item + remove(*it); + } + } + } + + if(changed) + save(); + + if(needBrodcast) + broadcastChanged(); + +#ifdef _UI_SERVERLIST_DEBUG + UiLog().dbg() << "<-- syncSystemFile"; +#endif +} + +void ServerList::clearSyncChange() +{ + for(size_t i=0;i < syncChange_.size(); i++) + delete syncChange_[i]; + + syncChange_.clear(); +} + + +//=========================================================== +// Observers +//=========================================================== + +void ServerList::addObserver(ServerListObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + { + observers_.push_back(o); + } +} + +void ServerList::removeObserver(ServerListObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + { + observers_.erase(it); + } +} + +void ServerList::broadcastChanged() +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + (*it)->notifyServerListChanged(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,974 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ServerListDialog.hpp" + +#include +#include +#include +#include +#include +#include + +#include "IconProvider.hpp" +#include "ServerFilter.hpp" +#include "ServerItem.hpp" +#include "ServerList.hpp" +#include "ServerListSyncWidget.hpp" +#include "SessionHandler.hpp" +#include "VConfig.hpp" +#include "WidgetNameProvider.hpp" + +static bool firstShowSysSyncLogW=true; + +//====================================== +// +// ServerDialogChecked +// +//====================================== + +bool ServerDialogChecker::checkName(QString name,QString oriName) +{ + if(name.simplified().isEmpty()) + { + error(QObject::tr("Name cannot be empty!")); + return false; + } + else if(name.contains(",")) + { + error(QObject::tr("Name cannot contain comma character!")); + return false; + } + + if(oriName != name && ServerList::instance()->find(name.toStdString()) ) + { + error(QObject::tr("The specified server already exists! Please select a different name!")); + return false; + } + + return true; +} + +bool ServerDialogChecker::checkHost(QString host) +{ + if(host.simplified().isEmpty()) + { + error(QObject::tr("Host cannot be empty!")); + return false; + } + else if(host.contains(",")) + { + error(QObject::tr("Host cannot contain comma character!")); + return false; + } + + return true; +} + +bool ServerDialogChecker::checkPort(QString port) +{ + if(port.simplified().isEmpty()) + { + error(QObject::tr("Port cannot be empty!")); + return false; + } + else if(port.contains(",")) + { + error(QObject::tr("Port cannot contain comma character!")); + return false; + } + return true; +} + + +void ServerDialogChecker::error(QString msg) +{ + QMessageBox::critical(0,QObject::tr("Server item"),errorText_ + "
    "+ msg); +} + +//====================================== +// +// ServerAddDialog +// +//====================================== + +ServerAddDialog::ServerAddDialog(QWidget *parent) : + QDialog(parent), + ServerDialogChecker(tr("Cannot create new server!")) +{ + setupUi(this); + + //nameEdit->setText(); + //hostEdit->setText(); + //portEdit->setText(); + addToCurrentCb->setChecked(true); + + //Validators + //nameEdit->setValidator(new QRegExpValidator("")); + //hostEdit->setValidator(new QRegExpValidator("")); + portEdit->setValidator(new QIntValidator(1025,65535,this)); +} + +void ServerAddDialog::accept() +{ + QString name=nameEdit->text(); + QString host=hostEdit->text(); + QString port=portEdit->text(); + + if(!checkName(name) || !checkHost(host) || !checkPort(port)) + return; + + QDialog::accept(); +} + +QString ServerAddDialog::name() const +{ + return nameEdit->text(); +} + +QString ServerAddDialog::host() const +{ + return hostEdit->text(); +} + +QString ServerAddDialog::port() const +{ + return portEdit->text(); +} + +bool ServerAddDialog::addToView() const +{ + return addToCurrentCb->isChecked(); +} + +//====================================== +// +// ServerEditDialog +// +//====================================== + +ServerEditDialog::ServerEditDialog(QString name, QString host, QString port,bool favourite,QWidget *parent) : + QDialog(parent), + ServerDialogChecker(tr("Cannot modify server!")), + oriName_(name) +{ + setupUi(this); + + nameEdit->setText(name); + hostEdit->setText(host); + portEdit->setText(port); + favCh->setChecked(favourite); + + //Validators + //nameEdit->setValidator(new QRegExpValidator("")); + //hostEdit->setValidator(new QRegExpValidator("")); + portEdit->setValidator(new QIntValidator(1025,65535,this)); +} + +void ServerEditDialog::accept() +{ + QString name=nameEdit->text(); + QString host=hostEdit->text(); + QString port=portEdit->text(); + + if(!checkName(name,oriName_) || !checkHost(host) || !checkPort(port)) + return; + + QDialog::accept(); +} + + +QString ServerEditDialog::name() const +{ + return nameEdit->text(); +} + +QString ServerEditDialog::host() const +{ + return hostEdit->text(); +} + +QString ServerEditDialog::port() const +{ + return portEdit->text(); +} + +bool ServerEditDialog::isFavourite() const +{ + return favCh->isChecked(); +} + +//====================================== +// +// ServerListDialog +// +//====================================== + +ServerListDialog::ServerListDialog(Mode mode,ServerFilter *filter,QWidget *parent) : + QDialog(parent), + filter_(filter), + mode_(mode) +{ + setupUi(this); + + QString wt=windowTitle(); + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + sortModel_=new ServerListFilterModel(this); + model_=new ServerListModel(filter_,this); + sortModel_->setSourceModel(model_); + sortModel_->setDynamicSortFilter(true); + + serverView->setRootIsDecorated(false); + serverView->setAllColumnsShowFocus(true); + serverView->setUniformRowHeights(true); + serverView->setAlternatingRowColors(true); + serverView->setSortingEnabled(true); + serverView->setSelectionMode(QAbstractItemView::ExtendedSelection); + serverView->sortByColumn(ServerListModel::NameColumn,Qt::AscendingOrder); + + serverView->setModel(sortModel_); + + //Add context menu actions to the view + QAction* sep1=new QAction(this); + sep1->setSeparator(true); + QAction* sep2=new QAction(this); + sep2->setSeparator(true); + QAction* sep3=new QAction(this); + sep3->setSeparator(true); + + serverView->addAction(actionAdd); + serverView->addAction(sep1); + serverView->addAction(actionDuplicate); + serverView->addAction(actionEdit); + serverView->addAction(sep2); + serverView->addAction(actionDelete); + serverView->addAction(sep3); + serverView->addAction(actionFavourite); + + //Add actions for the toolbuttons + addTb->setDefaultAction(actionAdd); + deleteTb->setDefaultAction(actionDelete); + editTb->setDefaultAction(actionEdit); + duplicateTb->setDefaultAction(actionDuplicate); + //rescanTb->setDefaultAction(actionRescan); + + checkActionState(); + +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + filterLe_->setPlaceholderText(tr("Filter")); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + filterLe_->setClearButtonEnabled(true); +#endif + + connect(filterLe_,SIGNAL(textEdited(QString)), + this,SLOT(slotFilter(QString))); + + connect(filterFavTb_,SIGNAL(clicked(bool)), + this,SLOT(slotFilterFavourite(bool))); + + //Load settings + readSettings(); + + //The selection changes in the view + connect(serverView->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); + + connect(serverView,SIGNAL(clicked(QModelIndex)), + this,SLOT(slotItemClicked(QModelIndex))); + + + for(int i=0; i < model_->columnCount()-1; i++) + serverView->resizeColumnToContents(i); + +#if 0 + QFont labelF; + labelF.setBold(true); + labelF.setPointSize(labelF.pointSize()-1); + + systemListLabel->setFont(labelF); + systemListLabel->setText("" + systemListLabel->text() + ""); +#endif + systemListLabel->hide(); + + //At the moment we do not want users to sync manually. It is done automatically + //on each startup. + sysSyncTb->hide(); + + //The synclog is hidden + sysSyncLogTb->setChecked(false); + sysSyncLogW_->hide(); + if(!ServerList::instance()->hasSystemFile()) + { + sysSyncLogTb->setEnabled(false); + } + + //Assign name to each object + WidgetNameProvider::nameChildren(this); +} + +ServerListDialog::~ServerListDialog() +{ + writeSettings(); +} + +void ServerListDialog::closeEvent(QCloseEvent* event) +{ + event->accept(); + writeSettings(); + ServerList::instance()->save(); +} + +void ServerListDialog::accept() +{ + //Overwrite ServerList with the actual data + writeSettings(); + QDialog::accept(); + ServerList::instance()->save(); +} + +void ServerListDialog::reject() +{ + //Overwrite ServerList with the actual data + writeSettings(); + QDialog::reject(); + ServerList::instance()->save(); +} + +void ServerListDialog::editItem(const QModelIndex& index) +{ + if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index))) + { + if(item->isSystem()) + return; + + ServerEditDialog d(QString::fromStdString(item->name()), + QString::fromStdString(item->host()), + QString::fromStdString(item->port()), + item->isFavourite(),this); + + //The dialog checks the name, host and port! + if(d.exec()== QDialog::Accepted) + { + ServerList::instance()->reset(item,d.name().toStdString(),d.host().toStdString(),d.port().toStdString()); + + if(item->isFavourite() != d.isFavourite()) + { + ServerList::instance()->setFavourite(item,d.isFavourite()); + QModelIndex idx=sortModel_->index(index.row(),ServerListModel::FavouriteColumn); + serverView->update(idx); + } + } + } +} + +void ServerListDialog::duplicateItem(const QModelIndex& index) +{ + if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index))) + { + std::string dname=ServerList::instance()->uniqueName(item->name()); + + ServerEditDialog d(QString::fromStdString(dname), + QString::fromStdString(item->host()), + QString::fromStdString(item->port()),item->isFavourite(),this); + + //The dialog checks the name, host and port! + if(d.exec() == QDialog::Accepted) + { + model_->dataIsAboutToChange(); + ServerList::instance()->add(d.name().toStdString(),d.host().toStdString(),d.port().toStdString(),false); + model_->dataChangeFinished(); + } + } +} + +void ServerListDialog::addItem() +{ + ServerAddDialog d(this); + + //The dialog checks the name, host and port! + if(d.exec() == QDialog::Accepted) + { + model_->dataIsAboutToChange(); + ServerItem* item=0; + try { + item=ServerList::instance()->add(d.name().toStdString(),d.host().toStdString(),d.port().toStdString(),false); + } + catch(std::exception& e) + { + + } + + model_->dataChangeFinished(); + if(d.addToView() && filter_) + { + filter_->addServer(item); + } + } +} + +void ServerListDialog::removeItem(const QModelIndex& index) +{ + ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); + + if(!item) + return; + + QString str=tr("Are you sure that you want to delete server: "); + str+=QString::fromStdString(item->name()) ; + str+="?"; + str+=tr("
    It will be removed from all the existing views!"); + + QMessageBox msgBox; + msgBox.setWindowTitle(tr("Delete server")); + msgBox.setText(str); + msgBox.setIcon(QMessageBox::Warning); + msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); + msgBox.setDefaultButton(QMessageBox::Cancel); + int ret=msgBox.exec(); + + switch (ret) + { + case QMessageBox::Yes: + model_->dataIsAboutToChange(); + //It will delete item as well! + ServerList::instance()->remove(item); + model_->dataChangeFinished(); + break; + case QMessageBox::Cancel: + // Cancel was clicked + break; + default: + // should never be reached + break; + } +} + + +void ServerListDialog::setFavouriteItem(const QModelIndex& index,bool b) +{ + ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); + + if(!item) + return; + + ServerList::instance()->setFavourite(item,b); + + QModelIndex idx=sortModel_->index(index.row(),ServerListModel::FavouriteColumn); + serverView->update(idx); +} + + +void ServerListDialog::on_serverView_doubleClicked(const QModelIndex& index) +{ + int col=index.column(); + if(col == ServerListModel::NameColumn || col == ServerListModel::HostColumn || + col == ServerListModel::PortColumn) + { + editItem(index); + } +} + +void ServerListDialog::on_actionEdit_triggered() +{ + QModelIndex index=serverView->currentIndex(); + editItem(index); +} + +void ServerListDialog::on_actionAdd_triggered() +{ + addItem(); +} + +void ServerListDialog::on_actionDelete_triggered() +{ + QModelIndex index=serverView->currentIndex(); + removeItem(index); +} + +void ServerListDialog::on_actionDuplicate_triggered() +{ + QModelIndex index=serverView->currentIndex(); + duplicateItem(index); +} + +void ServerListDialog::on_actionFavourite_triggered(bool checked) +{ + QModelIndex index=serverView->currentIndex(); + setFavouriteItem(index,checked); +} + +void ServerListDialog::on_actionRescan_triggered() +{ + +} + +void ServerListDialog::on_sysSyncTb_clicked(bool) +{ +#if 0 + ServerList::instance()->syncSystemFile(); + on_sysSyncLogTb_toggled(true); +#endif +} + +void ServerListDialog::on_sysSyncLogTb_toggled(bool b) +{ + sysSyncLogW_->setVisible(b); + if(b && firstShowSysSyncLogW) + { + firstShowSysSyncLogW=false; + + //Set the initial splitter sizes + QList sList=splitter_->sizes(); + Q_ASSERT(sList.count()==2); + int h=sList[0]+sList[1]; + if(h==0) + { + sList[0]=75; + sList[1]=25; + } + else + { + sList[1]=h/2; + if(ServerList::instance()->hasSyncChange()) + { + if(h > 500) + sList[1]=250; + } + else + { + if(h > 100) + sList[1]=50; + } + sList[0]=h-sList[1]; + } + + splitter_->setSizes(sList); + } +} + +void ServerListDialog::showSysSyncLog() +{ + sysSyncLogTb->setChecked(true); +} + +void ServerListDialog::slotItemSelected(const QModelIndex& current,const QModelIndex& prev) +{ + checkActionState(); +} + +void ServerListDialog::slotItemClicked(const QModelIndex& current) +{ + if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(current))) + { + if(current.column() == ServerListModel::FavouriteColumn) + { + setFavouriteItem(current,!item->isFavourite()); + } + } + + checkActionState(); +} + +void ServerListDialog::checkActionState() +{ + QModelIndex index=serverView->currentIndex(); + + if(!index.isValid()) + { + actionEdit->setEnabled(false); + actionDuplicate->setEnabled(false); + actionDelete->setEnabled(false); + actionFavourite->setEnabled(false); + } + else + { + ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); + + assert(item); + + actionEdit->setEnabled(!item->isSystem()); + actionDuplicate->setEnabled(true); + actionDelete->setEnabled(!item->isSystem()); + actionFavourite->setEnabled(true); + actionFavourite->setChecked(item->isFavourite()); + } +} + +void ServerListDialog::slotFilter(QString txt) +{ + sortModel_->setFilterStr(txt); +} + +void ServerListDialog::slotFilterFavourite(bool b) +{ + sortModel_->setFilterFavourite(b); +} + +void ServerListDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("ServerListDialog")), + QSettings::NativeFormat); + + //We have to clear it not to remember all the previous windows + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.setValue("filterFav",filterFavTb_->isChecked()); + settings.endGroup(); +} + +void ServerListDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("ServerListDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(350,500)); + } + + if(settings.contains("filterFav")) + { + //This does not emit the clicked signal + filterFavTb_->setChecked(settings.value("filterFav").toBool()); + //so we need to do it explicitly + sortModel_->setFilterFavourite(filterFavTb_->isChecked()); + } + + settings.endGroup(); +} + +//====================================== +// +// ServerListModel +// +//====================================== + +ServerListModel::ServerListModel(ServerFilter* filter,QObject *parent) : + QAbstractItemModel(parent), + filter_(filter) +{ + int id=IconProvider::add(":/viewer/favourite.svg","favourite"); + favPix_=IconProvider::pixmap(id,12); + + id=IconProvider::add(":/viewer/favourite_empty.svg","favourite_empty"); + favEmptyPix_=IconProvider::pixmap(id,12); + + id=IconProvider::add(":/viewer/system.svg","system"); + sysPix_=IconProvider::pixmap(id,12); + + loadFont_.setBold(true); +} + +ServerListModel::~ServerListModel() +{ +} + +void ServerListModel::dataIsAboutToChange() +{ + beginResetModel(); +} + +void ServerListModel::dataChangeFinished() +{ + endResetModel(); +} + +int ServerListModel::columnCount(const QModelIndex& parent) const +{ + return 6; +} + +int ServerListModel::rowCount(const QModelIndex& parent) const +{ + if(!parent.isValid()) + return static_cast(ServerList::instance()->count()); + + return 0; +} + +QVariant ServerListModel::data(const QModelIndex& index, int role) const +{ + if(!index.isValid() || + (role != Qt::DisplayRole && role != Qt::ForegroundRole && role != Qt::DecorationRole && + role != Qt::CheckStateRole && role != Qt::UserRole && role != Qt::FontRole && + role != IconStatusRole && role != Qt::ToolTipRole)) + { + return QVariant(); + } + + ServerItem* item=ServerList::instance()->itemAt(index.row()); + + if(!item) + return QVariant(); + + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case NameColumn: return QString::fromStdString(item->name()); + case HostColumn: return QString::fromStdString(item->host()); + case PortColumn: return QString::fromStdString(item->port()); + case UseColumn: + { + int n=item->useCnt(); + if(n > 0) + return "loaded (" + QString::number(n) + ")"; + + return QVariant(); + } + default: return QVariant(); + } + } + else if (role == Qt::ForegroundRole) + { + //return (item->isSystem())?QColor(70,71,72):QVariant(); + return (item->isSystem())?QColor(67,78,109):QVariant(); + } + else if (role == Qt::DecorationRole) + { + if(index.column() == SystemColumn) + return (item->isSystem())?sysPix_:QPixmap(); + + else if(index.column() == FavouriteColumn) + return (item->isFavourite())?favPix_:favEmptyPix_; + + return QVariant(); + } + else if (role == Qt::UserRole) + { + if(index.column() == FavouriteColumn) + return item->isFavourite(); + + return QVariant(); + } + else if (role == Qt::CheckStateRole) + { + if(index.column() == LoadColumn && filter_) + return (filter_->isFiltered(item))?QVariant(Qt::Checked):QVariant(Qt::Unchecked); + + return QVariant(); + } + else if (role == Qt::FontRole) + { + if(index.column() != LoadColumn && index.column() != FavouriteColumn && + filter_ && filter_->isFiltered(item)) + return loadFont_; + + return QVariant(); + } + else if (role == IconStatusRole) + { + if(index.column() == SystemColumn) + return item->isSystem(); + + else if(index.column() == FavouriteColumn) + return item->isFavourite(); + + return QVariant(); + } + + else if(role == Qt::ToolTipRole) + { + if(item->isSystem() && (index.column() == SystemColumn || index.column() == NameColumn || + index.column() == HostColumn || index.column() == PortColumn)) + { + return "This server appears in the central system server list and its name, \ + host or port cannot be modified."; + } + + return QString(); + } + + return QVariant(); +} + +QVariant ServerListModel::headerData(int section,Qt::Orientation ori,int role) const +{ + if(ori != Qt::Horizontal) + { + return QVariant(); + } + + if(role == Qt::DisplayRole) + { + switch(section) + { + case LoadColumn: return tr("L"); + case NameColumn: return tr("Name"); + case HostColumn: return tr("Host"); + case PortColumn: return tr("Port"); + case SystemColumn: return tr("S"); + case FavouriteColumn: return tr("F"); + case UseColumn: return tr("Loaded"); + default: return QVariant(); + } + } + else if(role == Qt::ToolTipRole) + { + switch(section) + { + case LoadColumn: return tr("Indicates if the server is loaded in the current tab"); + case NameColumn: return tr("Server name is a freely customisable nickname. It is only used by the
    viewer."); + case HostColumn: return tr("Hostname of the server"); + case PortColumn: return tr("Port number of the server"); + case SystemColumn: return tr("Indicates if a server appears in the centrally maintained system server list. \ +
    The name, host and port of these server entries cannot be edited."); + case FavouriteColumn: return tr("Indicates if a server is a favourite. Only favourite and loaded servers \ + are appearing in the server list under the Servers menu in the menubar"); + case UseColumn: return tr("Indicates the number of tabs where the server is loaded."); + default: return QVariant(); + } + } + else if(role == Qt::TextAlignmentRole) + { + return Qt::AlignCenter; + } + + + + return QVariant(); +} + +bool ServerListModel::setData(const QModelIndex& idx, const QVariant & value, int role ) +{ + if(filter_ && idx.column() == LoadColumn && role == Qt::CheckStateRole) + { + if(ServerItem* item=ServerList::instance()->itemAt(idx.row())) + { + bool checked=(value.toInt() == Qt::Checked)?true:false; + if(checked) + filter_->addServer(item); + else + filter_->removeServer(item); + + QModelIndex startIdx=index(idx.row(),0); + QModelIndex endIdx=index(idx.row(),columnCount()-1); + + Q_EMIT dataChanged(startIdx,endIdx); + return true; + } + } + + return false; +} + + +QModelIndex ServerListModel::index(int row, int column, const QModelIndex& /*parent*/) const +{ + return createIndex(row,column,static_cast(0)); +} + +QModelIndex ServerListModel::parent(const QModelIndex &) const +{ + return QModelIndex(); +} + +Qt::ItemFlags ServerListModel::flags ( const QModelIndex & index) const +{ + Qt::ItemFlags defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + if(filter_ && index.column() == LoadColumn) + { + defaultFlags=defaultFlags | Qt::ItemIsUserCheckable; + } + + return defaultFlags; +} + +ServerItem* ServerListModel::indexToServer(const QModelIndex& index) +{ + return ServerList::instance()->itemAt(index.row()); +} + +//====================================== +// +// ServerListFilterModel +// +//====================================== + +ServerListFilterModel::ServerListFilterModel(QObject *parent) : + QSortFilterProxyModel(parent), + filterFavourite_(false) +{ + +} + +void ServerListFilterModel::setFilterStr(QString t) +{ + QString newStr=t.simplified(); + if(newStr != filterStr_) + { + filterStr_=newStr; + invalidateFilter(); + } +} + +void ServerListFilterModel::setFilterFavourite(bool b) +{ + if(b != filterFavourite_) + { + filterFavourite_=b; + invalidateFilter(); + } +} + + +bool ServerListFilterModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const +{ + if(filterFavourite_) + { + QModelIndex idxLoad = sourceModel()->index(sourceRow, ServerListModel::LoadColumn, sourceParent); + QModelIndex idxFav = sourceModel()->index(sourceRow, ServerListModel::FavouriteColumn, sourceParent); + if(!sourceModel()->data(idxFav,Qt::UserRole).toBool() && + sourceModel()->data(idxLoad,Qt::CheckStateRole).toInt() != Qt::Checked) + return false; + } + + if(filterStr_.isEmpty()) + return true; + + QModelIndex idxName = sourceModel()->index(sourceRow,ServerListModel::NameColumn, sourceParent); + QModelIndex idxHost= sourceModel()->index(sourceRow,ServerListModel::HostColumn, sourceParent); + + if(sourceModel()->data(idxName).toString().contains(filterStr_,Qt::CaseInsensitive) || + sourceModel()->data(idxHost).toString().contains(filterStr_,Qt::CaseInsensitive)) + return true; + + return false; + +} + +bool ServerListFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const +{ + if(left.column() == ServerListModel::SystemColumn || left.column() == ServerListModel::FavouriteColumn) + { + return left.data(ServerListModel::IconStatusRole).toBool() < + right.data(ServerListModel::IconStatusRole).toBool(); + } + + return QSortFilterProxyModel::lessThan(left,right); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,175 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERLISTDIALOG_HPP_ +#define SERVERLISTDIALOG_HPP_ + +#include +#include + +#include "ui_ServerListDialog.h" +#include "ui_ServerEditDialog.h" +#include "ui_ServerAddDialog.h" + +class ServerFilter; +class ServerItem; +class ServerListModel; +class ServerListFilterModel; + +class ServerDialogChecker +{ +protected: + explicit ServerDialogChecker(QString txt) : errorText_(txt) {} + + bool checkName(QString name,QString oriName=QString()); + bool checkHost(QString host); + bool checkPort(QString port); + void error(QString msg); + + QString errorText_; +}; + + +class ServerEditDialog : public QDialog, private Ui::ServerEditDialog, public ServerDialogChecker +{ +Q_OBJECT + +public: + ServerEditDialog(QString name,QString host, QString port,bool favourite,QWidget* parent=0); + + QString name() const; + QString host() const; + QString port() const; + bool isFavourite() const; + +public Q_SLOTS: + void accept(); + +private: + QString oriName_; + +}; + +class ServerAddDialog : public QDialog, private Ui::ServerAddDialog, public ServerDialogChecker +{ +Q_OBJECT + +public: + explicit ServerAddDialog(QWidget* parent=0); + + QString name() const; + QString host() const; + QString port() const; + bool addToView() const; + +public Q_SLOTS: + void accept(); +}; + + +class ServerListDialog : public QDialog, protected Ui::ServerListDialog +{ +Q_OBJECT + +public: + enum Mode {SelectionMode,ManageMode}; + + ServerListDialog(Mode,ServerFilter*,QWidget *parent=0); + ~ServerListDialog(); + + void showSysSyncLog(); + +public Q_SLOTS: + void accept(); + void reject(); + +protected Q_SLOTS: + void on_actionEdit_triggered(); + void on_actionAdd_triggered(); + void on_actionDuplicate_triggered(); + void on_actionDelete_triggered(); + void on_actionRescan_triggered(); + void on_serverView_doubleClicked(const QModelIndex& index); + void on_actionFavourite_triggered(bool checked); + void on_sysSyncTb_clicked(bool); + void on_sysSyncLogTb_toggled(bool); + void slotItemSelected(const QModelIndex&,const QModelIndex&); + void slotItemClicked(const QModelIndex&); + void slotFilter(QString); + void slotFilterFavourite(bool); + +protected: + void closeEvent(QCloseEvent*); + void editItem(const QModelIndex& index); + void duplicateItem(const QModelIndex& index); + void addItem(); + void removeItem(const QModelIndex& index); + void setFavouriteItem(const QModelIndex& index,bool b); + void checkActionState(); + void writeSettings(); + void readSettings(); + + ServerFilter* filter_; + ServerListModel* model_; + ServerListFilterModel* sortModel_; + Mode mode_; +}; + + +class ServerListModel : public QAbstractItemModel +{ +public: + explicit ServerListModel(ServerFilter*,QObject *parent=0); + ~ServerListModel(); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + bool setData( const QModelIndex &, const QVariant &, int role = Qt::EditRole ); + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + Qt::ItemFlags flags ( const QModelIndex & index) const; + + void dataIsAboutToChange(); + void dataChangeFinished(); + ServerItem* indexToServer(const QModelIndex& index); + + enum Columns {LoadColumn=0, NameColumn=1, HostColumn=2, PortColumn=3, SystemColumn=4, FavouriteColumn=5, UseColumn=6}; + enum CustomItemRole {IconStatusRole = Qt::UserRole+1}; + +protected: + ServerFilter* filter_; + QPixmap favPix_; + QPixmap favEmptyPix_; + QPixmap sysPix_; + QFont loadFont_; +}; + +class ServerListFilterModel : public QSortFilterProxyModel +{ +public: + explicit ServerListFilterModel(QObject *parent=0); + ~ServerListFilterModel() {} + void setFilterStr(QString); + void setFilterFavourite(bool b); + +protected: + bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const; + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + + QString filterStr_; + bool filterFavourite_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,391 @@ + + + ServerListDialog + + + + 0 + 0 + 599 + 661 + + + + Manage servers + + + + + + + + + + + + + Show favourite and loaded servers only + + + Show favourite servers only + + + Favourites and loaded + + + + :/viewer/favourite.svg:/viewer/favourite.svg + + + true + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Show change log for <b>system servers</b> + + + + + + + :/viewer/info.svg:/viewer/info.svg + + + true + + + Qt::ToolButtonIconOnly + + + false + + + + + + + + + + + + 0 + 0 + + + + &Edit server + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + &Add server + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + + 0 + 0 + + + + &Duplicate server + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + &Remove server + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + System servers: + + + + + + + + 0 + 0 + + + + &Update from central list + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Vertical + + + + Qt::ActionsContextMenu + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + :/viewer/images/add.svg:/viewer/images/add.svg + + + &Add server + + + Add server + + + Ctrl+N + + + + + + :/viewer/close.svg:/viewer/close.svg + + + &Remove server + + + Delete server + + + Del + + + + + + :/viewer/images/configure.svg:/viewer/images/configure.svg + + + &Edit server + + + Edit server properties + + + Ctrl+E + + + + + + :/viewer/images/reload.svg:/viewer/images/reload.svg + + + Rescan + + + Rescan network for servers + + + + + Du&plicate server + + + Duplicate server + + + Ctrl+D + + + + + true + + + + :/viewer/favourite.svg:/viewer/favourite.svg + + + Set as &favourite + + + Ctrl+B + + + buttonBox + + + + + ServerListSyncWidget + QWidget +
    ServerListSyncWidget.hpp
    + 1 +
    +
    + + + + buttonBox + accepted() + ServerListDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + ServerListDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerList.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerList.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerList.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerList.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,123 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVERLIST_HPP_ +#define SERVERLIST_HPP_ + +#include +#include + +#include + +class ServerItem; +class ServerList; + + +class ServerListObserver +{ +public: + ServerListObserver() {} + virtual ~ServerListObserver() {} + virtual void notifyServerListChanged()=0; + virtual void notifyServerListFavouriteChanged(ServerItem*)=0; +}; + +class ServerListTmpItem +{ +public: + ServerListTmpItem() {} + ServerListTmpItem(const std::string& name,const std::string& host, const std::string& port) : + name_(name), host_(host), port_(port) {} + explicit ServerListTmpItem(ServerItem* item); + ServerListTmpItem(const ServerListTmpItem& o) : name_(o.name_), host_(o.host_), port_(o.port_) {} + + const std::string& name() const {return name_;} + const std::string& host() const {return host_;} + const std::string& port() const {return port_;} + +protected: + std::string name_; + std::string host_; + std::string port_; +}; + +class ServerListSyncChangeItem +{ +public: + enum ChangeType {AddedChange,RemovedChange, MatchChange, SetSysChange,UnsetSysChange}; + + ServerListSyncChangeItem(const ServerListTmpItem& sys,const ServerListTmpItem& local,ChangeType type) : + sys_(sys), local_(local), type_(type) {} + + const ServerListTmpItem& sys() const {return sys_;} + const ServerListTmpItem& local() const {return local_;} + ChangeType type() const {return type_;} + + ServerListTmpItem sys_; + ServerListTmpItem local_; + ChangeType type_; +}; + +class ServerList +{ +public: + int count() const {return static_cast(items_.size());} + ServerItem* itemAt(int); + ServerItem* find(const std::string& name); + ServerItem* find(const std::string& name, const std::string& host, const std::string& port); + + //Can be added or changed only via these static methods + ServerItem* add(const std::string&,const std::string&,const std::string&,bool,bool saveIt=true); + void remove(ServerItem*); + void reset(ServerItem*,const std::string& name,const std::string& host,const std::string& port); + void setFavourite(ServerItem*,bool); + + std::string uniqueName(const std::string&); + + void init(); + void save(); + void rescan(); + void syncSystemFile(); + bool hasSystemFile() const; + const std::vector& syncChange() const {return syncChange_;} + bool hasSyncChange() const {return !syncChange_.empty();} + QDateTime syncDate() const {return syncDate_;} + + void addObserver(ServerListObserver*); + void removeObserver(ServerListObserver*); + + static ServerList* instance(); + +protected: + ServerList() {} + ~ServerList() {} + + static ServerList* instance_; + + bool load(); + bool readRcFile(); + //bool readSystemFile(); + void clearSyncChange(); + bool checkItemToAdd(const std::string& name,const std::string& host,const std::string& port, + bool checkDuplicate,std::string& errStr); + + void broadcastChanged(); + void broadcastChanged(ServerItem*); + + std::vector items_; + std::string localFile_; + std::string systemFile_; + std::vector observers_; + std::vector syncChange_; + QDateTime syncDate_; +}; + + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,233 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ServerListSyncWidget.hpp" + +#include + +#include "ServerFilter.hpp" +#include "ServerItem.hpp" +#include "ServerList.hpp" + +ServerListSyncWidget::ServerListSyncWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + + title_->setShowTypeTitle(false); + browser_->setProperty("log","1"); + + //Set the list + if(ServerList::instance()->syncDate().isValid()) + { + QColor col(39,49,101); + + if(ServerList::instance()->hasSyncChange()) + { + QString t="Your copy of the system server list was updated "; + t+=" at " + + ServerList::instance()->syncDate().toString("yyyy-MM-dd HH:mm") + + ". This is the list of changes:"; + + title_->showInfo(t); + + const std::vector& items=ServerList::instance()->syncChange(); + int matchCnt=0, addedCnt=0, setCnt=0, unsetCnt=0; + for(size_t i=0; i < items.size(); i++) + { + if(items[i]->type() == ServerListSyncChangeItem::MatchChange) + matchCnt++; + else if(items[i]->type() == ServerListSyncChangeItem::AddedChange) + addedCnt++; + else if(items[i]->type() == ServerListSyncChangeItem::SetSysChange) + setCnt++; + else if(items[i]->type() == ServerListSyncChangeItem::UnsetSysChange) + unsetCnt++; + } + + if(matchCnt > 0) + { + QListWidgetItem *item=new QListWidgetItem("Host/port changed (" + QString::number(matchCnt) + ")"); + item->setData(Qt::UserRole,ServerListSyncChangeItem::MatchChange); + typeList_->addItem(item); + } + if(unsetCnt > 0) + { + QListWidgetItem *item=new QListWidgetItem("Server removed (" + QString::number(unsetCnt) + ")"); + item->setData(Qt::UserRole,ServerListSyncChangeItem::UnsetSysChange); + typeList_->addItem(item); + } + if(addedCnt > 0) + { + QListWidgetItem *item=new QListWidgetItem("New server (" + QString::number(addedCnt) + ")"); + item->setData(Qt::UserRole,ServerListSyncChangeItem::AddedChange); + typeList_->addItem(item); + } + if(setCnt > 0) + { + QListWidgetItem *item=new QListWidgetItem("Marked as system (" + QString::number(setCnt) + ")"); + item->setData(Qt::UserRole,ServerListSyncChangeItem::SetSysChange); + typeList_->addItem(item); + } + + QFont f; + QFontMetrics fm(f); + typeList_->setFixedWidth(fm.width("Host/port changed (2222)")); + + connect(typeList_,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + this,SLOT(slotTypeChanged(QListWidgetItem*,QListWidgetItem*))); + + typeList_->setCurrentRow(0); + } + else + { + QString t="Your copy of the system server list was updated "; + t+=" at " + + ServerList::instance()->syncDate().toString("yyyy-MM-dd HH:mm") + + " but no changes were found."; + title_->showInfo(t); + typeList_->hide(); + browser_->hide(); + } + } + else + { + QString t="Your copy of the system server list has not been updated!"; + title_->showInfo(t); + typeList_->hide(); + browser_->hide(); + } +} + +ServerListSyncWidget::~ServerListSyncWidget() +{ +} + +void ServerListSyncWidget::slotTypeChanged(QListWidgetItem* item,QListWidgetItem*) +{ + if(!item) + return; + + const std::vector& items=ServerList::instance()->syncChange(); + ServerListSyncChangeItem::ChangeType type= + static_cast(item->data(Qt::UserRole).toInt()); + + QString s; + + for(size_t i=0; i < items.size(); i++) + { + if(items[i]->type() == type) + { + if(!s.isEmpty()) + s+="
    "; + + switch(type) + { + case ServerListSyncChangeItem::MatchChange: + s+=buildMatchChange(items[i]); + break; + case ServerListSyncChangeItem::UnsetSysChange: + s+=buildUnsetSysChange(items[i]); + break; + case ServerListSyncChangeItem::AddedChange: + s+=buildAddedChange(items[i]); + break; + case ServerListSyncChangeItem::SetSysChange: + s+=buildSetSysChange(items[i]); + break; + default: + break; + } + } + } + + browser_->setHtml(s); +} + +QString ServerListSyncWidget::buildMatchChange(ServerListSyncChangeItem *t) +{ + QString hostTxt=QString::fromStdString(t->local().host()); + QString portTxt=QString::fromStdString(t->local().port()); + + QColor col(0,80,50); + + if(t->sys().host() != t->local().host()) + { + hostTxt+=" -> " + QString::fromStdString(t->sys().host()); + hostTxt="" + hostTxt + ""; + } + if(t->sys().port() != t->local().port()) + { + portTxt+=" -> " + QString::fromStdString(t->sys().port()); + portTxt="" + portTxt + ""; + } + + QString s=buildTable(QString::fromStdString(t->local().name()),hostTxt,portTxt); + return s; +} + +QString ServerListSyncWidget::buildAddedChange(ServerListSyncChangeItem *t) +{ +#if 0 + QString s="New server added"; + s+="" + buildTable(QString::fromStdString(t->sys().name()), + QString::fromStdString(t->sys().host()), + QString::fromStdString(t->sys().port())) + ""; + +#endif + + QString s=buildTable(QString::fromStdString(t->sys().name()), + QString::fromStdString(t->sys().host()), + QString::fromStdString(t->sys().port())); + return s; +} + + +QString ServerListSyncWidget::buildSetSysChange(ServerListSyncChangeItem *t) +{ +#if 0 + QString s="Existing server marked as system"; + s+="" + buildTable(QString::fromStdString(t->sys().name()), + QString::fromStdString(t->sys().host()), + QString::fromStdString(t->sys().port())) + ""; +#endif + + QString s=buildTable(QString::fromStdString(t->sys().name()), + QString::fromStdString(t->sys().host()), + QString::fromStdString(t->sys().port())); + return s; +} + +QString ServerListSyncWidget::buildUnsetSysChange(ServerListSyncChangeItem *t) +{ +#if 0 + QString s="Existing server unmarked as system"; + s+="" + buildTable(QString::fromStdString(t->local().name()), + QString::fromStdString(t->local().host()), + QString::fromStdString(t->local().port())) + ""; +#endif + QString s=buildTable(QString::fromStdString(t->local().name()), + QString::fromStdString(t->local().host()), + QString::fromStdString(t->local().port())); + + return s; +} + + + +QString ServerListSyncWidget::buildTable(QString name,QString host,QString port) const +{ + QColor col(60,60,60); + return "" + + "" + + "" + + "" + + "" + + "
     Name:" + name + "
     Host:" + host + "
     Port:" + port + "
    "; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,43 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERLISTSYNCWIDGET_HPP +#define SERVERLISTSYNCWIDGET_HPP + +#include + +#include + +#include "ui_ServerListSyncWidget.h" + +class ServerListSyncChangeItem; +class QListWidgetItem; + +class ServerListSyncWidget : public QWidget, protected Ui::ServerListSyncWidget +{ + Q_OBJECT + +public: + ServerListSyncWidget(QWidget *parent = 0); + ~ServerListSyncWidget(); + +protected Q_SLOTS: + void slotTypeChanged(QListWidgetItem* item,QListWidgetItem*); + +private: + void build(); + QString buildAddedChange(ServerListSyncChangeItem*); + QString buildMatchChange(ServerListSyncChangeItem*); + QString buildSetSysChange(ServerListSyncChangeItem *t); + QString buildUnsetSysChange(ServerListSyncChangeItem *t); + QString buildTable(QString name,QString host,QString port) const; +}; + +#endif // SERVERLISTSYNCWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerListSyncWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerListSyncWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,61 @@ + + + ServerListSyncWidget + + + + 0 + 0 + 640 + 401 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + + + + + + + + + + + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    +
    + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,126 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerLoadItemWidget.hpp" + +#include + +#include "Node.hpp" + +#include "InfoProvider.hpp" + +#ifdef ECFLOW_LOGVIEW +#include "LogLoadWidget.hpp" +#endif + +#include "MessageLabel.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "VNode.hpp" +#include "VNState.hpp" + +ServerLoadItemWidget::ServerLoadItemWidget(QWidget *parent) +{ + QVBoxLayout* vb=new QVBoxLayout(this); + vb->setContentsMargins(0,0,0,0); +#ifdef ECFLOW_LOGVIEW + w_=new LogLoadWidget(this); + vb->addWidget(w_); +#else + w_=new MessageLabel(this); + w_->showWarning("The server load view is only avialable when ecFlowUI is built with QtCharts."); + vb->addWidget(w_); + vb->addStretch(1); +#endif + + //This tab is always visible whatever node is selected!!! + //We keep the data unchanged unless a new server is selected + keepServerDataOnLoad_=true; + + //We will KEEP the contents when the item (aka tab) becomes unselected + //unselectedFlags_.clear(); +} + +ServerLoadItemWidget::~ServerLoadItemWidget() +{ + clearContents(); +} + +QWidget* ServerLoadItemWidget::realWidget() +{ + return this; +} + +void ServerLoadItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + bool same=hasSameContents(info); + + //set the info. We do not need to observe the node!!! + info_=info; + + if(!same) + load(); +} + +void ServerLoadItemWidget::load() +{ +#ifdef ECFLOW_LOGVIEW + if(info_ && info_->server()) + { + ServerHandler *sh=info_->server(); + Q_ASSERT(sh); + QString logFile; + if(VServer* vs=sh->vRoot()) + { + logFile=QString::fromStdString(vs->findVariable("ECF_LOG",false)); + + w_->load(QString::fromStdString(sh->name()), + QString::fromStdString(sh->host()), + QString::fromStdString(sh->port()), + logFile,-50000); //last 50000 rows are read + } + } +#endif +} + +void ServerLoadItemWidget::clearContents() +{ +#ifdef ECFLOW_LOGVIEW + w_->clear(); +#endif + InfoPanelItem::clear(); +} + +//We are independent of the server's state +void ServerLoadItemWidget::updateState(const FlagSet& flags) +{ +} + +bool ServerLoadItemWidget::hasSameContents(VInfo_ptr info) +{ + if(info && info_ && info->server()) + { + return info->server() == info_->server(); + } + return false; +} + +//We are independent of the server's state +void ServerLoadItemWidget::serverSyncFinished() +{ +} + +static InfoPanelItemMaker maker1("server_load"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,52 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERLOADITEMWIDGET_HPP +#define SERVERLOADITEMWIDGET_HPP + +#include + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" + +class VNode; +class LogLoadWidget; +class MessageLabel; + +class ServerLoadItemWidget : public QWidget, public InfoPanelItem +{ +public: + explicit ServerLoadItemWidget(QWidget *parent=0); + ~ServerLoadItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + bool hasSameContents(VInfo_ptr info); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected: + void updateState(const ChangeFlags&); + void serverSyncFinished(); + +private: + void load(); +#ifdef ECFLOW_LOGVIEW + LogLoadWidget* w_; +#else + MessageLabel* w_; +#endif +}; + +#endif // SERVERLOADITEMWIDGET_HPP + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,179 @@ + + + ServerLoadItemWidget + + + + 0 + 0 + 810 + 488 + + + + + 0 + 0 + + + + Form + + + + + + + + + + + Show log + + + + + + + Modes + + + + + + + + + Qt::Horizontal + + + + Qt::ActionsContextMenu + + + true + + + QAbstractItemView::ExtendedSelection + + + false + + + true + + + false + + + true + + + true + + + false + + + + + + + + + + + + + + + Resolution: + + + + + + + + + + + + news + + + + + + + + + + + + + + + + + :/viewer/terminate.svg:/viewer/terminate.svg + + + &Terminate + + + + + + :/viewer/rescue.svg:/viewer/rescue.svg + + + &Rescue + + + + + &Fob off + + + + + + :/viewer/close.svg:/viewer/close.svg + + + &Delete + + + + + &Kill + + + + + &Look up node in tree + + + Look up node in tree + + + + + + TreeView + QTreeView +
    TreeView.hpp
    +
    + + ServerLoadView + QWidget +
    ServerLoadView.hpp
    + 1 +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadProvider.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadProvider.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadProvider.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadProvider.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,140 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include + +#include +#include +#include +#include "ServerLoadProvider.hpp" + +#include "VNode.hpp" +#include "VReply.hpp" +#include "ServerHandler.hpp" +#include "File.hpp" +#include "File.hpp" +#include "Str.hpp" + +ServerLoadProvider::ServerLoadProvider(InfoPresenter* owner) : + InfoProvider(owner,VTask::HistoryTask) +{ + +} + +void ServerLoadProvider::clear() +{ + InfoProvider::clear(); +} + +void ServerLoadProvider::setAutoUpdate(bool autoUpdate) +{ + InfoProvider::setAutoUpdate(autoUpdate); + + if(active_) + { + if(!autoUpdate_) + { + //stopWatchFile(); + } + else + { + if(!inAutoUpdate_) + fetchFile(); + } + } + else + { + //stopWatchFile(); + } +} + +void ServerLoadProvider::visit(VInfoServer* info) +{ + fetchFile(); +} + +void ServerLoadProvider::fetchFile() +{ + if(!active_) + return; + + //stopWatchFile(); + + //Reset the reply + reply_->reset(); + + if(!info_) + { + owner_->infoFailed(reply_); + return; + } + + ServerHandler* server=info_->server(); + + //Get the filename + std::string fileName=server->vRoot()->genVariable("ECF_LOG"); + + fetchFile(server,fileName); +} + +void ServerLoadProvider::fetchFile(ServerHandler *server,const std::string& fileName) +{ + if(!server) + { + owner_->infoFailed(reply_); + return; + } + + //Set the filename in reply + reply_->fileName(fileName); + + //No filename is available + if(fileName.empty()) + { + reply_->setErrorText("Variable ECF_LOG is not defined!"); + owner_->infoFailed(reply_); + } + + //First we try to read the file directly from the disk + //if(server->readFromDisk()) + { + size_t file_size = 0; + std::string err_msg; + reply_->text( ecf::File::get_last_n_lines(fileName,100,file_size,err_msg)); + if(err_msg.empty()) + { + reply_->fileReadMode(VReply::LocalReadMode); + + if(autoUpdate_) + inAutoUpdate_=true; + + owner_->infoReady(reply_); + + //Try to track the changes in the log file + watchFile(fileName,file_size); + return; + } + } + + //Finally we try the server + reply_->fileReadMode(VReply::ServerReadMode); + + //Define a task for getting the info from the server. + task_=VTask::create(taskType_,server->vRoot(),this); + + //Run the task in the server. When it finish taskFinished() is called. The text returned + //in the reply will be prepended to the string we generated above. + server->run(task_); + +#if 0 + //If we are we could not get the file + //owner_->infoFailed(reply_); +#endif +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadProvider.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadProvider.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerLoadProvider.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerLoadProvider.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,33 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERLOADPROVIDER_HPP +#define SERVERLOADPROVIDER_HPP + +#include "VInfo.hpp" +#include "InfoProvider.hpp" + +class FileWatcher; + +class ServerLoadProvider : public InfoProvider +{ +public: + ServerLoadProvider(InfoPresenter* owner); + + void visit(VInfoServer*); + void clear(); + void setAutoUpdate(bool); + +private: + void fetchFile(); + void fetchFile(ServerHandler *server,const std::string& fileName); +}; + +#endif // SERVERLOADPROVIDER_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerObserver.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerObserver.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerObserver.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerObserver.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SERVEROBSERVER_HPP_ +#define SERVEROBSERVER_HPP_ + +#include +#include "Aspect.hpp" + +class ServerHandler; +class VServerChange; + +class ServerObserver +{ +public: + ServerObserver() {} + virtual ~ServerObserver() {} + virtual void notifyDefsChanged(ServerHandler* server, const std::vector& a)=0; + virtual void notifyServerDelete(ServerHandler* server)=0; + virtual void notifyBeginServerClear(ServerHandler* server) {} + virtual void notifyEndServerClear(ServerHandler* server) {} + virtual void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + virtual void notifyEndServerScan(ServerHandler* server) {} + virtual void notifyServerConnectState(ServerHandler* server) {} + virtual void notifyServerActivityChanged(ServerHandler* server) {} + virtual void notifyServerSuiteFilterChanged(ServerHandler* server) {} + virtual void notifyEndServerSync(ServerHandler* server) {} +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,133 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ServerSettingsItemWidget.hpp" + +#include "Node.hpp" +#include "ServerHandler.hpp" +#include "VNode.hpp" + +#include + +#include + +//======================================================== +// +// ServerSettingsItemWidget +// +//======================================================== + +ServerSettingsItemWidget::ServerSettingsItemWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + + connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), + this,SLOT(slotClicked(QAbstractButton *))); + + QPushButton* applyPb=buttonBox_->button(QDialogButtonBox::Apply); + assert(applyPb); + applyPb->setEnabled(false); + + connect(editor_,SIGNAL(changed()), + this,SLOT(slotEditorChanged())); + + //This tab is always visible whatever node is selected!!! + //We keep the data unchanged unless a new server is selected + keepServerDataOnLoad_=true; +} + +QWidget* ServerSettingsItemWidget::realWidget() +{ + return this; +} + +void ServerSettingsItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + clearContents(); + + info_=info; + + if(info_ && info_->server()) + { + editor_->edit(info_->server()->conf()->guiProp(), + QString::fromStdString(info_->server()->name())); + } + else + { + + } +} + +void ServerSettingsItemWidget::clearContents() +{ + InfoPanelItem::clear(); + //TODO: properly set gui state +} + +void ServerSettingsItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(ActiveChanged)) + { + if(active_) + { + editor_->setEnabled(true); + buttonBox_->setEnabled(true); + } + } + + if(flags.isSet(SuspendedChanged)) + { + if(active_) + { + if(suspended_) + { + editor_->setEnabled(false); + buttonBox_->setEnabled(false); + } + else + { + editor_->setEnabled(true); + buttonBox_->setEnabled(true); + } + } + } + +} + +void ServerSettingsItemWidget::slotEditorChanged() +{ + QPushButton* applyPb=buttonBox_->button(QDialogButtonBox::Apply); + assert(applyPb); + applyPb->setEnabled(true); +} + +void ServerSettingsItemWidget::slotClicked(QAbstractButton* button) +{ + if(!active_) + return; + + switch(buttonBox_->standardButton(button)) + { + case QDialogButtonBox::Apply: + { + if(editor_->applyChange()) + { + if(info_ && info_->server()) + info_->server()->conf()->saveSettings(); + } + } + break; + default: + break; + } +} + +static InfoPanelItemMaker maker1("server_settings"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,49 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SERVERSETTINGSITEMWIDGET_HPP_ +#define SERVERSETTINGSITEMWIDGET_HPP_ + +#include + +#include "InfoPanelItem.hpp" +#include "PropertyEditor.hpp" +#include "VInfo.hpp" + +#include "ui_ServerSettingsItemWidget.h" + +class QAbstractButton; + +class VNode; + +class ServerSettingsItemWidget : public QWidget, public InfoPanelItem, protected Ui::ServerSettingsItemWidget +{ +Q_OBJECT + +public: + explicit ServerSettingsItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + +protected Q_SLOTS: + void slotClicked(QAbstractButton* button); + void slotEditorChanged(); + +protected: + void updateState(const ChangeFlags&); +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/ServerSettingsItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ServerSettingsItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,45 @@ + + + ServerSettingsItemWidget + + + + 0 + 0 + 579 + 594 + + + + Form + + + + + + + + + Qt::Vertical + + + QDialogButtonBox::Apply|QDialogButtonBox::Reset + + + false + + + + + + + + PropertyEditor + QWidget +
    PropertyEditor.hpp
    + 1 +
    +
    + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,263 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include + + +#include "SessionDialog.hpp" +#include "ui_SessionDialog.h" + +#include "DirectoryHandler.hpp" +#include "SessionRenameDialog.hpp" + + +SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) +{ + //ui->setupUi(this); + setupUi(this); + + refreshListOfSavedSessions(); + + + // what was saved last time? + std::string lastSessionName = SessionHandler::instance()->lastSessionName(); + int index = SessionHandler::instance()->indexFromName(lastSessionName); + if (index != -1) + savedSessionsList_->setCurrentRow(index); // select this one in the table + + + if (SessionHandler::instance()->loadLastSessionAtStartup()) + restoreLastSessionCb_->setCheckState(Qt::Checked); + else + restoreLastSessionCb_->setCheckState(Qt::Unchecked); + + + newButton_->setVisible(false); // XXX TODO: enable New Session functionality + + // ensure the correct state of the Save button + on_sessionNameEdit__textChanged(); + setButtonsEnabledStatus(); +} + +SessionDialog::~SessionDialog() +{ + //delete ui; +} + +void SessionDialog::refreshListOfSavedSessions() +{ + + //sessionsTable_->clearContents(); + savedSessionsList_->clear(); + + // get the list of existing sessions + int numSessions = SessionHandler::instance()->numSessions(); + for (int i = 0; i < numSessions; i++) + { + SessionItem *s = SessionHandler::instance()->sessionFromIndex(i); + addSessionToTable(s); + } +} + +void SessionDialog::addSessionToTable(SessionItem *s) +{ + QListWidgetItem *item = new QListWidgetItem(QString::fromStdString(s->name())); + //item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); + //item->setCheckState(Qt::Unchecked); + savedSessionsList_->addItem(item); +/* + int lastRow = sessionsTable_->rowCount()-1; + sessionsTable_->insertRow(lastRow+1); + + QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(s->name())); + sessionsTable_->setItem(lastRow+1, 0, nameItem); +*/ +} + + +std::string SessionDialog::selectedSessionName() +{ + QListWidgetItem *ci = savedSessionsList_->currentItem(); + if (ci) + return ci->text().toStdString(); + else + return ""; +} + + + +// --------------------------------------------------------------------------------------------- +// setButtonsEnabledStatus +// - checks which session has been chosen and enables/disables the action buttons appropriately +// --------------------------------------------------------------------------------------------- + +void SessionDialog::setButtonsEnabledStatus() +{ + std::string name = selectedSessionName(); + + // if somehow no session is selected, then we need different logic + if (name.empty()) + { + cloneButton_ ->setEnabled(false); + deleteButton_ ->setEnabled(false); + renameButton_ ->setEnabled(false); + switchToButton_->setEnabled(false); + } + else + { + // the default session is special and cannot be deleted or renamed + bool enable = (name == "default") ? false : true; + deleteButton_ ->setEnabled(enable); + renameButton_ ->setEnabled(enable); + switchToButton_->setEnabled(true); // always available for a valid session + cloneButton_ ->setEnabled(true); + } +} + + +bool SessionDialog::validSaveName(const std::string &name) +{ +/* + QString boxName(QObject::tr("Save session")); + // name empty? + if (name.empty()) + { + QMessageBox::critical(0,boxName, tr("Please enter a name for the session")); + return false; + } + + + // is there already a session with this name? + bool sessionWithThisName = (SessionHandler::instance()->find(name) != NULL); + + if (sessionWithThisName) + { + QMessageBox::critical(0,boxName, tr("A session with that name already exists - please choose another name")); + return false; + } + else + { + return true; + }*/ + return true; +} + +void SessionDialog::on_sessionNameEdit__textChanged() +{ + // only allow to save a non-empty session name +// saveButton_->setEnabled(!sessionNameEdit_->text().isEmpty()); +} + +void SessionDialog::on_savedSessionsList__currentRowChanged(int currentRow) +{ + setButtonsEnabledStatus(); +} + +void SessionDialog::on_cloneButton__clicked() +{ + std::string sessionName = selectedSessionName(); + assert(!sessionName.empty()); // it should not be possible for the name to be empty + + SessionRenameDialog renameDialog; + renameDialog.exec(); + + int result = renameDialog.result(); + if (result == QDialog::Accepted) + { + std::string newName = renameDialog.newName(); + SessionHandler::instance()->copySession(sessionName, newName); + refreshListOfSavedSessions(); + } +} + +void SessionDialog::on_deleteButton__clicked() +{ + std::string sessionName = selectedSessionName(); + assert(!sessionName.empty()); // it should not be possible for the name to be empty + + QString message = tr("Are you sure that you want to delete the session '") + QString::fromStdString(sessionName) + tr("'' from disk?"); + if(QMessageBox::question(0,tr("Confirm: remove session"), + message, + QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Ok) + { + SessionHandler::instance()->remove(sessionName); + refreshListOfSavedSessions(); + } +} + + +void SessionDialog::on_renameButton__clicked() +{ + std::string sessionName = selectedSessionName(); + assert(!sessionName.empty()); // it should not be possible for the name to be empty + + SessionItem *item = SessionHandler::instance()->find(sessionName); + assert(item); // it should not be possible for the name to be empty + + SessionRenameDialog renameDialog; + renameDialog.exec(); + + int result = renameDialog.result(); + if (result == QDialog::Accepted) + { + std::string newName = renameDialog.newName(); + SessionHandler::instance()->rename(item, newName); + refreshListOfSavedSessions(); + } +} + + +void SessionDialog::on_switchToButton__clicked() +{ + std::string sessionName = selectedSessionName(); + assert(!sessionName.empty()); // it should not be possible for the name to be empty + + SessionItem *item = SessionHandler::instance()->find(sessionName); + assert(item); // it should not be possible for the name to be empty + + SessionHandler::instance()->current(item); // set this session as the current one + + if (restoreLastSessionCb_->checkState() == Qt::Checked) // save details of the selected session? + SessionHandler::instance()->saveLastSessionName(); + else + SessionHandler::instance()->removeLastSessionName(); // no, so we can delete the file + + accept(); // close the dialogue and continue loading the main user interface +} + + +void SessionDialog::on_saveButton__clicked() +{ +/* + std::string name = sessionNameEdit_->text().toStdString(); + + if (validSaveName(name)) + { + SessionItem* newSession = SessionHandler::instance()->copySession(SessionHandler::instance()->current(), name); + if (newSession) + { + //SessionHandler::instance()->add(name); + refreshListOfSavedSessions(); + SessionHandler::instance()->current(newSession); + QMessageBox::information(0,tr("Session"), tr("Session saved")); + } + else + { + QMessageBox::critical(0,tr("Session"), tr("Failed to save session")); + } + close(); + }*/ +} + +// called when the user clicks the Save button +//void SaveSessionAsDialog::accept() +//{ +// +//} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,48 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SESSIONDIALOG_HPP +#define SESSIONDIALOG_HPP + +#include +#include "ui_SessionDialog.h" + +#include "SessionHandler.hpp" + +namespace Ui { +class SessionDialog; +} + +class SessionDialog : public QDialog, protected Ui::SessionDialog +{ + Q_OBJECT + +public: + explicit SessionDialog(QWidget *parent = 0); + ~SessionDialog(); + +public Q_SLOTS: + void on_saveButton__clicked(); + void on_sessionNameEdit__textChanged(); + void on_savedSessionsList__currentRowChanged(int currentRow); + void on_cloneButton__clicked(); + void on_deleteButton__clicked(); + void on_renameButton__clicked(); + void on_switchToButton__clicked(); + +private: + //Ui::SaveSessionAsDialog *ui; + void addSessionToTable(SessionItem *s); + bool validSaveName(const std::string &name); + void refreshListOfSavedSessions(); + void setButtonsEnabledStatus(); + std::string selectedSessionName(); +}; + +#endif // SESSIONDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,134 @@ + + + SessionDialog + + + + 0 + 0 + 400 + 300 + + + + ecFlowUI Session Manager + + + + + + + + + + + + + New + + + + + + + Delete + + + + + + + Rename + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Clone + + + + + + + + + + + Use this session without asking at startup + + + + + + + + + Start with this session + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + + + + + buttonBox + accepted() + SessionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + SessionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,482 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include +#include +#include + +#include "SessionHandler.hpp" +#include "DirectoryHandler.hpp" +#include "Str.hpp" +#include "UserMessage.hpp" +#include "ServerList.hpp" + + +SessionHandler* SessionHandler::instance_=0; + + +SessionItem::SessionItem(const std::string& name) : + name_(name) +{ + checkDir(); + isTemporary_ = false; + askToPreserveTemporarySession_ = true; +} + +SessionItem::~SessionItem() +{ + // temporary session? if so, remove the directory on exit + if (isTemporary_) + { + std::string msg; + DirectoryHandler::removeDir(dirPath_, msg); + } +} + +void SessionItem::checkDir() +{ + dirPath_ = SessionHandler::sessionDirName(name_); + DirectoryHandler::createDir(dirPath_); + + qtPath_= SessionHandler::sessionQtDirName(name_); + DirectoryHandler::createDir(qtPath_); +} + +std::string SessionItem::sessionFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "session.json"); +} + +std::string SessionItem::windowFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "window.conf"); +} + +std::string SessionItem::settingsFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "settings.json"); +} + +std::string SessionItem::recentCustomCommandsFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "recent_custom_commands.json"); +} + +std::string SessionItem::savedCustomCommandsFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "saved_custom_commands.json"); +} + +std::string SessionItem::textFilterFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "text_filters.json"); +} + +std::string SessionItem::serverFile(const std::string& serverName) const +{ + return DirectoryHandler::concatenate(dirPath_, serverName + ".conf.json"); +} + +std::string SessionItem::infoPanelDialogFile() const +{ + return DirectoryHandler::concatenate(dirPath_, "info_panel_dialog.json"); +} + +std::string SessionItem::qtDir() const +{ + return qtPath_; +} + +std::string SessionItem::qtSettingsFile(const std::string name) const +{ + return DirectoryHandler::concatenate(qtPath_, name + ".conf"); +} + +//================================================= +// +// SessionHandler +// +//================================================= + +SessionHandler::SessionHandler() : + current_(0) +{ + //The default must always be exist! + current_=add(defaultSessionName()); + loadedLastSessionName_ = false; + + readSessionListFromDisk(); + readLastSessionName(); +} + +SessionHandler::~SessionHandler() +{ + for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) + { + delete (*it); + } +} + + +SessionHandler* SessionHandler::instance() +{ + if(!instance_) + { + instance_=new SessionHandler(); + } + + return instance_; +} + +void SessionHandler::destroyInstance() +{ + if (instance_) + delete instance_; + + instance_ = 0; +} + +std::string SessionHandler::sessionDirName(const std::string &sessionName) +{ + return DirectoryHandler::concatenate(DirectoryHandler::configDir(), sessionName + ".session"); +} + +std::string SessionHandler::sessionQtDirName(const std::string &sessionName) +{ + std::string basedir = sessionDirName(sessionName); + return DirectoryHandler::concatenate(basedir, "qt"); +} + +SessionItem* SessionHandler::find(const std::string& name) +{ + for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) + { + if((*it)->name() == name) + return *it; + } + return NULL; + +} + + +// returns -1 if the session name is not found +int SessionHandler::indexFromName(const std::string& name) +{ + int n = 0; + for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) + { + if((*it)->name() == name) + return n; + n++; + } + return -1; +} + +void SessionHandler::readSessionListFromDisk() +{ + // get the list of existing sessions (by listing the directories) + std::string configDir = DirectoryHandler::configDir(); + std::string filter = ".*\\.session"; + std::vector dirs; + DirectoryHandler::findDirs(configDir, filter, dirs); + + // add each session to our list (but remove the .session first) + for(std::vector::const_iterator it=dirs.begin(); it != dirs.end(); ++it) + { + std::string dirName = (*it); + std::string toRemove = ".session"; + std::string toReplaceWith = ""; + ecf::Str::replace(dirName, toRemove, toReplaceWith); + add(dirName); + } +} + +bool SessionHandler::loadLastSessionAtStartup() +{ + // if there was a last session file, then it means the user wanted to load at startup + return loadedLastSessionName_; +} + + + +SessionItem* SessionHandler::add(const std::string& name) +{ + // only add if not already there + if (find(name) == NULL) + { + SessionItem *item=new SessionItem(name); + sessions_.push_back(item); + return item; + } + else + return NULL; +} + +void SessionHandler::remove(const std::string& sessionName) +{ + SessionItem *session = find(sessionName); + assert(session); + + remove(session); +} + +void SessionHandler::remove(SessionItem* session) +{ + std::vector::iterator it = std::find(sessions_.begin(), sessions_.end(), session); + assert(it != sessions_.end()); // session was not found - should not be possible + + + // remove the session's directory + std::string errorMessage; + bool ok = DirectoryHandler::removeDir(sessionDirName(session->name()), errorMessage); + + if (ok) + { + // remove it from our list + sessions_.erase(it); + } + else + { + UserMessage::message(UserMessage::ERROR, true, errorMessage); + } +} + + +void SessionHandler::rename(SessionItem* item, const std::string& newName) +{ + std::string errorMessage; + bool ok = DirectoryHandler::renameDir(sessionDirName(item->name()), sessionDirName(newName), errorMessage); + + if (ok) + { + item->name(newName); + } + else + { + UserMessage::message(UserMessage::ERROR, true, errorMessage); + } +} + + +void SessionHandler::current(SessionItem* item) +{ + if(std::find(sessions_.begin(),sessions_.end(),item) != sessions_.end()) + { + current_=item; + load(); + } +} + + +SessionItem* SessionHandler::current() +{ + return current_; +} + +void SessionHandler::save() +{ + if(current_) + { + //current_->save(); + } +} + +void SessionHandler::load() +{ + +} + + +bool SessionHandler::requestStartupViaSessionManager() +{ + char *sm = getenv("ECFLOWUI_SESSION_MANAGER"); + if (sm) + return true; + else + return false; +} + + +void SessionHandler::setTemporarySessionIfReqested() +{ + char *sh = getenv("ECFLOWUI_TEMP_SESSION_HOST"); + if (sh) + { + char *sp = getenv("ECFLOWUI_TEMP_SESSION_PORT"); + if (sp) + { + // create a session name, likely to be unique - if it already exists in the list, then use that + char sessName[1024]; + sprintf(sessName, "temporary_%s_%s_%d", sh, sp, getpid()); + std::string sname(sessName); + SessionItem *si = instance()->add(sname); + if (!si) + si = instance()->find(sname); + + instance()->current(si); + si->temporary(true); + + char *sask = getenv("ECFLOWUI_TEMP_SESSION_PRESERVE_CONFIRM"); + if (sask && !strcmp(sask, "no")) + si->askToPreserveTemporarySession(false); + + std::string templateName("ecflow_ui_test_session_template.json"); + instance()->createSessionDirWithTemplate(sname, templateName); + + // construct an alias for this server + char calias[1024]; + sprintf(calias, "%s:%s", sh, sp); + std::string alias(calias); + si->temporaryServerAlias(alias); + + // does this exact server already exist in the user's list? + std::string host(sh); + std::string port(sp); + if(ServerList::instance()->find(alias, host, port) == 0) + { + // no - add it, and make sure it's got a unique alias + std::string uniqueName = ServerList::instance()->uniqueName(alias); + ServerList::instance()->add(uniqueName, host, port, false, true); + } + } + } +} + + +void SessionHandler::saveLastSessionName() +{ + std::string configDir = DirectoryHandler::configDir(); + std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); + + // open the last_session.txt file and try to read it + std::ofstream out(lastSessionFilename.c_str()); + + if (out.good()) + { + // the file is a one-line file containing the name of the current session + std::string line = current()->name(); + out << line << std::endl; + } +} + +void SessionHandler::removeLastSessionName() +{ + std::string configDir = DirectoryHandler::configDir(); + std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); + + std::string errorMessage; + bool ok = DirectoryHandler::removeFile(lastSessionFilename, errorMessage); + + if (!ok) + { + UserMessage::message(UserMessage::ERROR, true, errorMessage); + } +} + + +void SessionHandler::readLastSessionName() +{ + std::string configDir = DirectoryHandler::configDir(); + std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); + + // open the last_session.txt file and try to read it + std::ifstream in(lastSessionFilename.c_str()); + + if (in.good()) + { + // the file is a one-line file containing the name of the session we want + std::string line; + if (getline(in, line)) + { + loadedLastSessionName_ = true; + lastSessionName_ = line; + } + else + lastSessionName_ = defaultSessionName(); + } + else + { + // could not read the file, so just use the default + lastSessionName_ = defaultSessionName(); + } + + + // set this session as the current one if it exists + SessionItem *item = find(lastSessionName_); + if (item) + { + current(item); + } + else + { + lastSessionName_ = defaultSessionName(); // the last session file contained the name of a non-existant session + } +} + + +bool SessionHandler::createSessionDirWithTemplate(const std::string &sessionName, const std::string &templateFile) +{ + // we want to: + // 1) create a new directory for the session + // 2) copy the session template JSON file across to the new dir + + std::string sessionDir = sessionDirName(sessionName); + bool created = DirectoryHandler::createDir(sessionDir); + + if (created) + { + std::string errorMsg; + std::string fullTemplatePath = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), templateFile); + std::string fullDestPath = DirectoryHandler::concatenate(sessionDir, "session.json"); + bool copied = DirectoryHandler::copyFile(fullTemplatePath, fullDestPath, errorMsg); + if (!copied) + { + UserMessage::message(UserMessage::ERROR, true, errorMsg); + return false; + } + } + else + { + UserMessage::message(UserMessage::ERROR, true, "Could not create temporary session directory: " + sessionDir); + return false; + } + + return true; +} + + +SessionItem *SessionHandler::copySession(SessionItem* source, std::string &destName) +{ + // the main work is to make a copy of the source session's directory (recursively) + std::string errorMessage; + std::string sourceSessionDir = sessionDirName(source->name()); + std::string destSessionDir = sessionDirName(destName); + bool ok = DirectoryHandler::copyDir(sourceSessionDir, destSessionDir, errorMessage); + if (ok) + { + // add it to our list + SessionItem *newItem = add(destName); + return newItem; + } + else + { + UserMessage::message(UserMessage::ERROR, true, errorMessage); + return NULL; + } +} + +SessionItem *SessionHandler::copySession(std::string &source, std::string &destName) +{ + SessionItem *sourceSession = find(source); + assert(sourceSession); + + return copySession(sourceSession, destName); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,105 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SESSIONHANDLER_HPP_ +#define SESSIONHANDLER_HPP_ + +#include +#include + +class ServerItem; + + +class SessionItem +{ +public: + explicit SessionItem(const std::string&); + virtual ~SessionItem(); + + void name(const std::string& name) {name_ = name;} + const std::string& name() const {return name_;} + + std::string sessionFile() const; + std::string windowFile() const; + std::string settingsFile() const ; + std::string recentCustomCommandsFile() const ; + std::string savedCustomCommandsFile() const ; + std::string textFilterFile() const ; + std::string serverFile(const std::string& serverName) const; + std::string infoPanelDialogFile() const; + std::string qtDir() const; + std::string qtSettingsFile(const std::string name) const; + void temporary(bool t) {isTemporary_ = t;} + bool temporary() const {return isTemporary_;} + void temporaryServerAlias(const std::string &alias) {temporaryServerAlias_ = alias;} + std::string temporaryServerAlias() const {return temporaryServerAlias_;} + void askToPreserveTemporarySession(bool a) {askToPreserveTemporarySession_ = a;} + bool askToPreserveTemporarySession() {return askToPreserveTemporarySession_;} + +protected: + void checkDir(); + + std::string name_; + std::string dirPath_; + std::string qtPath_; + std::string temporaryServerAlias_; + bool isTemporary_; + bool askToPreserveTemporarySession_; +}; + +class SessionHandler +{ +public: + SessionHandler(); + ~SessionHandler(); + + SessionItem* add(const std::string&); + void remove(const std::string&); + void remove(SessionItem*); + void rename(SessionItem*, const std::string&); + void current(SessionItem*); + SessionItem* current(); + void save(); + void load(); + int numSessions() {return sessions_.size();} + SessionItem *find(const std::string&); + int indexFromName(const std::string&); + SessionItem *sessionFromIndex(int i) {return sessions_[i];} + SessionItem *copySession(SessionItem* source, std::string &destName); + SessionItem *copySession(std::string &source, std::string &destName); + bool createSessionDirWithTemplate(const std::string &sessionName, const std::string &templateFile); + void saveLastSessionName(); + void removeLastSessionName(); + bool loadLastSessionAtStartup(); + std::string lastSessionName() {return lastSessionName_;} + + + const std::vector& sessions() const {return sessions_;} + + static std::string sessionDirName(const std::string &sessionName); // static because they are called from the constructor + static std::string sessionQtDirName(const std::string &sessionName); // static because they are called from the constructor + static SessionHandler* instance(); + static void destroyInstance(); + static bool requestStartupViaSessionManager(); + static void setTemporarySessionIfReqested(); + +protected: + void readSessionListFromDisk(); + std::string defaultSessionName() {return "default";} + void readLastSessionName(); + + static SessionHandler* instance_; + + std::vector sessions_; + SessionItem* current_; + bool loadedLastSessionName_; + std::string lastSessionName_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,49 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include + +#include "SessionHandler.hpp" + +#include "SessionRenameDialog.hpp" +#include "ui_SessionRenameDialog.h" + +SessionRenameDialog::SessionRenameDialog(QWidget *parent) : QDialog(parent) +{ + setupUi(this); +} + +SessionRenameDialog::~SessionRenameDialog() +{ +} + + +void SessionRenameDialog::on_buttonBox__accepted() +{ + // store the name + newName_ = nameEdit_->text().toStdString(); + + + // check it does not clash with an existing session name + if (SessionHandler::instance()->find(newName_)) + { + QMessageBox::critical(0,tr("Rename session"), tr("A session with that name already exists - please choose another name")); + } + else + { + // close the dialogue + accept(); + } +} + +void SessionRenameDialog::on_buttonBox__rejected() +{ + // close the dialogue + reject(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,40 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SESSIONRENAMEDIALOG_HPP +#define SESSIONRENAMEDIALOG_HPP + +#include +#include "ui_SessionRenameDialog.h" + +#include "SessionHandler.hpp" + +namespace Ui { +class SessionRenameDialog; +} + +class SessionRenameDialog : public QDialog, protected Ui::SessionRenameDialog +{ + Q_OBJECT + +public: + explicit SessionRenameDialog(QWidget *parent = 0); + ~SessionRenameDialog(); + + std::string newName() {return newName_;}; + +public Q_SLOTS: + void on_buttonBox__accepted(); + void on_buttonBox__rejected(); + +private: + std::string newName_; + }; + +#endif // SESSIONRENAMEDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/SessionRenameDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SessionRenameDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,81 @@ + + + SessionRenameDialog + + + + 0 + 0 + 284 + 129 + + + + Add variable + + + true + + + + + + + 0 + 0 + + + + New name: + + + + + + + + + &Name: + + + label_1 + + + + + + + false + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ShellCommand.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ShellCommand.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ShellCommand.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ShellCommand.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,147 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "ShellCommand.hpp" + +#include +#include +#include + +#include + +#include "CommandOutputDialog.hpp" +#include "DirectoryHandler.hpp" +#include "CommandOutput.hpp" +#include "UiLog.hpp" +#include "VFile.hpp" + +//static std::vector commands; + +bool ShellCommand::envChecked_=false; +bool ShellCommand::envHasToBeSet_=false; + +ShellCommand::ShellCommand(const std::string& cmdStr,const std::string& cmdDefStr) : + QObject(0), + proc_(0), + commandDef_(QString::fromStdString(cmdDefStr)) +{ + QString cmdIn=QString::fromStdString(cmdStr); + + //A valid shell command must start with "sh " + if(!cmdIn.startsWith("sh ")) + { + deleteLater(); + return; + } + + command_=cmdIn.mid(3); + + proc_=new QProcess(this); + + //Check environment - only has to be once + if(!envChecked_) + { + envChecked_=true; + + QString exeDir=QString::fromStdString(DirectoryHandler::exeDir()); + + //If there is an exe dir we check if it is added to the PATH env + //variable + if(!exeDir.isEmpty()) + { + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString envPath=env.value("PATH"); + if(!envPath.contains(exeDir)) + envHasToBeSet_=true; + } + } + + //If the shell command runs ecflow_client it has to be in the PATH env variable. + //When we develop the ui it is not the case so we need to add its path to PATH + //whenever is possible. The same is true for node_state_diag.sh. + if((command_.contains("ecflow_client") || + command_.contains("ecflow_ui_node_state_diag.sh") || command_.contains("ecflow_ui_create_jira_issue.sh")) && + envHasToBeSet_) + { + QString exeDir=QString::fromStdString(DirectoryHandler::exeDir()); + Q_ASSERT(!exeDir.isEmpty()); + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString envPath=env.value("PATH"); + env.insert("PATH",exeDir + ":" + envPath); + proc_->setProcessEnvironment(env); + } + + connect(proc_,SIGNAL(finished(int,QProcess::ExitStatus)), + this,SLOT(procFinished(int,QProcess::ExitStatus))); + + connect(proc_,SIGNAL(readyReadStandardOutput()), + this,SLOT(slotStdOutput())); + + connect(proc_,SIGNAL(readyReadStandardError()), + this,SLOT(slotStdError())); + + startTime_=QDateTime::currentDateTime(); + proc_->start("/bin/sh -c \"" + command_ + "\""); +} + +QString ShellCommand::command() const +{ + return command_; +} + +ShellCommand* ShellCommand::run(const std::string& cmd,const std::string& cmdDef) +{ + return new ShellCommand(cmd,cmdDef); +} + +void ShellCommand::procFinished(int exitCode, QProcess::ExitStatus exitStatus) +{ + if(!item_ && exitCode !=0) + { + item_=CommandOutputHandler::instance()->addItem(command_,commandDef_,startTime_); + } + + if(item_) + { + if(exitCode == 0 && exitStatus == QProcess::NormalExit) + CommandOutputHandler::instance()->finished(item_); + else + CommandOutputHandler::instance()->failed(item_); + } + deleteLater(); + +} + +void ShellCommand::slotStdOutput() +{ + if(!item_) + { + item_=CommandOutputHandler::instance()->addItem(command_,commandDef_,startTime_); + } + Q_ASSERT(item_); + if(item_->isEnabled()) + { + QString txt=proc_->readAllStandardOutput(); + CommandOutputHandler::instance()->appendOutput(item_,txt); + } +} + +void ShellCommand::slotStdError() +{ + if(!item_) + { + item_=CommandOutputHandler::instance()->addItem(command_,commandDef_,startTime_); + } + Q_ASSERT(item_); + if(item_->isEnabled()) + { + QString txt=proc_->readAllStandardError(); + CommandOutputHandler::instance()->appendError(item_,txt); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ShellCommand.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ShellCommand.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ShellCommand.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ShellCommand.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,51 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SHELLCOMMAND_HPP +#define SHELLCOMMAND_HPP + +#include +#include +#include + +#include +#include + +#include + +#include "CommandOutput.hpp" + +class ShellCommand : public QObject +{ + Q_OBJECT +public: + static ShellCommand* run(const std::string&,const std::string&); + + QString command() const; + QString commandDef() const {return commandDef_;} + QDateTime startTime() const {return startTime_;} + +protected Q_SLOTS: + void procFinished(int exitCode, QProcess::ExitStatus exitStatus); + void slotStdOutput(); + void slotStdError(); + +protected: + ShellCommand(const std::string&,const std::string&); + + QProcess *proc_; + QString command_; + QString commandDef_; + QDateTime startTime_; + CommandOutput_ptr item_; + static bool envChecked_; + static bool envHasToBeSet_; +}; + +#endif // SHELLCOMMAND_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Sound.cpp ecflow-4.11.1/Viewer/ecflowUI/src/Sound.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Sound.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Sound.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,158 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "Sound.hpp" + +#include "DirectoryHandler.hpp" +#include "UiLog.hpp" +#include "VConfig.hpp" +#include "VConfigLoader.hpp" +#include "VProperty.hpp" + +#include +#include + +//#ifdef ECFLOW_QT5 +//#include +//#endif + + +#include +#include +#include + +Sound* Sound::instance_=NULL; + + +/* + if(sound) + { + const char *soundCmd = "play -q /usr/share/xemacs/xemacs-packages/etc/sounds/boing.wav"; + if (system(soundCmd)) + UiLog().dbg() << "ChangeNotify:add() could not play sound alert"; +*/ +//#ifdef ECFLOW_QT5 +// QSoundEffect effect(dialog_); +// effect.setSource(QUrl::fromLocalFile("file:/usr/share/xemacs/xemacs-packages/etc/sounds/boing.wav")); +// effect.setLoopCount(1); +// effect.setVolume(0.25f); +// effect.play(); +//#endif + + +Sound::Sound() : + prevPlayedAt_(0), + delay_(2), + prop_(NULL) +{ + formats_=".+\\.(wav|mp3|ogg|oga)"; + + sysDir_=DirectoryHandler::concatenate(DirectoryHandler::etcDir(), "sounds"); + + std::vector res; + + DirectoryHandler::findFiles(sysDir_,formats_,res); + + for(unsigned int i=0; i < res.size(); i++) + { + sysSounds_.push_back(res.at(i)); + } +} + +Sound* Sound::instance() +{ + if(!instance_) + instance_=new Sound(); + + return instance_; +} + +void Sound::playSystem(const std::string& fName,int loopCount) +{ + std::string fullName=DirectoryHandler::concatenate(sysDir_, fName); + play(fullName,loopCount); +} + +void Sound::play(const std::string& fName,int loopCount) +{ + assert(loopCount < 6); + + time_t t=time(NULL); + if(t < prevPlayedAt_+delay_) + return; + + if(currentCmd_.empty()) + { + + } + else + { + std::string cmd=currentCmd_; + boost::replace_first(cmd,"%FILE%",fName); + boost::replace_first(cmd,"%REPEAT%",boost::lexical_cast(loopCount-1)); + if(system(cmd.c_str())) + { + UiLog().dbg() << "Sound::play() could not play sound alert. Command: " << cmd; + } + } + + prevPlayedAt_=time(NULL); +} + +void Sound::setCurrentPlayer(const std::string& current) +{ + std::map::const_iterator it=players_.find(current); + if(it != players_.end()) + { + currentPlayer_=it->first; + currentCmd_=it->second; + } + else + assert(0); +} + +bool Sound::isSoundFile(const std::string& fName) const +{ + const boost::regex expr(formats_); + boost::smatch what; + if(boost::regex_match(fName, what,expr)) + return true; + return false; +} + +void Sound::load(VProperty* prop) +{ + UiLog().dbg() << "Sound:load() -- > begin"; + + if(prop->name() != "sound") + { + UiLog().err() << "Sound:load() -- > no property found!"; + return; + } + + Sound::instance_->prop_=prop; + + if(VProperty *pp=prop->findChild("players")) + { + Q_FOREACH(VProperty* p,pp->children()) + { + Sound::instance_->players_[p->strName()]=p->param("command").toStdString(); + } + } + + if(VProperty *pp=prop->findChild("player")) + { + Sound::instance_->setCurrentPlayer(pp->valueAsStdString()); + } + + UiLog().dbg() << "Sound:load() -- > end"; +} + +static SimpleLoader loaderSound("sound"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Sound.hpp ecflow-4.11.1/Viewer/ecflowUI/src/Sound.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Sound.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Sound.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,54 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SOUND_HPP_ +#define SOUND_HPP_ + +#include +#include + +#include "VProperty.hpp" + +class Sound : public VPropertyObserver +{ +public: + static Sound* instance(); + + const std::vector& sysSounds() const {return sysSounds_;} + const std::string sysDir() const {return sysDir_;} + + void playSystem(const std::string&,int repeat); + void play(const std::string&,int repeat); + bool isSoundFile(const std::string& fName) const; + + void notifyChange(VProperty*) {}; + + //Called from VConfigLoader + static void load(VProperty* group); + +protected: + Sound(); + void setCurrentPlayer(const std::string&); + + static Sound* instance_; + + std::map players_; + std::string currentPlayer_; + std::string currentCmd_; + + std::string formats_; + std::string sysDir_; + std::vector sysSounds_; + time_t prevPlayedAt_; + int delay_; + VProperty* prop_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StandardView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/StandardView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StandardView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StandardView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,792 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "StandardView.hpp" + +#include "ExpandState.hpp" +#include "TreeNodeModel.hpp" +#include "TreeNodeViewDelegate.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include + +//#define _UI_STANDARDVIEW_DEBUG + +StandardView::StandardView(TreeNodeModel* model,QWidget* parent) : + AbstractNodeView(model,parent), + expandIndicatorBoxWidth_(20), + expandIndicatorWidth_(10) +{ + //Overwrite some base class values + drawConnector_=false; + indentation_=10; + + //This is needed for making the context menu work + setProperty("view","tree"); + + //we cannot call it from the constructor of the base class + //because it calls a pure virtual method + reset(); +} + +StandardView::~StandardView() +{ + +} + +//Creates and initialize the viewItem structure of the children of the element +// parentId: the items whose children are to be expanded +// recursiveExpanding: all the children will be expanded +// afterIsUninitialized: when we recurse from layout(-1) it indicates +// the items after 'i' are not yet initialized and need not to be moved + +void StandardView::layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated) +{ + //This is the root item. + if(parentId == -1) + { + rowCount_=0; + maxRowWidth_=0; + } + + QModelIndex parentIndex = (parentId < 0) ? root_ : modelIndex(parentId); + + if(parentId >=0 && !parentIndex.isValid()) + { + //modelIndex() should never return something invalid for the real items. + //This can happen if columncount has been set to 0. + //To avoid infinite loop we stop here. + return; + } + + int count=model_->rowCount(parentIndex); + bool expanding=true; + + //This is the root item. viewItems must be empty at this point. + if(parentId == -1) + { + Q_ASSERT(viewItems_.empty()); + Q_ASSERT(preAllocated == false); + viewItems_.resize(count); + afterIsUninitialized = true; //It can only be true when we expand from the root! + } + //The count of the stored children does not match the actual count + else if(viewItems_[parentId].total != (uint)count) + { + //Expand + if(!afterIsUninitialized) + { + //We called expandall for a non-root item. All the new items need must be + //already instered at this point. This is the duty of the caller routine. + //const int itemsCount = viewItems_.size(); + //if(recursiveExpanding) + if(preAllocated) + { + //We called expandAll() for a non-root item. All the needed items need must already be + //inserted at this point. This is the duty of the caller routine! + //When layout() is finished we need to adjust the parent of all the items + //after the insertion position. This is the duty of the caller routine. We + //have chosen this solution for performance reasons! + } + else + { + insertViewItems(parentId + 1, count, TreeNodeViewItem()); + } + } + //ExpandAll from the root + else if(count > 0) + viewItems_.resize(viewItems_.size() + count); + } + else + { + expanding=false; + } + + int first = parentId + 1; + int last = 0; + int children = 0; + int level=(parentId >=0?viewItems_[parentId].level+1:0); + TreeNodeViewItem *item=0; + + +#ifdef _UI_STANDARDVIEW_DEBUG + //if(parentId >=0) + // UiLog().dbg() << "layout parent=" << viewItems_[parentId].index.data().toString(); +#endif + + //Iterate through the direct children of parent item. At this point all the items + //needed in the loop below are pre-allocated but not yet initialised. + for(int i=first; i < first+count; i++) + { + QModelIndex currentIndex=model_->index(i-first,0,parentIndex); + + last = i + children; + item = &viewItems_[last]; + item->parentItem = parentId; + item->index=currentIndex; + item->hasMoreSiblings=(i < first+count-1); + item->level=level; + item->expanded = false; + item->total = 0; + + //We compute the size of the item. For attributes we delay the width computation until we + //actually paint them and we set their width to 300. + int w,h; + delegate_->sizeHint(currentIndex,w,h); + item->width=w; + item->height=h; + + int xp=leftMargin_+expandIndicatorBoxWidth_; // no indentation for the root + + if(parentId >=0) + { + //item->widestInSiblings=widest; + xp=viewItems_[parentId].x+indentation_+expandIndicatorBoxWidth_; + } + else + { + //item->widestInSiblings=item->width; + } + + item->x=xp; + + if(item->alignedRight() > maxRowWidth_) + maxRowWidth_=item->right(); + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " item=" << currentIndex.data().toString(); +#endif + //We need to expand the item + if(recursiveExpanding || isIndexExpanded(currentIndex)) + { + if(recursiveExpanding) + expandedIndexes.insert(currentIndex); + + item->expanded = true; + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " is expanded"; +#endif + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " before " << item->index.data().toString() << " total=" << item->total; +#endif + //Add the children to the layout + layout(last,recursiveExpanding,afterIsUninitialized,preAllocated); + + item = &viewItems_[last]; + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " after " << item->index.data().toString() << " total=" << item->total; +#endif + children+=item->total; + item->hasChildren = item->total > 0; + } + else + { + item->hasChildren = model_->hasChildren(currentIndex); + } + } + + if(!expanding) + return; // nothing changed + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " update parent total"; +#endif + + int pp=parentId; + while (pp > -1) + { + viewItems_[pp].total += count; + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " parent=" << viewItems_[pp].index.data().toString() << + // " total=" << viewItems_[pp].total; +#endif + + pp = viewItems_[pp].parentItem; + } +} + +//Paint the rows intersecting with the given region +void StandardView::paint(QPainter *painter,const QRegion& region) +{ + //Even though the viewport palette is set correctly at the + //beginning something sets it to another value. Here we try + //to detect it and correct the palette with the right colour. + if(expectedBg_.isValid()) + { + QPalette p=viewport()->palette(); + if(p.color(QPalette::Window) != expectedBg_) + { + p.setColor(QPalette::Window,expectedBg_); + viewport()->setPalette(p); + viewport()->update(); + expectedBg_=QColor(); + return; + } + } + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "StandardView::paint -->"; + //UiLog().dbg() << "sizeof(TreeNodeViewItem)=" << sizeof(TreeNodeViewItem); + //UiLog().dbg() << "region=" << region; +#endif + + int firstVisibleOffset=0; + + //The first visible item at the top of the viewport + int firstVisible=firstVisibleItem(firstVisibleOffset); +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "firstVisible " << firstVisible; +#endif + + if(firstVisible<0) + return; + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "scrollX" << horizontalScrollBar()->value() << " " << viewport()->width(); +#endif + + int xOffset=0; + if(horizontalScrollBar()->value() > 0) + { + xOffset=horizontalScrollBar()->value(); + painter->translate(-xOffset,0); + } + + const int itemsCount = viewItems_.size(); + const int viewportWidth = viewport()->width(); + QVector rects = region.rects(); + QVector drawn; + bool multipleRects = (rects.size() > 1); + + //Iterate through the rectangles in the region + for(int a = 0; a < rects.size(); ++a) + { + const QRect area = (multipleRects + ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height()) + : rects.at(a)); +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " area=" << area; +#endif + std::vector indentVec; + + if(drawConnector_) + { + //Initialise indentVec. For each indentation level it tells us if + //a connector line is to be drawn. Here we scan up to the + //toplevel item in the firstVisible item's branch. + indentVec=std::vector(1000,0); + if(firstVisible >0) + { + TreeNodeViewItem* item=&viewItems_[firstVisible]; + int level=item->level; + while(item->parentItem >= 0 && level >0) + { + TreeNodeViewItem* pt=&viewItems_[item->parentItem]; + if(item->hasMoreSiblings) + { + indentVec[item->level]=connectorPos(item); + } + UI_ASSERT(pt->level == level-1, "item->parentItem=" << item->parentItem << + " pt->level=" << pt->level << " level=" << level); + item=pt; + level--; + } + } + } + + int i = firstVisible; // the first item at the top of the viewport + int y = firstVisibleOffset; // we may only see part of the first item + + //start at the top of the viewport and iterate down through the update area + for (; i < itemsCount; i++) + { + int itemHeight=viewItems_[i].height; + + if(drawConnector_) + { + //Adjust indentVec + if(viewItems_[i].hasMoreSiblings) + { + indentVec[viewItems_[i].level]=connectorPos(&viewItems_[i]); + } + else + indentVec[viewItems_[i].level]=0; + } + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "row: " << i << " " << itemHeight; +#endif + //Try to find the first item int the current rect + if(y + itemHeight > area.top()) + break; + y += itemHeight; + } + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "y: " << y << " " << area.bottom(); +#endif + + //Paint the visible rows in the current rect + for (; i < itemsCount && y <= area.bottom(); i++) + { + if(!multipleRects || !drawn.contains(i)) + { + //Draw a whole row. It will update y,itemsInRow and indentVec!! + drawRow(painter,i,xOffset,y,indentVec); + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " row rendered - item=" << i << " y=" << y; +#endif + } + else + { + int rh=viewItems_[i].height; + y+=rh; +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " row skipped - item=" << i << " y=" << y; +#endif + } + + if(multipleRects) + drawn.append(i); + } + } +} + +//Draw a whole row +void StandardView::drawRow(QPainter* painter,int start,int xOffset,int& yp,std::vector& indentVec) +{ + TreeNodeViewItem* item=&(viewItems_[start]); + + //Get the rowheight + int rh=item->height; + + //See if there are no multiline items in this row + bool singleRow=delegate_->isSingleHeight(rh); + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " item=" << " " << item->index.data().toString(); +#endif + + //Find out the indentation level of the row + int firstLevel=item->level; + + //Init style option + QStyleOptionViewItem opt; + if(selectionModel_->isSelected(item->index)) + opt.state |= QStyle::State_Selected; + + int optWidth=2000; + if(item->width > optWidth) + optWidth=item->width; + opt.rect=QRect(item->x,yp,optWidth,item->height); + + //We do not render the item if it is outisde the viewport and + //its parent's right is also outside the viewport. Here we considered that + //the connector line is always drawn from the child to the parent. + bool needToDraw=true; +#if 0 + if(item->parentItem >=0) + { + if(viewItems_[item->parentItem].right() >= translation() + viewportWidth) + needToDraw=false; + } +#endif + if(needToDraw) + { + //For single rows we center items halfway through the rowHeight +#if 0 + if(singleRow) + { + if(item->height < rh) + { + opt.rect.moveTop(yp+(rh-item->height)/2); + } + } +#endif + + //QRect vr=visualRect(item->index); + //painter->fillRect(vr,QColor(120,120,120,120)); + +//#ifdef _UI_STANDARDVIEW_DEBUG +// UiLog().dbg() << " optRect=" << opt.rect << " visRect=" << vr; +//#endif + + //Draw the item with the delegate + QSize paintedSize; + delegate_->paint(painter,opt,item->index,paintedSize); + + //we have to know if the item width/height is the same that we expected. + //This can happen when: + // -we set a fixed initial width for the item (e.g. for an attribute) + // and now we got the real width + // -the number of icons or additional extra information + // changed for a node (so the width changed) + // -the number of lines changed in a multiline label (so the height changed) + // -the node becomes submitted + bool wChanged=paintedSize.width() != item->width; + bool hChanged=paintedSize.height() != item->height; + + if(wChanged || hChanged) + { + //set new size + item->width=paintedSize.width(); + item->height=paintedSize.height(); + + if(item->right() > maxRowWidth_) + { + maxRowWidth_=item->right(); + doDelayedWidthAdjustment(); + } + else if(hChanged) + { + doDelayedWidthAdjustment(); + } + } + + //draw expand indicator + if(item->hasChildren) + { + //We draw a triangle into the middle of the expand indicator box + float indX=item->x-expandIndicatorBoxWidth_/2; + float indY=yp+item->height/2; + + //painter->drawRect(QRect(indX-expandIndicatorBoxWidth_/2,indY-item->height/2, + // expandIndicatorBoxWidth_,expandIndicatorBoxWidth_)); + + float tw=expandIndicatorWidth_; + float th=tw/2.*0.95; + + QPolygonF shape; + if(item->expanded) + { + shape << QPointF(indX-tw/2,indY-0.2*th) << + QPointF(indX+tw/2,indY-0.2*th) << + QPointF(indX,indY+0.8*th); + } + else + { + shape << QPointF(indX,indY-tw/2.) << + QPointF(indX,indY+tw/2.) << + QPointF(indX+th,indY); + } + + QPen oriPen=painter->pen(); + painter->setPen(Qt::NoPen); + painter->setBrush(QColor(71,71,70)); + painter->setRenderHint(QPainter::Antialiasing,true); + painter->drawPolygon(shape); + painter->setRenderHint(QPainter::Antialiasing,false); + painter->setPen(oriPen); + } + + //Draw the connector lines + if(drawConnector_) + { + painter->setPen(connectorColour_); + + int lineX1=item->x-expandIndicatorBoxWidth_/2; + int lineY=yp+item->height/2; + + //For multiline labels the connector shoudl be close to the top + //not in the middle. We use the parentitem's height to find the proper + //y position. + if(!singleRow && item->parentItem >=0) + { + lineY=yp+viewItems_[item->parentItem].height/2; + } + + if(item->hasMoreSiblings) + { + indentVec[item->level]=lineX1; + } + else + indentVec[item->level]=0; + + //If not a top level item (e.i. not a server) and a leaf we need to draw + //a connector line straight to the item + if(item->parentItem >=0 && item->hasChildren == 0) + { + int lineX2=item->x-connectorGap_; + + //First child + if(item->index.row() == 0) + { + //horizontal line to the node + painter->drawLine(lineX1,lineY,lineX2,lineY); + + //vertical line to the parent + painter->drawLine(lineX1,lineY,lineX1,yp); + + //line towards the siblings - downwards + if(item->hasMoreSiblings) + { + painter->drawLine(lineX1,lineY,lineX1,yp+rh); + } + } + + //Child in the middle - has sibling both upwards and downwards + else if(item->hasMoreSiblings) + { + //horizontal line to the node + painter->drawLine(lineX1,lineY,lineX2,lineY); + + //vertical line to the parent and sibling + painter->drawLine(lineX1,yp,lineX1,yp+rh); + } + + //The last child - has sibling only upwards + else + { + //horizontal line to the node + painter->drawLine(lineX1,lineY,lineX2,lineY); + + //vertical line to the parent + painter->drawLine(lineX1,lineY,lineX1,yp); + } + } + + //Draw the vertical connector lines for all the levels + //preceding the first level in the row! + painter->setPen(connectorColour_); + for(int j=0; j < firstLevel; j++) + { + int xp=indentVec[j]; + if(xp != 0) + painter->drawLine(xp,yp,xp,yp+rh); + } + } + } + + yp+=rh; +} + +int StandardView::connectorPos(TreeNodeViewItem* item) const +{ + return item->x-expandIndicatorBoxWidth_/2; +} + +int StandardView::itemRow(int item) const +{ + return item; +} + +int StandardView::firstVisibleItem(int &offset) const +{ + const int value = verticalScrollBar()->value(); +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "CompactNodeView::firstVisibleItem --> value=" << value; +#endif + + if (verticalScrollMode_ == ScrollPerItem) + { + offset = 0; + //value is the row number + + if(value <0 || value >= rowCount_) + return -1; + + return value; + } + + return -1; +} + + +//This has to be very quick. Called after each collapse/expand. +void StandardView::updateRowCount() +{ + rowCount_=static_cast(viewItems_.size()); + +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "CompactNodeView::updateRowCount --> " << rowCount_; +#endif +} + +void StandardView::updateScrollBars() +{ +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << "CompactNodeView::updateScrollBars -->"; +#endif + + QSize viewportSize = viewport()->size(); + if(!viewportSize.isValid()) + viewportSize = QSize(0, 0); + + if(viewItems_.empty()) + { + //doItemsLayout(); + } + + int itemsInViewport = 0; + + const int itemsCount = viewItems_.size(); + if(itemsCount ==0) + return; + + const int viewportHeight = viewportSize.height(); + for(int height = 0, item = itemsCount - 1; item >= 0; item--) + { + //UiLog().dbg() << "item=" << item; + height +=viewItems_[item].height; + if(height > viewportHeight) + break; + itemsInViewport++; + } +#ifdef _UI_STANDARDVIEW_DEBUG + //UiLog().dbg() << " itemsCount=" << itemsCount << " rowCount=" << rowCount_; + //UiLog().dbg() << " itemsInViewport " << itemsInViewport; +#endif + + if(verticalScrollMode_ == ScrollPerItem) + { + if(!viewItems_.empty()) + itemsInViewport = qMax(1, itemsInViewport); + + //verticalScrollBar()->setRange(0, itemsCount - itemsInViewport); + verticalScrollBar()->setRange(0, rowCount_ - itemsInViewport); + verticalScrollBar()->setPageStep(itemsInViewport); + verticalScrollBar()->setSingleStep(1); + } + else + { + // scroll per pixel + } + + //Horizontal scrollbar + if(viewportSize.width() < maxRowWidth_) + { + horizontalScrollBar()->setRange(0,maxRowWidth_+10-viewportSize.width()); + horizontalScrollBar()->setPageStep(viewportSize.width()); + horizontalScrollBar()->setSingleStep(1); + } + else + { + horizontalScrollBar()->setRange(0,0); + } +} + +/* + Returns the rectangle on the viewport occupied by the item at \a index. + If the index is not visible or explicitly hidden, the returned rectangle is invalid. +*/ +QRect StandardView::visualRect(const QModelIndex &index) const +{ + //if (!d->isIndexValid(index) || isIndexHidden(index)) + // return QRect(); + + //d->executePostedLayout(); + + int vi = viewIndex(index); + if (vi < 0) + return QRect(); + + int y =coordinateForItem(vi); + if(y >=0) + { + //return QRect(viewItems_[vi].x, y, viewItems_[vi].width,rh); //TODO: optimise it + return QRect(viewItems_[vi].x-1-translation(), y, viewItems_[vi].width+2,viewItems_[vi].height); + } + return QRect(); +} + +//Returns the viewport y coordinate for item. +int StandardView::coordinateForItem(int item) const +{ + if(verticalScrollMode_ == ScrollPerItem) + { + int offset = 0; + //firstVisibleItem must always start a row!!!! + int topViewItemIndex=firstVisibleItem(offset); + if (item >= topViewItemIndex) + { + // search in the visible area first and continue down + // ### slow if the item is not visible + + const int itemsCount = viewItems_.size(); + const int viewportHeight = viewport()->size().height(); + + for(int height = 0, viewItemIndex = topViewItemIndex; + height <= viewportHeight && viewItemIndex < itemsCount; viewItemIndex++) + { + int h=viewItems_[viewItemIndex].height; + if(viewItemIndex ==item) + { + return height; + } + height +=h; + } + } + } + + + return -1; +} + +//coordinate is in viewport coordinates +int StandardView::itemAtCoordinate(const QPoint& coordinate) const +{ + const std::size_t itemCount = viewItems_.size(); + if(itemCount == 0) + return -1; + + if(verticalScrollMode_ == ScrollPerItem) + { + //int topRow = verticalScrollBar()->value(); + + int offset = 0; + int topViewItemIndex=firstVisibleItem(offset); + + if(coordinate.y() >= 0) + { + // the coordinate is in or below the viewport + int viewItemCoordinate = 0; + for(std::size_t viewItemIndex = topViewItemIndex; viewItemIndex < itemCount; viewItemIndex++) + { + viewItemCoordinate += viewItems_[viewItemIndex].height; + if (viewItemCoordinate > coordinate.y()) + { + //viewItemIndex=itemAtRowCoordinate(viewItemIndex,itemsInRow,coordinate.x()+translation()); + return (viewItemIndex >= itemCount ? -1 : viewItemIndex); + } + } + } + } + + return -1; +} + +bool StandardView::isPointInExpandIndicator(int item,QPoint p) const +{ + const int itemCount = static_cast(viewItems_.size()); + return item >=0 && item < itemCount && + p.x() > viewItems_[item].x-expandIndicatorBoxWidth_ && + p.x() < viewItems_[item].x-2; +} + +void StandardView::updateViewport(const QRect rect) +{ + if(rect.right() < viewport()->rect().right()) + viewport()->update(rect.adjusted(0,0,viewport()->rect().right()-rect.right(),0)); + else + viewport()->update(rect); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StandardView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/StandardView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StandardView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StandardView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,63 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef STANDARDVIEW_HPP +#define STANDARDVIEW_HPP + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "AbstractNodeView.hpp" + +class TreeNodeModel; +class GraphNodeViewItem; +class QStyledItemDelegate; + +//Implements a standard tree view (similar to QTreeView) where there is +//one item per row + +class StandardView : public AbstractNodeView +{ +public: + explicit StandardView(TreeNodeModel* model,QWidget *parent=0); + ~StandardView(); + + QRect visualRect(const QModelIndex &index) const; + +protected: + void paint(QPainter *painter,const QRegion& region); + void drawRow(QPainter* painter,int start,int xOffset,int &yp,std::vector&); + + void layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated); + + int itemRow(int item) const; + int coordinateForItem(int item) const; + int itemAtCoordinate(const QPoint& coordinate) const; + bool isPointInExpandIndicator(int,QPoint) const; + + int firstVisibleItem(int &offset) const; + void updateRowCount(); + void updateScrollBars(); + void updateViewport(const QRect rect); + + int expandIndicatorBoxWidth_; + int expandIndicatorWidth_; + +private: + int connectorPos(TreeNodeViewItem* item) const; +}; + +#endif // STANDARDVIEW_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchCombo.cpp ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchCombo.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchCombo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchCombo.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "StringMatchCombo.hpp" + +#include +#include + +StringMatchTb::StringMatchTb(QWidget* parent) : QToolButton(parent) +{ + QIcon ic(QPixmap(":/viewer/edit.svg")); + setIcon(ic); + setAutoRaise(true); + QMenu *menu=new QMenu(this); + menu->addAction("Contains"); //,StringMatchMode::ContainsMatch); + menu->addAction("Matches"); //,StringMatchMode::WildcardMatch); + menu->addAction("Regexp"); //,StringMatchMode::RegexpMatch); + setMenu(menu); + setPopupMode(QToolButton::InstantPopup); +} + +StringMatchCombo::StringMatchCombo(QWidget* parent) : QComboBox(parent) +{ + addItem("contains",StringMatchMode::ContainsMatch); + addItem("matches",StringMatchMode::WildcardMatch); + addItem("regexp",StringMatchMode::RegexpMatch); + + setCurrentIndex(1); +} + +StringMatchMode::Mode StringMatchCombo::matchMode(int index) const +{ + if(index >=0 && index < count()) + { + return static_cast(itemData(index).toInt()); + } + return StringMatchMode::WildcardMatch; +} + +StringMatchMode::Mode StringMatchCombo::currentMatchMode() const +{ + return matchMode(currentIndex()); +} + +void StringMatchCombo::setMatchMode(const StringMatchMode& mode) +{ + int im=mode.toInt(); + for(int i=0; i < count(); i++) + { + if(itemData(i).toInt() == im) + { + setCurrentIndex(i); + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchCombo.hpp ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchCombo.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchCombo.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchCombo.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,37 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_STRINGMATCHCOMBO_HPP_ +#define VIEWER_SRC_STRINGMATCHCOMBO_HPP_ + +#include +#include + +#include "StringMatchMode.hpp" + +class StringMatchTb : public QToolButton +{ +public: + StringMatchTb(QWidget* parent=0); +}; + +class StringMatchCombo : public QComboBox +{ +Q_OBJECT + +public: + explicit StringMatchCombo(QWidget* parent=0); + + StringMatchMode::Mode matchMode(int) const; + StringMatchMode::Mode currentMatchMode() const; + void setMatchMode(const StringMatchMode& mode); +}; + +#endif /* VIEWER_SRC_STRINGMATCHCOMBO_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchMode.cpp ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchMode.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchMode.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchMode.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,56 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + + +#include "StringMatchMode.hpp" + +std::map StringMatchMode::matchOper_; + +StringMatchMode::StringMatchMode() : + mode_(WildcardMatch) +{ + init(); +} + +StringMatchMode::StringMatchMode(Mode mode) : + mode_(mode) +{ + init(); +} + +void StringMatchMode::init() +{ + if(matchOper_.empty()) + { + matchOper_[ContainsMatch]="~"; + matchOper_[WildcardMatch]="="; + matchOper_[RegexpMatch]="=~"; + } +} + +const std::string& StringMatchMode::matchOperator() const +{ + static std::string emptyStr; + std::map::const_iterator it=matchOper_.find(mode_); + if(it != matchOper_.end()) + return it->second; + + return emptyStr; +} + +StringMatchMode::Mode StringMatchMode::operToMode(const std::string& op) +{ + for(std::map::const_iterator it=matchOper_.begin(); it != matchOper_.end(); ++it) + { + if(op == it->second) + return it->first; + } + return InvalidMatch; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchMode.hpp ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchMode.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/StringMatchMode.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/StringMatchMode.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,41 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_STRINGMATCHMODE_HPP_ +#define VIEWER_SRC_STRINGMATCHMODE_HPP_ + +#include +#include + +class StringMatchMode +{ +public: + enum Mode {InvalidMatch=-1,ContainsMatch=0,WildcardMatch=1,RegexpMatch=2}; + + StringMatchMode(); + StringMatchMode(Mode m); + StringMatchMode(const StringMatchMode& r) {mode_=r.mode_;} + + Mode mode() const {return mode_;} + void setMode(Mode m) {mode_=m;} + const std::string& matchOperator() const; + int toInt() const {return static_cast(mode_);} + + static Mode operToMode(const std::string&); + +private: + void init(); + + Mode mode_; + static std::map matchOper_; +}; + + +#endif /* VIEWER_SRC_STRINGMATCHMODE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,492 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "SuiteFilter.hpp" + +#include "SuiteFilterObserver.hpp" +#include "UserMessage.hpp" +#include "VSettings.hpp" +#include "VProperty.hpp" +#include "UiLog.hpp" + +#include +#include + +#define SUITEFILTER_UI_DEBUG_ + +std::string SuiteFilter::dummySuite_="__DUMMY_FOR_UI__"; + +//================================================================= +// +// SuiteFilterItem +// +//================================================================= + +SuiteFilterItem::SuiteFilterItem(const SuiteFilterItem& other) : + name_(other.name_), + loaded_(other.loaded_), + filtered_(other.filtered_) +{ +} + +//================================================================= +// +// SuiteFilter +// +//================================================================= + +SuiteFilter::~SuiteFilter() +{ + std::vector obsCopy=observers_; + for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) + { + (*it)->notifyDelete(this); + } +} + +void SuiteFilter::clear() +{ + items_.clear(); + broadcastChange(); +} + +bool SuiteFilter::adjustLoaded(const std::vector& loaded) +{ +#ifdef SUITEFILTER_UI_DEBUG_ + UI_FUNCTION_LOG +#endif + bool changed=false; + bool filteredChanged=false; + +#ifdef SUITEFILTER_UI_DEBUG_ + UiLog().dbg() << "loaded=" << loaded; + UiLog().dbg() << "items_=" << *this; +#endif + + std::vector currentLoaded; + for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) + { + bool ld=(std::find(loaded.begin(), loaded.end(),(*it).name_) != loaded.end()); + if((*it).loaded_ != ld) + { + (*it).loaded_=ld; + if(ld && + loadedInitialised_ && enabled_ && autoAddNew_) + { + if(!(*it).filtered_) + { + filteredChanged=true; + } + + (*it).filtered_=true; + } + changed=true; + } + if(ld) + currentLoaded.push_back((*it).name_); + } + + + //TODO: do we need to check enabled_ + for(std::vector::const_iterator it=loaded.begin(); it != loaded.end(); ++it) + { + if(std::find(currentLoaded.begin(), currentLoaded.end(),*it) == currentLoaded.end()) + { + bool filtered=loadedInitialised_ && enabled_ && autoAddNew_; + items_.push_back(SuiteFilterItem(*it,true,filtered)); + changed=true; + if(filtered) + { + filteredChanged=true; + } + } + } + + if(!loadedInitialised_ && loaded.size() > 0) + loadedInitialised_=true; + + if(changed) + broadcastChange(); + +#ifdef SUITEFILTER_UI_DEBUG_ + UiLog().dbg() << "(2) items_=" << *this; +#endif + + return filteredChanged; +} + + +std::vector SuiteFilter::loaded() const +{ + std::vector fv; + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it).loaded_) + fv.push_back((*it).name()); + } + return fv; +} + +//Called only once on init +void SuiteFilter::adjustFiltered(const std::vector& filtered) +{ + bool changed=false; + + std::vector currentFiltered; + for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) + { + bool ld=(std::find(filtered.begin(), filtered.end(),(*it).name_) != filtered.end()); + if((*it).filtered_ != ld) + { + (*it).filtered_=ld; + changed=true; + } + if(ld) + currentFiltered.push_back((*it).name_); + } + + for(std::vector::const_iterator it=filtered.begin(); it != filtered.end(); ++it) + { + if(std::find(currentFiltered.begin(), currentFiltered.end(),*it) == currentFiltered.end()) + { + items_.push_back(SuiteFilterItem(*it,false,true)); + changed=true; + } + } + + if(changed) + broadcastChange(); +} + +std::vector SuiteFilter::filter() const +{ + std::vector fv; + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it).filtered_) + fv.push_back((*it).name()); + } + +#ifdef SUITEFILTER_UI_DEBUG_ + UI_FUNCTION_LOG + UiLog().dbg() << "filter=" << fv; +#endif + + return fv; +} + +void SuiteFilter::setFiltered(int index,bool val) +{ + Q_ASSERT(index >=0 && index < count()); + items_[index].filtered_=val; +} + + +bool SuiteFilter::isOnlyOneFiltered(const std::string& oneSuite) const +{ + const std::vector& current=filter(); + return (current.size() == 1 && current[0] != oneSuite); +} + +void SuiteFilter::selectOnlyOne(const std::string& oneSuite) +{ + if(isOnlyOneFiltered(oneSuite)) + return; + + unselectAll(); + for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) + { + if((*it).name() == oneSuite) + { + (*it).filtered_=true; + return; + } + } +} + +SuiteFilter* SuiteFilter::clone() +{ + SuiteFilter* sf=new SuiteFilter(); + sf->items_=items_; + sf->enabled_=enabled_; + sf->autoAddNew_=autoAddNew_; + + return sf; +} + +bool SuiteFilter::loadedSameAs(const std::vector& loaded) const +{ + int cnt=0; + + for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + bool ld=(std::find(loaded.begin(), loaded.end(),(*it).name()) != loaded.end()); + if((*it).loaded() != ld) + return false; + else if(ld && (*it).loaded()) + cnt++; + } + + return cnt == static_cast(loaded.size()); +} + +bool SuiteFilter::setLoaded(const std::vector& loaded,bool checkDiff) +{ + bool same=false; + if(checkDiff) + same=loadedSameAs(loaded); + + if(!checkDiff || !same) + { + return adjustLoaded(loaded); + } + + return false; + //return !same; +} + +bool SuiteFilter::sameAs(const SuiteFilter* sf) const +{ + if(autoAddNew_ != sf->autoAddNewSuites()) + return false; + + if(enabled_ != sf->isEnabled()) + return false; + + if(sf->count() != count()) + return false; + + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i] != sf->items()[i]) + { + return false; + } + } + + return true; +} + +bool SuiteFilter::sameAsLoadedIgnored(const SuiteFilter* sf) const +{ + if(autoAddNew_ != sf->autoAddNewSuites()) + return false; + + if(enabled_ != sf->isEnabled()) + return false; + + if(sf->count() != count()) + return false; + + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i].filtered_ != sf->items()[i].filtered_ || + items_[i].name_ != sf->items()[i].name_ ) + { + return false; + } + } + + return true; +} + +bool SuiteFilter::merge(const SuiteFilter* sf) +{ + bool filteredChanged=false; + + if(enabled_ != sf->enabled_) + { + filteredChanged=true; + } + //enabled_=sf->enabled_; + + if(autoAddNew_ !=sf->autoAddNew_ ) + { + filteredChanged=true; + } + //autoAddNew_=sf->autoAddNew_; + + std::vector oriItems=items_; + + items_=sf->items_; + for(size_t i=0; i < oriItems.size(); i++) + { + for(size_t j=0; j < items_.size(); j++) + { + if(items_[j].name_ == oriItems[i].name_) + { + if(items_[j].filtered_ != oriItems[i].filtered_ ) + { + filteredChanged=true; + } + + //we keep the filtered state from the original items + items_[j].filtered_ = oriItems[i].filtered_; + + break; + } + } + } + + return filteredChanged; +} + +bool SuiteFilter::update(SuiteFilter* sf) +{ + changeFlags_.clear(); + + assert(sf); + + if(autoAddNew_ != sf->autoAddNewSuites()) + { + autoAddNew_ = sf->autoAddNewSuites(); + changeFlags_.set(AutoAddChanged); + } + + if(enabled_ != sf->isEnabled()) + { + enabled_ = sf->isEnabled(); + changeFlags_.set(EnabledChanged); + } + + if(sf->count() != count()) + { + items_=sf->items(); + changeFlags_.set(ItemChanged); + } + else + { + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i] != sf->items()[i]) + { + items_=sf->items(); + changeFlags_.set(ItemChanged); + break; + } + } + } + + bool changed=(changeFlags_.isEmpty() == false); + if(changed) + { + broadcastChange(); + } + return changed; +} + +void SuiteFilter::selectAll() +{ + for(size_t i=0; i < items_.size(); i++) + { + items_[i].filtered_=true; + } +} + +void SuiteFilter::unselectAll() +{ + for(size_t i=0; i < items_.size(); i++) + { + items_[i].filtered_=false; + } +} + +bool SuiteFilter::removeUnloaded() +{ + std::vector v; + + bool changed=false; + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i].loaded()) + { + v.push_back(items_[i]); + } + else + { + changed=true; + } + } + + if(changed) + items_=v; + + return changed; +} + +bool SuiteFilter::hasUnloaded() const +{ + for(size_t i=0; i < items_.size(); i++) + { + if(!items_[i].loaded()) + return true; + } + return false; +} + +void SuiteFilter::addObserver(SuiteFilterObserver* o) +{ + assert(o); + + std::vector::const_iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + observers_.push_back(o); +} + +void SuiteFilter::removeObserver(SuiteFilterObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + observers_.erase(it); +} + +void SuiteFilter::broadcastChange() +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + { + (*it)->notifyChange(this); + } +} + +std::ostream& operator<< ( std::ostream& aStream, const SuiteFilter& obj) +{ + aStream << " " << obj.loadedInitialised_ << " " << obj.enabled_ << " " << obj.autoAddNew_ << std::endl; + for(std::vector::const_iterator it=obj.items_.begin(); it != obj.items_.end(); ++it) + { + aStream << " " << (*it).name() << " " << (*it).filtered() << " " << (*it).loaded() << std::endl; + } + return aStream; +} + +void SuiteFilter::readSettings(VSettings *vs) +{ + enabled_=vs->getAsBool("enabled",enabled_); + autoAddNew_=vs->getAsBool("autoAddNew",autoAddNew_); + + std::vector filter; + if(vs->contains("suites")) + { + vs->get("suites",filter); + } + + adjustFiltered(filter); + + changeFlags_.clear(); + changeFlags_.set(ItemChanged); +} + +void SuiteFilter::writeSettings(VSettings *vs) +{ + vs->putAsBool("enabled",enabled_); + vs->putAsBool("autoAddNew",autoAddNew_); + + std::vector fv=filter(); + if(fv.size() > 0) + vs->put("suites",fv); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,132 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef SUITEFILTER_HPP_ +#define SUITEFILTER_HPP_ + +#include +#include + +#include "FlagSet.hpp" + +class SuiteFilter; +class SuiteFilterObserver; +class VSettings; + +#if 0 +template +class FlagSet +{ +public: + FlagSet() : flags_(0) {} + + void clear() {flags_=0;} + void set(T flag ) { flags_ |= (1 << flag); } + void unset(T flag ) { flags_ &= ~ (1 << flag); } + bool isSet(T flag) const { return (flags_ >> flag) & 1; } + bool isEmpty() const {return flags_==0;} + bool sameAs(T flag) const {return flags_ == flag;} + +private: + int flags_; + +}; + +#endif + +class SuiteFilterItem +{ + friend class SuiteFilter; +public: + SuiteFilterItem(const std::string& name,bool loaded, bool filtered) : + name_(name), loaded_(loaded), filtered_(filtered) {} + + SuiteFilterItem(const SuiteFilterItem& other); + + bool operator!=(const SuiteFilterItem& rhs) const {return name_ != rhs.name_ || loaded_ != rhs.loaded_ || + filtered_ != rhs.filtered_;} + + const std::string& name() const {return name_;} + bool loaded() const {return loaded_;} + bool filtered() const {return filtered_;} + +protected: + std::string name_; + bool loaded_; + bool filtered_; +}; + +class SuiteFilter +{ +public: + SuiteFilter() : autoAddNew_(false), enabled_(false), loadedInitialised_(false) {} + ~SuiteFilter(); + + enum ChangeFlag {AutoAddChanged=1,EnabledChanged=2,ItemChanged=4}; + + friend std::ostream& operator<< ( std::ostream& aStream, const SuiteFilter& obj); + + SuiteFilter* clone(); + + std::vector filter() const; + std::vector loaded() const; + const std::vector& items() const {return items_;} + + void current(const std::vector& suites); + int count() const {return static_cast(items_.size());} + void setFiltered(int index,bool val); + bool isOnlyOneFiltered(const std::string& oneSuite) const; + bool isLoadedInitialised() const {return loadedInitialised_;} + bool autoAddNewSuites() const {return autoAddNew_;} + bool isEnabled() const {return enabled_;} + + void setLoadedInitialised(bool b) {loadedInitialised_=b;} + void setAutoAddNewSuites(bool b) {autoAddNew_=b;} + void setEnabled(bool b) {enabled_=b;} + void selectOnlyOne(const std::string& oneSuite); + void selectAll(); + void unselectAll(); + bool removeUnloaded(); + bool hasUnloaded() const; + + bool sameAs(const SuiteFilter*) const; + bool sameAsLoadedIgnored(const SuiteFilter*) const; + bool merge(const SuiteFilter*); + bool update(SuiteFilter*); + bool setLoaded(const std::vector& loaded,bool checkDiff=true); + bool loadedSameAs(const std::vector& loaded) const; + const FlagSet& changeFlags() {return changeFlags_;} + + bool hasObserver() const {return !observers_.empty();} + void addObserver(SuiteFilterObserver*); + void removeObserver(SuiteFilterObserver*); + + void readSettings(VSettings *vs); + void writeSettings(VSettings *vs); + + static const std::string dummySuite() {return dummySuite_;} + +private: + void clear(); + void adjust(); + void broadcastChange(); + bool adjustLoaded(const std::vector& loaded); + void adjustFiltered(const std::vector& filtered); + + std::vector items_; + bool autoAddNew_; + bool enabled_; + bool loadedInitialised_; + FlagSet changeFlags_; + std::vector observers_; + static std::string dummySuite_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilterObserver.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilterObserver.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteFilterObserver.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteFilterObserver.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,25 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ +#define VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ + +class SuiteFilter; + +class SuiteFilterObserver +{ +public: + SuiteFilterObserver() {} + virtual ~SuiteFilterObserver() {} + + virtual void notifyChange(SuiteFilter*)=0; + virtual void notifyDelete(SuiteFilter*)=0; +}; +#endif /* VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,394 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "SuiteItemWidget.hpp" + +#include + +#include "InfoProvider.hpp" +#include "ServerHandler.hpp" +#include "SuiteFilter.hpp" +#include "SuiteModel.hpp" +#include "VNode.hpp" +#include "VReply.hpp" +#include "UiLog.hpp" + +#define SuiteItemWidget__DEBUG__ + +//======================================================== +// +// SuiteItemWidget +// +//======================================================== + +//Interface to edit the suite filter. +//This class contains a model which itself contains a SuiteFilter object. This is a +//copy of the original SuiteFilter stored by ServerHandler. The idea is that the +//editing is performed on this copy then users need to click apply +//to update the original SuiteFilter. The complication comes from the fact that +//the original SuiteFilter can be modified independently from this interface +//(e.g. suites are added/loaded/removed from the server). Then we need to make sure +//that the changes are merged into the edited state. + +SuiteItemWidget::SuiteItemWidget(QWidget *parent) : + QWidget(parent), + columnsAdjusted_(false), + edited_(false) +{ + setupUi(this); + + infoProvider_=new SuiteProvider(this); + + model_=new SuiteModel(this); + QSortFilterProxyModel* sortModel=new QSortFilterProxyModel(this); + sortModel->setSourceModel(model_); + + suiteView->setUniformRowHeights(true); + suiteView->setSortingEnabled(true); + suiteView->setModel(sortModel); + + connect(model_,SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this,SLOT(slotModelEdited(QModelIndex,QModelIndex))); + + connect(model_,SIGNAL(dataUpdated()), + this,SLOT(slotModelDataUpdated())); + + messageLabel->setShowTypeTitle(false); + messageLabel->setNarrowMode(true); + messageLabel->hide(); + + QFont labelF; + labelF.setBold(true); + labelF.setPointSize(labelF.pointSize()-1); + + controlLabel->setFont(labelF); + controlLabel->setText("" + controlLabel->text() + ""); + + selectLabel->setFont(labelF); + selectLabel->setText("" + selectLabel->text() + ""); + + loadedLabel->setFont(labelF); + loadedLabel->setText("" + loadedLabel->text() + ""); + + QPalette pal=okTb->palette(); + QColor col(10,150,10); + pal.setColor(QPalette::Active,QPalette::Button,col); + pal.setColor(QPalette::Active,QPalette::ButtonText,QColor(Qt::white)); + //okTb->setPalette(pal); + + okTb->setEnabled(false); + resetTb->setEnabled(false); + enableTb->setChecked(false); + + checkActionState(); + + okTb->setEnabled(false); +} + +QWidget* SuiteItemWidget::realWidget() +{ + return this; +} + +void SuiteItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + info_=info; + + if(info_ && info_->server()) + { + //Get the current suitefilter + SuiteFilter *sf=info_->server()->suiteFilter(); + + UiLog().dbg() << "SuiteItemWidget::reload 1"; + sf->filter(); + + assert(sf); + + //The model will be an observer of the suitefilter + model_->setData(sf,info_->server()); + + UiLog().dbg() << "SuiteItemWidget::reload 2"; + sf->filter(); + + if(!columnsAdjusted_) + { + for(int i=0; i < model_->columnCount()-1; i++) + { + suiteView->resizeColumnToContents(i); + suiteView->setColumnWidth(i,suiteView->columnWidth(i) + ((i==0)?25:15)); + } + columnsAdjusted_=true; + } + + suiteView->sortByColumn(0,Qt::AscendingOrder); + + model_->filter()->addObserver(this); + + enableTb->setChecked(sf->isEnabled()); + autoCb->setChecked(sf->autoAddNewSuites()); + + checkActionState(); + + //We update the filter because it might not show the current status. If + //there is a change the model will be notified + + //If the filter is disabled we update the filter with the + //current list of suites in the defs. These are all the suites + //loaded to the server. + if(!sf->isEnabled()) + { + info_->server()->updateSuiteFilterWithDefs(); + } + //If the filter is enabled we need to fetch the total list of suites + //loaded onto the server directly from the server (through the thread) + else + { + //inforReady or infoFailed will always be called. + infoProvider_->info(info_); + } + + UiLog().dbg() << "SuiteItemWidget::reload 3"; + sf->filter(); + } +} + +void SuiteItemWidget::updateData() +{ + /*if(info_.get() && info_->isServer() && info_->server()) + { + model_->updateData(info_->server()->suiteFilter()); + }*/ +} + +void SuiteItemWidget::infoReady(VReply* reply) +{ + suiteView->sortByColumn(0,Qt::AscendingOrder); + //updateData(); +} + +void SuiteItemWidget::infoFailed(VReply* reply) +{ + //commandSent_=false; + //QString s=QString::fromStdString(reply->errorText()); + //checkActionState(); +} + +void SuiteItemWidget::clearContents() +{ + model_->setData(0,0); + InfoPanelItem::clear(); + okTb->setEnabled(false); + resetTb->setEnabled(false); + messageLabel->hide(); +} + +void SuiteItemWidget::updateState(const FlagSet&) +{ + checkActionState(); +} + +void SuiteItemWidget::checkActionState() +{ + if(suspended_) + { + enableTb->setEnabled(false); + autoCb->setEnabled(false); + selectAllTb->setEnabled(false); + unselectAllTb->setEnabled(false); + syncTb->setEnabled(false); + removeTb->setEnabled(false); + suiteView->setEnabled(false); + okTb->setEnabled(false); + resetTb->setEnabled(false); + messageLabel->clear(); + messageLabel->hide(); + return; + } + else + { + enableTb->setEnabled(true); + suiteView->setEnabled(true); + bool st=model_->isEdited(); + if(st) + { + SuiteFilter *sf=model_->filter(); + SuiteFilter *oriSf=model_->realFilter(); + if(sf && oriSf) + { + st=(sf->sameAsLoadedIgnored(oriSf) == false); + } + } + + okTb->setEnabled(st); + resetTb->setEnabled(st); + if(!st) + { + messageLabel->clear(); + messageLabel->hide(); + } + else + { + messageLabel->showTip("You have edited the suite filter! Please click Apply to submit the changes to the server!"); + } + } + + if(enableTb->isChecked()) + { + autoCb->setEnabled(true); + selectAllTb->setEnabled(true); + unselectAllTb->setEnabled(true); + syncTb->setEnabled(true); + + if(SuiteFilter* sf=model_->filter()) + { + autoCb->setChecked(sf->autoAddNewSuites()); + removeTb->setEnabled(sf->hasUnloaded()); + syncTb->setEnabled(true); + + } + } + else + { + autoCb->setEnabled(false); + selectAllTb->setEnabled(false); + unselectAllTb->setEnabled(false); + syncTb->setEnabled(false); + removeTb->setEnabled(false); + } +} + +void SuiteItemWidget::on_autoCb_clicked(bool val) +{ + if(SuiteFilter* sf=model_->filter()) + { + model_->setEdited(true); + sf->setAutoAddNewSuites(val); + checkActionState(); + } +} + +void SuiteItemWidget::on_enableTb_clicked(bool val) +{ + if(SuiteFilter* sf=model_->filter()) + { + sf->setEnabled(val); + model_->setEdited(true); + model_->reloadData(); + checkActionState(); + } + + checkActionState(); +} + +void SuiteItemWidget::on_selectAllTb_clicked(bool) +{ + if(SuiteFilter* sf=model_->filter()) + { + sf->selectAll(); + model_->setEdited(true); + model_->reloadData(); + checkActionState(); + } +} + +void SuiteItemWidget::on_unselectAllTb_clicked(bool) +{ + if(SuiteFilter* sf=model_->filter()) + { + sf->unselectAll(); + model_->setEdited(true); + model_->reloadData(); + checkActionState(); + } +} + +//get a fresh suite list from the server +void SuiteItemWidget::on_syncTb_clicked(bool) +{ + if(info_.get() && info_->server()) + { + infoProvider_->info(info_); + } +} + +void SuiteItemWidget::on_removeTb_clicked(bool val) +{ + if(SuiteFilter* sf=model_->filter()) + { + if(sf->removeUnloaded()) + { + model_->setEdited(true); + model_->reloadData(); + checkActionState(); + } + } + + checkActionState(); +} + +void SuiteItemWidget::on_okTb_clicked(bool) +{ + if(info_.get() && info_->server()) + { + //This replace the edited filter in the model with the one + //stored by the server + model_->setEdited(false); + info_->server()->updateSuiteFilter(model_->filter()); + okTb->setEnabled(false); + resetTb->setEnabled(false); + messageLabel->clear(); + messageLabel->hide(); + } +} + +void SuiteItemWidget::on_resetTb_clicked(bool) +{ + if(info_.get() && info_->server()) + { + model_->setEdited(false); + model_->resetData(); + okTb->setEnabled(false); + resetTb->setEnabled(false); + messageLabel->clear(); + messageLabel->hide(); + } +} + + +void SuiteItemWidget::slotModelEdited(const QModelIndex&,const QModelIndex&) +{ + checkActionState(); +} + +void SuiteItemWidget::slotModelDataUpdated() +{ + checkActionState(); +} + + +void SuiteItemWidget::notifyChange(SuiteFilter *filter) +{ + checkActionState(); +} + +void SuiteItemWidget::notifyDelete(SuiteFilter *filter) +{ + Q_ASSERT(filter); + filter->removeObserver(this); +} + + +static InfoPanelItemMaker maker1("suite"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,68 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef SUITEITEMWIDGET_HPP_ +#define SUITEITEMWIDGET_HPP_ + +#include + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" +#include "SuiteFilterObserver.hpp" + +#include "ui_SuiteItemWidget.h" + +class SuiteModel; + +class SuiteItemWidget : public QWidget, public InfoPanelItem, public SuiteFilterObserver, protected Ui::SuiteItemWidget +{ +Q_OBJECT + +public: + explicit SuiteItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + void infoReady(VReply*); + void infoFailed(VReply*); + void infoProgress(VReply*) {} + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + + void notifyChange(SuiteFilter *filter); + void notifyDelete(SuiteFilter *filter); + +protected Q_SLOTS: + void on_autoCb_clicked(bool); + void on_enableTb_clicked(bool); + void on_selectAllTb_clicked(bool); + void on_unselectAllTb_clicked(bool); + void on_syncTb_clicked(bool); + void on_okTb_clicked(bool); + void on_resetTb_clicked(bool); + void on_removeTb_clicked(bool); + void slotModelEdited(const QModelIndex&,const QModelIndex&); + void slotModelDataUpdated(); + +protected: + void updateData(); + void updateState(const ChangeFlags&); + void checkActionState(); + + SuiteModel *model_; + bool columnsAdjusted_; + bool edited_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,363 @@ + + + SuiteItemWidget + + + + 0 + 0 + 564 + 470 + + + + + 0 + 0 + + + + Form + + + + + + + 1 + + + 0 + + + 3 + + + 2 + + + 0 + + + + + + + + + + + + + + + Qt::ActionsContextMenu + + + true + + + false + + + true + + + false + + + true + + + false + + + + + + + + + + + + 0 + 0 + + + + Apply changes + + + &Apply + + + + :/viewer/submit_round.svg:/viewer/submit_round.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + &Revert changes + + + + :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 15 + + + + + + + + Control: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>By specifiying a <span style=" font-weight:600;">suite filter</span> we can define a subset of suites that ecflowUI needs to keep track of. This can significantly reduce network bandwith and <span style=" font-weight:600;">speed up communication</span> to the server.</p></body></html> + + + Enable filter + + + + :/viewer/filter_decor.svg:/viewer/filter_decor.svg + + + true + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + <html><head/><body><p>By selecting this option all <span style=" font-weight:600;">new suites</span> loaded to the server will be <span style=" font-weight:600;">automatically</span> added to the suite filter and become visible in the views.</p></body></html> + + + Auto add new suites + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 8 + + + + + + + + Selection: + + + + + + + + 0 + 0 + + + + Select all suites + + + Select &all + + + + :/viewer/select_all.svg:/viewer/select_all.svg + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + + 0 + 0 + + + + Unselect all suites + + + &Unselect all + + + + :/viewer/unselect_all.svg:/viewer/unselect_all.svg + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 8 + + + + + + + + Load/unload status: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Get the<span style=" font-weight:600;"> current</span> list of <span style=" text-decoration: underline;">loaded suites</span> from the server. </p></body></html> + + + &Fetch load status + + + + :/viewer/sync_black.svg:/viewer/sync_black.svg + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + + 0 + 0 + + + + Remove all currently unloaded suites from the suite filter list + + + &Remove unloaded + + + + :/viewer/remove.svg:/viewer/remove.svg + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,321 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "SuiteModel.hpp" + +#include "ServerHandler.hpp" +#include "SuiteFilter.hpp" +#include "VNode.hpp" +#include "UiLog.hpp" + +//#define SUITEMODEL_UI_DEBUG_ + +SuiteModel::SuiteModel(QObject *parent) : + QAbstractItemModel(parent), + server_(0), + data_(0), + realData_(0), + presentCol_(QColor(1,128,73)), + notPresentCol_(QColor(255,0,0)), + edited_(false) +{ +} + +SuiteModel::~SuiteModel() +{ + beginResetModel(); + clearData(); + endResetModel(); +} + +void SuiteModel::clearData() +{ + server_=0; + + if(data_) + delete data_; + + data_=0; + + if(realData_) + realData_->removeObserver(this); + + realData_=0; + edited_=false; +} + +void SuiteModel::setEdited(bool e) +{ + edited_=e; +} + +void SuiteModel::setData(SuiteFilter* filter,ServerHandler* server) +{ + beginResetModel(); + + if(data_ && data_ != filter) + { + clearData(); + } + + if(filter) + { + server_=server; + Q_ASSERT(server); + data_=filter->clone(); + realData_=filter; + realData_->addObserver(this); + } + + endResetModel(); +} + +void SuiteModel::notifyChange(SuiteFilter *filter) +{ + if(filter && filter == realData_) + { + updateData(); + } +} + +void SuiteModel::notifyDelete(SuiteFilter *filter) +{ + if(filter && filter == realData_) + { + beginResetModel(); + clearData(); + endResetModel(); + } +} + +void SuiteModel::updateData() +{ +#if 0 + if(realData_ && data_ && + !data_->sameAs(realData_->loaded())) + { + beginResetModel(); + data_->setLoaded(realData_->loaded()); + endResetModel(); + } +#endif + + if(realData_ && data_ && + !data_->sameAs(realData_)) + { + bool filteredChanged=false; + beginResetModel(); + + if(edited_) + filteredChanged=data_->merge(realData_); + else + { + delete data_; + data_=realData_->clone(); + } + + endResetModel(); + + if(filteredChanged) + Q_EMIT dataUpdated(); + } +} + +void SuiteModel::resetData() +{ + beginResetModel(); + + if(data_) + delete data_; + + if(realData_) + data_=realData_->clone(); + + endResetModel(); +} + +void SuiteModel::reloadData() +{ + beginResetModel(); + endResetModel(); +} + +bool SuiteModel::hasData() const +{ + return (data_ && data_->count() > 0); +} + +int SuiteModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return 3; +} + +int SuiteModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + //Parent is the root: + if(!parent.isValid()) + { + return data_->count(); + } + + return 0; +} + +Qt::ItemFlags SuiteModel::flags ( const QModelIndex & index) const +{ + Qt::ItemFlags defaultFlags; + + if(data_->isEnabled()) + { + defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; // | Qt::ItemIsEditable; + } + + return defaultFlags; +} + +QVariant SuiteModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid() || !hasData()) + { + return QVariant(); + } + + //Data lookup can be costly so we immediately return a default value for all + //the cases where the default should be used. + //if(role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::ForegroundRole) + //{ + // return QVariant(); + //} + + int row=index.row(); + if(row < 0 || row >= data_->count()) + return QVariant(); + + if(role == Qt::DisplayRole) + { + switch(index.column()) + { + case 0: + return QString::fromStdString(data_->items().at(row).name()); + break; + case 1: + return (data_->items().at(row).loaded())?"loaded":"not loaded"; + break; + case 2: + { + if(data_->items().at(row).loaded()) + { + Q_ASSERT(server_); + int n=server_->vRoot()->totalNumOfTopLevel(data_->items().at(row).name()); + if(n != -1) + return n; + else + return QVariant(); + } + else + return QVariant(); + } + break; + default: + break; + } + } + else if(role == Qt::CheckStateRole) + { + if(index.column()==0) + return (data_->items().at(row).filtered())?QVariant(Qt::Checked):QVariant(Qt::Unchecked); + } + else if(role == Qt::ForegroundRole) + { + if(!data_->isEnabled()) + { + return QVariant(); + } + else if(index.column() == 1) + { + return (data_->items().at(row).loaded())?presentCol_:notPresentCol_; + } + return QVariant(); + } + + return QVariant(); +} + +bool SuiteModel::setData(const QModelIndex& index, const QVariant & value, int role ) +{ + if(index.column() == 0 && role == Qt::CheckStateRole) + { + int row=index.row(); + if(row >=0 && row < data_->count()) + { + bool checked=(value.toInt() == Qt::Checked)?true:false; + data_->setFiltered(row,checked); + edited_=true; + Q_EMIT dataChanged(index,index); + return true; + } + } + + return false; +} + +QVariant SuiteModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QAbstractItemModel::headerData( section, orient, role ); + + if(role == Qt::DisplayRole) + { + switch ( section ) + { + case 0: return tr("Suite"); + case 1: return tr("Load status on server"); + case 2: return tr("Number of children"); + default: return QVariant(); + } + } + else if(role== Qt::ToolTipRole) + { + switch ( section ) + { + case 0: return tr("Filter status of suite"); + case 1: return tr("Indicates if the suite is currently loaded on the server.

    \ + Please note: this information might not be up to date for unfiltered suites. Use Fetch load status \ + to get the actual load status list."); + case 2: return tr("Number of children"); + default: return QVariant(); + } + } + return QVariant(); +} + +QModelIndex SuiteModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex SuiteModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/SuiteModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/SuiteModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/SuiteModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/SuiteModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,58 @@ +#ifndef SUITEMODEL_H +#define SUITEMODEL_H + +#include +#include + +#include "SuiteFilterObserver.hpp" + +class ServerHandler; +class SuiteFilter; + +class SuiteModel : public QAbstractItemModel, public SuiteFilterObserver +{ + Q_OBJECT +public: + explicit SuiteModel(QObject *parent=0); + ~SuiteModel(); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + bool setData(const QModelIndex & index, const QVariant & value, int role ); + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + void setData(SuiteFilter* filter,ServerHandler* server); + void reloadData(); + void resetData(); + + SuiteFilter* filter() const {return data_;} + SuiteFilter* realFilter() const {return realData_;} + void setEdited(bool); + bool isEdited() const {return edited_;} + + void notifyChange(SuiteFilter*); + void notifyDelete(SuiteFilter*); + +Q_SIGNALS: + void dataUpdated(); + +protected: + bool hasData() const; + void clearData(); + void updateData(); + + ServerHandler* server_; + SuiteFilter* data_; + SuiteFilter* realData_; + QColor presentCol_; + QColor notPresentCol_; + bool edited_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TableFilterWidget.hpp" + +#include +#include +#include + +#include "FilterWidget.hpp" +#include "NodeFilterDialog.hpp" +#include "NodeQuery.hpp" +#include "NodeQueryOption.hpp" +#include "ServerFilter.hpp" +#include "UiLog.hpp" +#include "VFilter.hpp" + +#include + +TableFilterWidget::TableFilterWidget(QWidget *parent) : + QWidget(parent), + filterDef_(0), + serverFilter_(0) +{ + setupUi(this); + + connect(queryTe_,SIGNAL(clicked()), + this,SLOT(slotEdit())); + + numLabel_->hide(); + + slotTotalNumChanged(0); + + //queryTe_->setFixedHeight(18); +} + +void TableFilterWidget::slotEdit() +{ + assert(filterDef_); + assert(serverFilter_); + + NodeFilterDialog d(this); + d.setServerFilter(serverFilter_); + d.setQuery(filterDef_->query()); + if(d.exec() == QDialog::Accepted) + { + filterDef_->setQuery(d.query()); + UiLog().dbg() << "new table query: " << filterDef_->query()->query(); + } +} + +void TableFilterWidget::build(NodeFilterDef* def,ServerFilter *sf) +{ + filterDef_=def; + serverFilter_=sf; + + connect(filterDef_,SIGNAL(changed()), + this,SLOT(slotDefChanged())); + + slotDefChanged(); +} + +void TableFilterWidget::slotDefChanged() +{ + QColor bg(240,240,240); + queryTe_->setHtml(filterDef_->query()->sqlQuery()); +} + +void TableFilterWidget::slotHeaderFilter(QString column,QPoint globalPos) +{ + NodeQuery *q=filterDef_->query()->clone(); + if(column == "status") + { + QMenu *menu=new QMenu(this); + //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); + + NodeStateFilter sf; + NodeQueryListOption* op=q->stateOption(); + Q_ASSERT(op); + //if(!op->selection().isEmpty()) + sf.setCurrent(op->selection()); + + //The menu takes ownership of it + new VParamFilterMenu(menu,&sf,"Status filter", + VParamFilterMenu::FilterMode,VParamFilterMenu::ColourDecor); + + if(menu->exec(globalPos) != NULL) + { + //if(sf.isComplete()) + // op->setSelection(QStringList()); + //else + op->setSelection(sf.currentAsList()); + + //this will create deep copy so we can delet q in the end + filterDef_->setQuery(q); + } + + delete menu; + } + + delete q; +} + +void TableFilterWidget::slotTotalNumChanged(int n) +{ + numLabel_->setText(tr("Total: ") + QString::number(n)); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TABLEFILTERWIDGET_INC_ +#define TABLEFILTERWIDGET_INC_ + +#include "ui_TableFilterWidget.h" + +#include + +class NodeFilterDef; +class ServerFilter; + +class TableFilterWidget : public QWidget, private Ui::TableFilterWidget +{ +Q_OBJECT + +public: + explicit TableFilterWidget(QWidget *parent=0); + ~TableFilterWidget() {} + + void build(NodeFilterDef*,ServerFilter*); + +public Q_SLOTS: + void slotEdit(); + void slotDefChanged(); + void slotHeaderFilter(QString column,QPoint globalPos); + void slotTotalNumChanged(int); + +private: + NodeFilterDef* filterDef_; + ServerFilter* serverFilter_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TableFilterWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableFilterWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,79 @@ + + + TableFilterWidget + + + + 0 + 0 + 412 + 118 + + + + Form + + + + 2 + + + 0 + + + 0 + + + 0 + + + + + Filter: + + + + + + + + + + + + + + + + + true + + + Breadcrumbs + + + Show breadcrumbs + + + + + true + + + Frozen + + + Do not update panel when node changes + + + + + + OneLineTextEdit + QTextEdit +
    OneLineTextEdit.hpp
    +
    +
    + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,595 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TableNodeModel.hpp" + +#include + +#include "DiagData.hpp" +#include "IconProvider.hpp" +#include "ModelColumn.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VConfig.hpp" +#include "VFilter.hpp" +#include "VIcon.hpp" +#include "VModelData.hpp" +#include "VNode.hpp" +#include "VNState.hpp" + +//#define _UI_TABLENODEMODEL_DEBUG + +//static int hitCount=0; + +static std::map attrTypes; +static VAttributeType* columnToAttrType(TableNodeModel::ColumnType ct); + +VAttributeType* columnToAttrType(TableNodeModel::ColumnType ct) +{ + std::map::const_iterator it= + attrTypes.find(ct); + return (it != attrTypes.end())?it->second:0; +} + + +//======================================================= +// +// TableNodeModel +// +//======================================================= + + +TableNodeModel::TableNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef,QObject *parent) : + AbstractNodeModel(parent), + data_(0), + columns_(0) +{ + columns_=ModelColumn::def("table_columns"); + + Q_ASSERT(columns_); + + //Check the mapping between the enum and column ids (only for the non-extra columns!) + Q_ASSERT(columns_->id(PathColumn) == "path"); + Q_ASSERT(columns_->id(StatusColumn) == "status"); + Q_ASSERT(columns_->id(TypeColumn) == "type"); + Q_ASSERT(columns_->id(TriggerColumn) == "trigger"); + Q_ASSERT(columns_->id(LabelColumn) == "label"); + Q_ASSERT(columns_->id(EventColumn) == "event"); + Q_ASSERT(columns_->id(MeterColumn) == "meter"); + Q_ASSERT(columns_->id(StatusChangeColumn) == "statusChange"); + + if(attrTypes.empty()) + { + QList ctLst; + ctLst << TriggerColumn << LabelColumn << EventColumn << MeterColumn; + Q_FOREACH(ColumnType ct,ctLst) + { + VAttributeType* t=VAttributeType::find(columns_->id(ct).toStdString()); + Q_ASSERT(t); + attrTypes[ct]=t; + } + } + + //Create the data handler for the model. + data_=new VTableModelData(filterDef,this); + + data_->reset(serverFilter); + + //We need to react to changes in the extra columns! + connect(columns_,SIGNAL(appendItemBegin()), + this,SLOT(slotAppendColumnBegin())); + + connect(columns_,SIGNAL(appendItemEnd()), + this,SLOT(slotAppendColumnEnd())); + + connect(columns_,SIGNAL(addItemsBegin(int,int)), + this,SLOT(slotAddColumnsBegin(int,int))); + + connect(columns_,SIGNAL(addItemsEnd(int,int)), + this,SLOT(slotAddColumnsEnd(int,int))); + + connect(columns_,SIGNAL(changeItemBegin(int)), + this,SLOT(slotChangeColumnBegin(int))); + + connect(columns_,SIGNAL(changeItemEnd(int)), + this,SLOT(slotChangeColumnEnd(int))); + + connect(columns_,SIGNAL(removeItemsBegin(int,int)), + this,SLOT(slotRemoveColumnsBegin(int,int))); + + connect(columns_,SIGNAL(removeItemsEnd(int,int)), + this,SLOT(slotRemoveColumnsEnd(int,int))); + + //pixmap + diagPixId_=IconProvider::add(":/viewer/diag.svg","diag.svg"); +} + +VModelData* TableNodeModel::data() const +{ + return data_; +} + +int TableNodeModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return columns_->count(); +} + +int TableNodeModel::rowCount( const QModelIndex& parent) const +{ +#ifdef _UI_TABLENODEMODEL_DEBUG + UiLog().dbg() << "rowCount=" << parent; +#endif + + //There are no servers + if(!hasData()) + { + return 0; + } + //We use only column 0 + else if(parent.column() > 0) + { + return 0; + } + //"parent" is the root + else if(!parent.isValid()) + { + //The total number of nodes displayed + int cnt=0; + for(int i=0; i < data_->count(); i++) + { + if(!data_->server(i)->inScan()) + cnt+=data_->numOfNodes(i); + } +#ifdef _UI_TABLENODEMODEL_DEBUG + //UiLog().dbg() << "table count " << cnt; +#endif + return cnt; + } + + return 0; +} + + +QVariant TableNodeModel::data( const QModelIndex& index, int role ) const +{ + //Data lookup can be costly so we immediately return a default value for all + //the cases where the default should be used. + if( !index.isValid() || + (role != Qt::DisplayRole && role != Qt::ToolTipRole && + role != Qt::BackgroundRole && role != IconRole && role != SortRole)) + { + return QVariant(); + } + + //We only display nodes!! + return nodeData(index,role); +} + +QVariant TableNodeModel::nodeData(const QModelIndex& index, int role) const +{ + VNode* vnode=indexToNode(index); + if(!vnode || !vnode->node()) + return QVariant(); + + if(index.column() < 0) + return QVariant(); + + ColumnType id=ExtraColumn; + if(index.column() < ExtraColumn) + { + id=static_cast(index.column()); + } + + if(role == Qt::DisplayRole) + { + //QString id=columns_->id(index.column()); + + if(id == PathColumn) + { + return QString::fromStdString(vnode->absNodePath()); + } + else if(id == StatusColumn) + return vnode->stateName(); + else if(id == TypeColumn) + return QString::fromStdString(vnode->nodeType()); + + //Attributes + else if(id == EventColumn || id == LabelColumn || id == MeterColumn || + id == TriggerColumn) + { + if(VAttribute* a=vnode->attributeForType(0,columnToAttrType(id))) + return a->data(true); + else + return QVariant(); + } + + else if(id == StatusChangeColumn) + { + QString s; + vnode->statusChangeTime(s); + return s; + } + //Extra columns added by the user - they all represent ecflow variables!!! + else if(id == ExtraColumn) + { + Q_ASSERT(columns_->isExtra(index.column())); + QString n=columns_->id(index.column()); + if(!n.isEmpty()) + { + //Standard variable column + if(columns_->isEditable(index.column())) + return QString::fromStdString(vnode->findInheritedVariable(n.toStdString())); + //extra diagnostic column + else + { + DiagData* diag=DiagData::instance(); + int diagCol=index.column()-columns_->diagStartIndex(); + if(diagCol >= 0) + return QString::fromStdString(diag->dataAt(vnode,diagCol)); + } + } + } + } + else if(role == Qt::BackgroundRole) + { + return vnode->stateColour(); + } + else if(role == IconRole) + { + if(id == PathColumn) + return VIcon::pixmapList(vnode,0); + else + return QVariant(); + } + else if(role == SortRole) + { + if(id == MeterColumn) + { + if(VAttribute* a=vnode->attributeForType(0,columnToAttrType(id))) + { + std::string val; + if(a->value("meter_value",val)) + return QString::fromStdString(val).toInt(); + } + return -9999; + } + else if(id == StatusChangeColumn) + { + return vnode->statusChangeTime(); + } + } + + return QVariant(); +} + +QVariant TableNodeModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal) + return QAbstractItemModel::headerData( section, orient, role ); + + if (section < 0 || section >= columns_->count()) // this can happen during a server reset + return QVariant(); + + if(role == Qt::DisplayRole) + { + return columns_->label(section); + } + //the id of the column + else if(role == Qt::UserRole) + { + return columns_->id(section); + } + else if(role == Qt::ToolTipRole) + { + if(section < ExtraColumn) + { + return columns_->tooltip(section); + } + else if(columns_->isEditable(section)) + { + return tr("Displays the value of the given ecFlow variable (can be changed or removed)"); + } + else + { + return tr("Extra diagnostic"); + } + } + else if(role == VariableRole) + { + return (section >= ExtraColumn && columns_->isEditable(section))?true:false; + } + else if(role == Qt::DecorationRole) + { + if(section >= ExtraColumn && !columns_->isEditable(section)) + { + return IconProvider::pixmap(diagPixId_,12); + } + } + + return QVariant(); +} + + +QModelIndex TableNodeModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0 || parent.isValid()) + { + return QModelIndex(); + } + + if(VNode *node=data_->nodeAt(row)) + { + return createIndex(row,column,node); + } + + return QModelIndex(); +} + +QModelIndex TableNodeModel::parent(const QModelIndex& /*child*/) const +{ + //Parent is always the root!!! + return QModelIndex(); +} + +//---------------------------------------------- +// +// Server to index mapping and lookup +// +//---------------------------------------------- + +VNode* TableNodeModel::indexToNode( const QModelIndex & index) const +{ + if(index.isValid()) + { + return static_cast(index.internalPointer()); + } + return NULL; +} + +QModelIndex TableNodeModel::nodeToIndex(const VNode* node, int column) const +{ + if(!node) + return QModelIndex(); + + int row=0; + if((row=data_->position(node)) != -1) + { + return createIndex(row,column,const_cast(node)); + } + return QModelIndex(); +} + + +//Find the index for the node when we know what the server is! +QModelIndex TableNodeModel::nodeToIndex(VTableServer* server,const VNode* node, int column) const +{ + if(!node) + return QModelIndex(); + + int row=0; + if((row=data_->position(server,node)) != -1) + { + return createIndex(row,column,const_cast(node)); + } + return QModelIndex(); +} + +QModelIndex TableNodeModel::attributeToIndex(const VAttribute* a, int column) const +{ + if(!a) + return QModelIndex(); + + VNode* node=a->parent(); + if(!node) + return QModelIndex(); + + int row=0; + if((row=data_->position(node)) != -1) + { + return createIndex(row,column,const_cast(node)); + } + return QModelIndex(); +} + +void TableNodeModel::selectionChanged(QModelIndexList lst) +{ +#if 0 + Q_FOREACH(QModelIndex idx,lst) + { + VInfo_ptr info=nodeInfo(idx); + + for(int i=0; i < data_->count(); i++) + { + VTableServer *ts=data_->server(i)->tableServer(); + Q_ASSERT(ts); + ts->clearForceShow(info->item()); + } + } +#endif +} + +VInfo_ptr TableNodeModel::nodeInfo(const QModelIndex& index) +{ + VNode *n=indexToNode(index); + if(n) + { + return VInfoNode::create(n); + } + + VInfo_ptr info; + return info; +} + +//Server is about to be added +void TableNodeModel::slotServerAddBegin(int /*row*/) +{ +} + +//Addition of the new server has finished +void TableNodeModel::slotServerAddEnd() +{ +} + +//Server is about to be removed +void TableNodeModel::slotServerRemoveBegin(VModelServer* server,int num) +{ + Q_ASSERT(active_ == true); + Q_ASSERT(server); + + if(num >0) + { + int start=-1; + int count=-1; + data_->position(server->tableServer(),start,count); + + Q_ASSERT(start >=0); + Q_ASSERT(count == num); + + beginRemoveRows(QModelIndex(),start,start+count-1); + } +} + +//Removal of the server has finished +void TableNodeModel::slotServerRemoveEnd(int num) +{ + assert(active_ == true); + + if(num >0) + endRemoveRows(); +} + +//The node changed (it status etc) +void TableNodeModel::slotNodeChanged(VTableServer* server,const VNode* node) +{ + if(!node) + return; + + QModelIndex index=nodeToIndex(server,node,0); + + if(!index.isValid()) + return; + + Q_EMIT dataChanged(index,index); +} + +void TableNodeModel::slotBeginServerScan(VModelServer* server,int num) +{ + Q_ASSERT(active_ == true); + Q_ASSERT(server); + +#ifdef _UI_TABLENODEMODEL_DEBUG + UiLog().dbg() << "TableNodeModel::slotBeginServerScan --> " << server->realServer()->name() << + " " << num; +#endif + + if(num >0) + { + int count=num; + int start=data_->position(server->tableServer()); + beginInsertRows(QModelIndex(),start,start+count-1); + } +} + +void TableNodeModel::slotEndServerScan(VModelServer* server,int num) +{ + assert(active_ == true); + +#ifdef _UI_TABLENODEMODEL_DEBUG + UiLog().dbg() << "TableNodeModel::slotEndServerScan --> " << server->realServer()->name() << + " " << num; + QTime t; + t.start(); +#endif + + if(num >0) + endInsertRows(); + +#ifdef _UI_TABLENODEMODEL_DEBUG + UiLog().dbg() << " elapsed: " << t.elapsed() << " ms"; + UiLog().dbg() << "<-- slotEndServerScan"; +#endif +} + +void TableNodeModel::slotBeginServerClear(VModelServer* server,int num) +{ + Q_ASSERT(active_ == true); + Q_ASSERT(server); + + if(num >0) + { + int start=-1; + int count=-1; + data_->position(server->tableServer(),start,count); + + Q_ASSERT(start >=0); + Q_ASSERT(count == num); + + beginRemoveRows(QModelIndex(),start,start+count-1); + } +} + +void TableNodeModel::slotEndServerClear(VModelServer* server,int num) +{ + assert(active_ == true); + + if(num >0) + endRemoveRows(); +} + +//======================================= +// Column management +//======================================= + +void TableNodeModel::removeColumn(QString name) +{ + Q_ASSERT(columns_); + columns_->removeExtraItem(name); +} + +void TableNodeModel::slotAppendColumnBegin() +{ + int col=columnCount(); + beginInsertColumns(QModelIndex(),col,col); +} + +void TableNodeModel::slotAppendColumnEnd() +{ + endInsertColumns(); +} + +void TableNodeModel::slotAddColumnsBegin(int idxStart,int idxEnd) +{ + beginInsertColumns(QModelIndex(),idxStart,idxEnd); +} + +void TableNodeModel::slotAddColumnsEnd(int idxStart,int idxEnd) +{ + endInsertColumns(); +} + +void TableNodeModel::slotChangeColumnBegin(int /*idx*/) +{ +} + +void TableNodeModel::slotChangeColumnEnd(int idx) +{ + Q_EMIT dataChanged(index(0,idx),index(rowCount(),idx)); +} + +void TableNodeModel::slotRemoveColumnsBegin(int idxStart,int idxEnd) +{ + beginRemoveColumns(QModelIndex(),idxStart,idxEnd); +} + +void TableNodeModel::slotRemoveColumnsEnd(int idxStart,int idxEnd) +{ + endRemoveColumns(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,106 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TABLENODEMODEL_H +#define TABLENODEMODEL_H + +#include + +#include "AbstractNodeModel.hpp" +#include "VInfo.hpp" + +class ModelColumn; +class Node; +class NodeFilterDef; +class ServerFilter; +class ServerHandler; +class VTableModelData; +class VTableServer; + +class TableNodeModel : public AbstractNodeModel +{ +Q_OBJECT + + friend class TableNodeSortModel; + +public: + TableNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef,QObject *parent=0); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + VInfo_ptr nodeInfo(const QModelIndex&); + void selectionChanged(QModelIndexList lst); + + VModelData* data() const; + void removeColumn(QString); + ModelColumn* columns() const {return columns_;} + + //To speed up identifying a column. The mapping here must match the definition of + //"table_columns" in ecflowview_view_conf.json !!! + enum ColumnType {InvalidColumn=-1, PathColumn=0,StatusColumn=1,TypeColumn=2,TriggerColumn=3, + LabelColumn=4, EventColumn=5, MeterColumn=6, StatusChangeColumn=7, + ExtraColumn = 8 }; + +public Q_SLOTS: + void slotServerAddBegin(int); + void slotServerAddEnd(); + void slotServerRemoveBegin(VModelServer* server,int); + void slotServerRemoveEnd(int); + + void slotDataChanged(VModelServer*) {} + void slotNodeChanged(VTableServer*,const VNode*); + void slotAttributesChanged(VModelServer*,const VNode*) {} + void slotBeginAddRemoveAttributes(VModelServer*,const VNode*,int,int) {} + void slotEndAddRemoveAttributes(VModelServer*,const VNode*,int,int) {} + + void slotBeginServerScan(VModelServer* server,int); + void slotEndServerScan(VModelServer* server,int); + void slotBeginServerClear(VModelServer* server,int); + void slotEndServerClear(VModelServer* server,int); + + void slotAppendColumnBegin(); + void slotAppendColumnEnd(); + void slotAddColumnsBegin(int,int); + void slotAddColumnsEnd(int,int); + void slotChangeColumnBegin(int idx); + void slotChangeColumnEnd(int idx); + void slotRemoveColumnsBegin(int,int); + void slotRemoveColumnsEnd(int,int); + +Q_SIGNALS: + void filterChangeBegun(); + void filterChangeEnded(); + +protected: + bool isServer(const QModelIndex & index) const {return false;} + ServerHandler* indexToRealServer(const QModelIndex & index) const {return NULL;} + VModelServer* indexToServer(const QModelIndex & index) const {return NULL;} + QModelIndex serverToIndex(ServerHandler*) const {return QModelIndex();} + + QModelIndex nodeToIndex(VTableServer* server,const VNode* node, int column) const; + QModelIndex nodeToIndex(const VNode*,int column=0) const; + VNode* indexToNode( const QModelIndex & index) const; + + QModelIndex attributeToIndex(const VAttribute* a, int column=0) const; + + QVariant nodeData(const QModelIndex& index,int role) const; + + VTableModelData* data_; + ModelColumn* columns_; + int diagPixId_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeSortModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeSortModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeSortModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeSortModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,97 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TableNodeSortModel.hpp" + +#include "TableNodeModel.hpp" +#include "ModelColumn.hpp" + +TableNodeSortModel::TableNodeSortModel(TableNodeModel* nodeModel,QObject *parent) : + QSortFilterProxyModel(parent), + nodeModel_(nodeModel), + skipSort_(false) +{ + Q_ASSERT(nodeModel_); + //connect(nodeModel_,SIGNAL(filterChanged()), + // this,SLOT(slotFilterChanged())); + + QSortFilterProxyModel::setSourceModel(nodeModel_); + + setDynamicSortFilter(false); +} + +TableNodeSortModel::~TableNodeSortModel() +{ +} + +VInfo_ptr TableNodeSortModel::nodeInfo(const QModelIndex& index) +{ + return nodeModel_->nodeInfo(mapToSource(index)); +} + +QModelIndex TableNodeSortModel::infoToIndex(VInfo_ptr info) +{ + return mapFromSource(nodeModel_->infoToIndex(info)); +} + +QModelIndex TableNodeSortModel::nodeToIndex(const VNode *node) +{ + return mapFromSource(nodeModel_->nodeToIndex(node)); +} + +void TableNodeSortModel::selectionChanged(QModelIndexList lst) +{ + QModelIndexList lstm; + Q_FOREACH(QModelIndex idx,lst) + lstm << mapToSource(idx); + + nodeModel_->selectionChanged(lstm); +} + + +bool TableNodeSortModel::lessThan(const QModelIndex &left, + const QModelIndex &right) const +{ + if(skipSort_) + return true; + + TableNodeModel::ColumnType id=static_cast(left.column()); + + if(id == TableNodeModel::PathColumn) + return left.row() < right.row(); + + else if(id == TableNodeModel::MeterColumn) + { + return left.data(AbstractNodeModel::SortRole).toInt() < + right.data(AbstractNodeModel::SortRole).toInt(); + } + + else if(id == TableNodeModel::StatusChangeColumn) + { + return left.data(AbstractNodeModel::SortRole).toUInt() < + right.data(AbstractNodeModel::SortRole).toUInt(); + } + + QVariant leftData = nodeModel_->data(left); + QVariant rightData = nodeModel_->data(right); + + return leftData.toString() < rightData.toString(); +} + +void TableNodeSortModel::removeColumn(QString name) +{ + nodeModel_->removeColumn(name); +} + +ModelColumn* TableNodeSortModel::columns() const +{ + nodeModel_->columns(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeSortModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeSortModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeSortModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeSortModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,46 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TABLENODEFILTERMODEL_H +#define TABLENODEFILTERMODEL_H + +#include + +#include "VInfo.hpp" + +class TableNodeModel; +class NodeFilterDef; +class ModelColumn; + +class TableNodeSortModel : public QSortFilterProxyModel +{ +public: + TableNodeSortModel(TableNodeModel*,QObject *parent=0); + ~TableNodeSortModel(); + + //From QSortFilterProxyModel: + //we set the source model in the constructor. So this function should not do anything. + void setSourceModel(QAbstractItemModel*) {} + + VInfo_ptr nodeInfo(const QModelIndex&); + QModelIndex infoToIndex(VInfo_ptr); + QModelIndex nodeToIndex(const VNode *node); + void selectionChanged(QModelIndexList lst); + void setSkipSort(bool b) {skipSort_=b;} + void removeColumn(QString); + ModelColumn* columns() const; + +protected: + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + + TableNodeModel* nodeModel_; + bool skipSort_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,836 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TableNodeView.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ActionHandler.hpp" +#include "AddModelColumnDialog.hpp" +#include "FilterWidget.hpp" +#include "IconProvider.hpp" +#include "TableNodeSortModel.hpp" +#include "PropertyMapper.hpp" +#include "TableNodeModel.hpp" +#include "TableNodeViewDelegate.hpp" +#include "UiLog.hpp" +#include "VFilter.hpp" +#include "VNode.hpp" +#include "VSettings.hpp" + +#define _UI_TABLENODEVIEW_DEBUG + +TableNodeView::TableNodeView(TableNodeSortModel* model,NodeFilterDef* filterDef,QWidget* parent) : + QTreeView(parent), + NodeViewBase(filterDef), + model_(model), + needItemsLayout_(false), + prop_(NULL), + setCurrentIsRunning_(false) +{ + setObjectName("view"); + setProperty("style","nodeView"); + setProperty("view","table"); + + setRootIsDecorated(false); + + //We enable sorting but do not want to perform it immediately + setSortingEnabledNoExec(true); + + //setSortingEnabled(false); + //sortByColumn(-1,Qt::AscendingOrder); + + setAllColumnsShowFocus(true); + setUniformRowHeights(true); + setMouseTracking(true); + setSelectionMode(QAbstractItemView::ExtendedSelection); + + //!!!!We need to do it because: + //The background colour between the views left border and the nodes cannot be + //controlled by delegates or stylesheets. It always takes the QPalette::Highlight + //colour from the palette. Here we set this to transparent so that Qt could leave + //this area empty and we fill it appropriately in our delegate. + QPalette pal=palette(); + pal.setColor(QPalette::Highlight,QColor(128,128,128,0)); + setPalette(pal); + + //Context menu + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + + //Selection + connect(this,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClickItem(const QModelIndex))); + + actionHandler_=new ActionHandler(this,this); + + //expandAll(); + + //Header + header_=new TableNodeHeader(this); + + setHeader(header_); + + //Set header ContextMenuPolicy + header_->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(header_,SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotHeaderContextMenu(const QPoint &))); + + connect(header_,SIGNAL(customButtonClicked(QString,QPoint)), + this,SIGNAL(headerButtonClicked(QString,QPoint))); + + //for(int i=0; i < model_->columnCount(QModelIndex())-1; i++) + // resizeColumnToContents(i); + + /*connect(header(),SIGNAL(sectionMoved(int,int,int)), + this, SLOT(slotMessageTreeColumnMoved(int,int,int)));*/ + + QTreeView::setModel(model_); + + //Create delegate to the view + TableNodeViewDelegate *delegate=new TableNodeViewDelegate(this); + setItemDelegate(delegate); + + connect(delegate,SIGNAL(sizeHintChangedGlobal()), + this,SLOT(slotSizeHintChangedGlobal())); + + //Properties + std::vector propVec; + propVec.push_back("view.table.background"); + prop_=new PropertyMapper(propVec,this); + + //Initialise bg + adjustBackground(prop_->find("view.table.background")->value().value()); + + header_->setSortIndicatorShown(true); +} + +TableNodeView::~TableNodeView() +{ + delete prop_; +} + +void TableNodeView::setModel(TableNodeSortModel *model) +{ + model_= model; + + //Set the model. + QTreeView::setModel(model_); +} + +QWidget* TableNodeView::realWidget() +{ + return this; +} + +QObject* TableNodeView::realObject() +{ + return this; +} + +//Enable sorting without actually performing it!!! +void TableNodeView::setSortingEnabledNoExec(bool b) +{ + if(b) + { + model_->setSkipSort(true); + setSortingEnabled(true); + model_->setSkipSort(false); + } + else + { + setSortingEnabled(false); + } +} + +//Collects the selected list of indexes +QModelIndexList TableNodeView::selectedList() +{ + QModelIndexList lst; + Q_FOREACH(QModelIndex idx,selectedIndexes()) + if(idx.column() == 0) + lst << idx; + return lst; +} + + +// reimplement virtual function from QTreeView - called when the selection is changed +void TableNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QModelIndexList lst=selectedIndexes(); + if(lst.count() > 0) + { + VInfo_ptr info=model_->nodeInfo(lst.front()); + if(info && !info->isEmpty()) + { +#ifdef _UI_TABLENODEVIEW_DEBUG + UiLog().dbg() << "TableNodeView::selectionChanged --> emit=" << info->path(); +#endif + Q_EMIT selectionChanged(info); + } + } + QTreeView::selectionChanged(selected, deselected); + + //The model has to know about the selection in order to manage the + //nodes that are forced to be shown + model_->selectionChanged(lst); +} + +VInfo_ptr TableNodeView::currentSelection() +{ + QModelIndexList lst=selectedIndexes(); + if(lst.count() > 0) + { + return model_->nodeInfo(lst.front()); + } + return VInfo_ptr(); +} + +void TableNodeView::setCurrentSelection(VInfo_ptr info) +{ + //While the current is being selected we do not allow + //another setCurrent call go through + if(setCurrentIsRunning_) + return; + + setCurrentIsRunning_=true; + QModelIndex idx=model_->infoToIndex(info); + if(idx.isValid()) + { +#ifdef _UI_TABLENODEVIEW_DEBUG + if(info) + UiLog().dbg() << "TableNodeView::setCurrentSelection --> " << info->path(); +#endif + setCurrentIndex(idx); + } + setCurrentIsRunning_=false; +} + +void TableNodeView::slotDoubleClickItem(const QModelIndex&) +{ +} + +void TableNodeView::slotContextMenu(const QPoint &position) +{ + QModelIndexList lst=selectedList(); + //QModelIndex index=indexAt(position); + QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); + + handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); +} + + +void TableNodeView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) +{ + //Node actions + if(indexClicked.isValid() && indexClicked.column() == 0) //indexLst[0].isValid() && indexLst[0].column() == 0) + { + UiLog().dbg() << "context menu " << indexClicked; + + std::vector nodeLst; + for(int i=0; i < indexLst.count(); i++) + { + VInfo_ptr info=model_->nodeInfo(indexLst[i]); + if(!info->isEmpty()) + nodeLst.push_back(info); + } + + actionHandler_->contextMenu(nodeLst,globalPos); + } + + //Desktop actions + else + { + } +} + +void TableNodeView::slotViewCommand(VInfo_ptr info,QString cmd) +{ +} + +void TableNodeView::rerender() +{ + if(needItemsLayout_) + { + doItemsLayout(); + needItemsLayout_=false; + } + else + { + viewport()->update(); + } +} + +void TableNodeView::slotRerender() +{ + rerender(); +} + +void TableNodeView::slotSizeHintChangedGlobal() +{ + needItemsLayout_=true; +} + +void TableNodeView::adjustBackground(QColor col) +{ + if(col.isValid()) + { + QString sh="QTreeView { background : " + col.name() + ";}"; + setStyleSheet(sh); + } +} + +void TableNodeView::notifyChange(VProperty* p) +{ + if(p->path() == "view.table.background") + { + adjustBackground(p->value().value()); + } +} + +//========================================= +// Header +//========================================= + +void TableNodeView::collectVariableNames(std::set& vars) +{ + Q_ASSERT(model_); + + //collect the list of avialable variables using the selected node + //or if it is not valid the first row in the table + QModelIndex idx=currentIndex(); + if(!idx.isValid()) + { + idx=model_->index(0,0); + } + + if(idx.isValid()) + { + VInfo_ptr info=model_->nodeInfo(idx); + if(info) + { + if(VNode* vn=info->node()) + { + vn->collectInheritedVariableNames(vars); + } + } + } +} + + +void TableNodeView::slotAddVariableColumn() +{ + Q_ASSERT(model_); + + std::set vars; + collectVariableNames(vars); + + AddModelColumnDialog d(this); + d.init(model_->columns(),vars); + d.exec(); +} + +void TableNodeView::changeVariableColumn(QString varName) +{ + Q_ASSERT(model_); + + std::set vars; + collectVariableNames(vars); + + ChangeModelColumnDialog d(this); + d.init(model_->columns(),vars,varName); + d.setColumn(varName); + d.exec(); +} + +void TableNodeView::slotHeaderContextMenu(const QPoint &position) +{ + int section=header_->logicalIndexAt(position); + if(section< 0 || section >= header_->count()) + return; + + int visCnt=0; + for(int i=0; i count(); i++) + { + if(!header_->isSectionHidden(i)) + visCnt++; + } + + QList lst; + QMenu *menu=new QMenu(this); + QAction *ac; + + //Show/hide current columns + QString name=header_->model()->headerData(section,Qt::Horizontal).toString(); + ac=new QAction(menu); + ac->setData("show_current"); + bool vis=!header_->isSectionHidden(section); + ac->setText(((!vis)?"Show column \'":"Hide column \'") + name + "\'"); + if(vis && visCnt <=1) + { + ac->setEnabled(false); + } + menu->addAction(ac); + + menu->addSeparator(); + + //Add special menu for variable columns + bool varColumn=header_->model()->headerData(section,Qt::Horizontal,AbstractNodeModel::VariableRole).toBool(); + QString varName; + + if(varColumn) + { + varName=header_->model()->headerData(section,Qt::Horizontal).toString(); + + ac=new QAction(menu); + ac->setText(tr("Change column \'") + varName + "\'"); + ac->setData("change"); + menu->addAction(ac); + + ac=new QAction(menu); + ac->setText(tr("Remove column \'") + varName + "\'"); + ac->setIcon(QPixmap(":viewer/remove.svg")); + ac->setData("remove"); + menu->addAction(ac); + + menu->addSeparator(); + } + + //Submenu to control the visibility of other columns + QMenu *visMenu=menu->addMenu("Show/hide other columns"); + for(int i=0; i count(); i++) + { + if(i != section) + { + name=header_->model()->headerData(i,Qt::Horizontal).toString(); + ac=new QAction(visMenu); + ac->setText(name); + ac->setCheckable(true); + ac->setData(i); + + bool vis=!header_->isSectionHidden(i); + ac->setChecked(vis); + + if(vis && visCnt <=1) + { + ac->setEnabled(false); + } + + visMenu->addAction(ac); + } + } + + //Show the context menu and check selected action + ac=menu->exec(header_->mapToGlobal(position)); + if(ac && ac->isEnabled()) + { + if(ac->data().toString() == "change") + { + changeVariableColumn(varName); + } + else if(ac->data().toString() == "remove") + { + delete menu; + if(QMessageBox::question(0,tr("Confirm: remove column"), + tr("Are you sure that you want to remove column ") + varName + "?", + QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Ok) + { + model_->removeColumn(varName); + } + return; + } + else if(ac->data().toString() == "show_current") + { + header_->setSectionHidden(section,!header_->isSectionHidden(section)); + } + else if(ac->isCheckable()) + { + int i=ac->data().toInt(); + header_->setSectionHidden(i,!ac->isChecked()); + } + } + delete menu; +} + +void TableNodeView::readSettings(VSettings* vs) +{ + vs->beginGroup("column"); + + std::vector orderVec; + std::vector visVec, wVec; + + vs->get("order",orderVec); + vs->get("visible",visVec); + vs->get("width",wVec); + + vs->endGroup(); + + if(orderVec.size() != visVec.size() || orderVec.size() != wVec.size()) + return; + + for(size_t i=0; i < orderVec.size(); i++) + { + std::string id=orderVec[i]; + for(int j=0; j < model_->columnCount(QModelIndex()); j++) + { + if(model_->headerData(j,Qt::Horizontal,Qt::UserRole).toString().toStdString() == id) + { + if(visVec[i] == 0) + header()->setSectionHidden(j,true); + + else if(wVec[i] > 0) + setColumnWidth(j,wVec[i]); + + break; + } + } + } + + if(header_->count() > 0) + { + int visCnt=0; + for(int i=0; i < header_->count(); i++) + if(!header_->isSectionHidden(i)) + visCnt++; + + if(visCnt==0) + header()->setSectionHidden(0,false); + } +} + +void TableNodeView::writeSettings(VSettings* vs) +{ + vs->beginGroup("column"); + + std::vector orderVec; + std::vector visVec, wVec; + for(int i=0; i < model_->columnCount(QModelIndex()); i++) + { + std::string id=model_->headerData(i,Qt::Horizontal,Qt::UserRole).toString().toStdString(); + orderVec.push_back(id); + visVec.push_back((header()->isSectionHidden(i))?0:1); + wVec.push_back(columnWidth(i)); + } + + vs->put("order",orderVec); + vs->put("visible",visVec); + vs->put("width",wVec); + + vs->endGroup(); +} + +//========================================= +// TableNodeHeader +//========================================= + +TableNodeHeader::TableNodeHeader(QWidget *parent) : QHeaderView(Qt::Horizontal, parent) +{ + setStretchLastSection(true); + + connect(this, SIGNAL(sectionResized(int, int, int)), + this, SLOT(slotSectionResized(int))); + + int pixId=IconProvider::add(":viewer/filter_decor.svg","filter_decor"); + + customPix_=IconProvider::pixmap(pixId,10); + + + //connect(this, SIGNAL(sectionMoved(int, int, int)), this, + // SLOT(handleSectionMoved(int, int, int))); + + //setMovable(true); +} + +void TableNodeHeader::showEvent(QShowEvent *e) +{ + /* for(int i=0;isetGeometry(sectionViewportPosition(i),0, + sectionSize(i),height()); + widgets_[i]->show(); + } + } + */ + + QHeaderView::showEvent(e); +} + +void TableNodeHeader::slotSectionResized(int i) +{ + /*for (int j=visualIndex(i);jsetGeometry(sectionViewportPosition(logical), height()/2, + sectionSize(logical) - 16, height()); + } + }*/ +} + +QSize TableNodeHeader::sizeHint() const +{ + return QHeaderView::sizeHint(); + + QSize s = size(); + //s.setHeight(headerSections[0]->minimumSizeHint().height() + 35); + //s.setHeight(2*35); + return s; +} + +void TableNodeHeader::setModel(QAbstractItemModel *model) +{ + if(model) + { + for(int i=0; i< model->columnCount(); i++) + { + QString id=model->headerData(i,Qt::Horizontal,Qt::UserRole).toString(); + if(id == "status") + customButton_.insert(i,TableNodeHeaderButton(id)); + } + } + QHeaderView::setModel(model); +} + +void TableNodeHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const +{ + painter->save(); + //QHeaderView::paintSection(painter, rect, logicalIndex); + //painter->restore(); + + + /*QPixmap customPix(":viewer/filter_decor.svg"); + QRect cbRect(0,0,12,12); + cbRect.moveCenter(QPoint(rect.right()-16-6,rect.center().y())); + customButton_[logicalIndex].setRect(cbRect); + painter->drawPixmap(cbRect,pix);*/ + + if (!rect.isValid()) + return; + + QStyleOptionHeader opt; + initStyleOption(&opt); + QStyle::State state = QStyle::State_None; + if(isEnabled()) + state |= QStyle::State_Enabled; + if(window()->isActiveWindow()) + state |= QStyle::State_Active; + + bool clickable; + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + clickable=sectionsClickable(); +#else + clickable=isClickable(); +#endif + + if(clickable) + { + /*if (logicalIndex == d->hover) + state |= QStyle::State_MouseOver; + if (logicalIndex == d->pressed) + state |= QStyle::State_Sunken; + else if (d->highlightSelected) { + if (d->sectionIntersectsSelection(logicalIndex)) + state |= QStyle::State_On; + if (d->isSectionSelected(logicalIndex)) + state |= QStyle::State_Sunken; + }*/ + + } + + // if(isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) + // opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder) + // ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; + + // setup the style options structure + //QVariant textAlignment = model->headerData(logicalIndex, d->orientation, + // Qt::TextAlignmentRole); + opt.rect = rect; + opt.section = logicalIndex; + opt.state |= state; + //opt.textAlignment = Qt::Alignment(textAlignment.isValid() + // ? Qt::Alignment(textAlignment.toInt()) + // : d->defaultAlignment); + + //opt.text = model()->headerData(logicalIndex, Qt::Horizontal), + // Qt::DisplayRole).toString(); + + QVariant foregroundBrush; + if (foregroundBrush.canConvert()) + opt.palette.setBrush(QPalette::ButtonText, qvariant_cast(foregroundBrush)); + + QPointF oldBO = painter->brushOrigin(); + QVariant backgroundBrush; + if (backgroundBrush.canConvert()) + { + opt.palette.setBrush(QPalette::Button, qvariant_cast(backgroundBrush)); + opt.palette.setBrush(QPalette::Window, qvariant_cast(backgroundBrush)); + painter->setBrushOrigin(opt.rect.topLeft()); + } + + // the section position + int visual = visualIndex(logicalIndex); + assert(visual != -1); + + if (count() == 1) + opt.position = QStyleOptionHeader::OnlyOneSection; + else if (visual == 0) + opt.position = QStyleOptionHeader::Beginning; + else if (visual == count() - 1) + opt.position = QStyleOptionHeader::End; + else + opt.position = QStyleOptionHeader::Middle; + + opt.orientation = Qt::Horizontal; + + // the selected position + /*bool previousSelected = d->isSectionSelected(logicalIndex(visual - 1)); + bool nextSelected = d->isSectionSelected(logicalIndex(visual + 1)); + if (previousSelected && nextSelected) + opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected; + else if (previousSelected) + opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected; + else if (nextSelected) + opt.selectedPosition = QStyleOptionHeader::NextIsSelected; + else + opt.selectedPosition = QStyleOptionHeader::NotAdjacent; + */ + + // draw the section + style()->drawControl(QStyle::CE_Header, &opt, painter, this); + painter->setBrushOrigin(oldBO); + + painter->restore(); + + + int rightPos=rect.right(); + if(isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) + opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder) + ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; + + if (opt.sortIndicator != QStyleOptionHeader::None) + { + QStyleOptionHeader subopt = opt; + subopt.rect = style()->subElementRect(QStyle::SE_HeaderArrow, &opt, this); + rightPos=subopt.rect.left(); + style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &subopt, painter, this); + } + + + QMap::iterator it=customButton_.find(logicalIndex); + if(it != customButton_.end()) + { + //Custom button + QStyleOptionButton optButton; + + //visPbOpt.text="Visualise"; + optButton.state = QStyle::State_AutoRaise ; //QStyle::State_Active | QStyle::State_Enabled; + //optButton.icon=customIcon_; + //optButton.iconSize=QSize(12,12); + + int buttonWidth=customPix_.width(); + int buttonHeight=buttonWidth; + optButton.rect = QRect(rightPos-4-buttonWidth,(rect.height()-buttonWidth)/2, + buttonWidth,buttonHeight); + + painter->drawPixmap(optButton.rect,customPix_); + + rightPos=optButton.rect.left(); + it.value().setRect(optButton.rect); + } + + //Text is left aligned, a decoration icon might be added to left just before the text + QString text=model()->headerData(logicalIndex,Qt::Horizontal).toString(); + QRect textRect=rect; + textRect.setRight(rightPos-5); + textRect.adjust(2,0,0,0); + + //Draw icon to the left of the text + QVariant pixVa=model()->headerData(logicalIndex,Qt::Horizontal,Qt::DecorationRole); + if(pixVa.type() == QVariant::Pixmap) + { + QPixmap pix=pixVa.value(); + int pixH=qMin(pix.height(),rect.height()-2); + + QFont f; + QFontMetrics fm(f); + int textLeft=textRect.x(); + QRect pixRect=QRect(rect.x()+3,rect.center().y()-pixH/2, + pix.width(),pix.height()); + if(pixRect.x()+pixRect.width() + fm.width(text) + 4 < textRect.x() + textRect.width()) + { + painter->drawPixmap(pixRect,pix); + textRect.setX(pixRect.x()+pixRect.width() + 3); + } + } + + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + //style()->drawControl(QStyle::CE_PushButton, &optButton,painter,this); +} + +void TableNodeHeader::mousePressEvent(QMouseEvent *event) +{ + QMap::const_iterator it = customButton_.constBegin(); + while(it != customButton_.constEnd()) + { + if(it.value().rect_.contains(event->pos())) + { + UiLog().dbg() << "header " << it.key() << " clicked"; + Q_EMIT customButtonClicked(it.value().id(),event->globalPos()); + } + ++it; + } + + QHeaderView::mousePressEvent(event); +} + +/*void TableNodeHeader::mouseMoveEvent(QMouseEvent *event) +{ + int prevIndex=hoverIndex_; + QMap::const_iterator it = customButton_.constBegin(); + while(it != customButton_.constEnd()) + { + if(it.value().rect_.contains(event->pos())) + { + hoverIndex_=it.key(); + if(hoveIndex != prevIndex) + { + rerender; + } + } + ++it; + } + + if(preIndex !=-1) + { + + } + hoverIndex_=-1; +}*/ + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeViewDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeViewDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,266 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TableNodeViewDelegate.hpp" + +#include +#include +#include +#include +#include + +#include + +#include "AbstractNodeModel.hpp" +#include "Animation.hpp" +#include "IconProvider.hpp" +#include "ModelColumn.hpp" +#include "PropertyMapper.hpp" + +static std::vector propVec; + +//Define node renderer properties +struct TableNodeDelegateBox : public NodeDelegateBox +{ + TableNodeDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=3; + rightMargin=0; + topPadding=0; + bottomPadding=0; + leftPadding=2; + rightPadding=1; + } +}; + +//Define attribute renderer properties +struct TableAttrDelegateBox : public AttrDelegateBox +{ + TableAttrDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=1; + rightMargin=0; + topPadding=0; + bottomPadding=0; + leftPadding=0; + rightPadding=0; + } +}; + +TableNodeViewDelegate::TableNodeViewDelegate(QWidget *parent) +{ + borderPen_=QPen(QColor(230,230,230)); + + columns_=ModelColumn::def("table_columns"); + + nodeBox_=new TableNodeDelegateBox; + attrBox_=new TableAttrDelegateBox; + + nodeBox_->adjust(font_); + attrFont_=font_; + attrBox_->adjust(attrFont_); + + //Property + if(propVec.empty()) + { + propVec.push_back("view.table.font"); + + //Base settings + addBaseSettings(propVec); + } + + prop_=new PropertyMapper(propVec,this); + + updateSettings(); +} + +TableNodeViewDelegate::~TableNodeViewDelegate() +{ +} + +void TableNodeViewDelegate::updateSettings() +{ + if(VProperty* p=prop_->find("view.table.font")) + { + QFont newFont=p->value().value(); + + if(font_ != newFont) + { + font_=newFont; + attrFont_=newFont; + nodeBox_->adjust(font_); + attrBox_->adjust(attrFont_); + Q_EMIT sizeHintChangedGlobal(); + } + } + + //Update the settings handled by the base class + updateBaseSettings(); +} + +QSize TableNodeViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QSize size=QStyledItemDelegate::sizeHint(option,index); + return QSize(size.width(),nodeBox_->sizeHintCache.height()); +} + + +void TableNodeViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ + //Background +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QStyleOptionViewItem vopt(option); +#else + QStyleOptionViewItemV4 vopt(option); +#endif + + initStyleOption(&vopt, index); + + const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); + const QWidget* widget = vopt.widget; + + //Save painter state + painter->save(); + + QString id=columns_->id(index.column()); + + if(id == "path") + { + QString text=index.data(Qt::DisplayRole).toString(); + renderNode(painter,index,vopt,text); + } + else if(id == "status") + { + renderStatus(painter,index,vopt); + } + + //Render attributes + else if(id == "event" || id == "label" || id == "meter" || id == "trigger") + { + QVariant va=index.data(Qt::DisplayRole); + if(va.type() == QVariant::StringList) + { + QStringList lst=va.toStringList(); + if(lst.count() > 0) + { + QMap::const_iterator it=attrRenderers_.find(lst.at(0)); + if(it != attrRenderers_.end()) + { + QSize size; + AttributeRendererProc a=it.value(); + (this->*a)(painter,lst,vopt,size); + } + } + } + } + + //rest of the columns + else + { + QString text=index.data(Qt::DisplayRole).toString(); + QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); + painter->setFont(font_); + painter->setPen(Qt::black); + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + } + + //Render the horizontal border for rows. We only render the top border line. + //With this technique we miss the bottom border line of the last row!!! + //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); + QRect bgRect=option.rect; + painter->setPen(borderPen_); + painter->drawLine(bgRect.topLeft(),bgRect.topRight()); + + painter->restore(); +} + +void TableNodeViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const +{ + bool selected=option.state & QStyle::State_Selected; + QFontMetrics fm(font_); + + //The initial filled rect (we will adjust its width) + QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); + + //The text rectangle + QRect textRect = itemRect; + + int textWidth=fm.width(text); + textRect.setWidth(textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding); + + //Adjust the filled rect width + int currentRight=textRect.x()+textRect.width(); + + //Icons area + QList pixLst; + QList pixRectLst; + + QVariant va=index.data(AbstractNodeModel::IconRole); + if(va.type() == QVariant::List) + { + QVariantList lst=va.toList(); + if(lst.count() >0) + { + int xp=currentRight+nodeBox_->iconPreGap; + int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; + for(int i=0; i < lst.count(); i++) + { + int id=lst[i].toInt(); + if(id != -1) + { + pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); + pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); + xp+=nodeBox_->iconSize+nodeBox_->iconGap; + } + } + + if(!pixLst.isEmpty()) + { + currentRight=xp-nodeBox_->iconGap; + } + } + } + + //Define clipping + int rightPos=currentRight+1; + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw text + QColor fg=index.data(Qt::ForegroundRole).value(); + painter->setPen(fg); + painter->setFont(font_); + painter->drawText(textRect,Qt::AlignHCenter | Qt::AlignVCenter,text); + + if(selected) + { + QRect sr=textRect; + sr.setX(option.rect.x()+nodeBox_->leftMargin); + renderSelectionRect(painter,sr); + } + + //Draw icons + for(int i=0; i < pixLst.count(); i++) + { + painter->drawPixmap(pixRectLst[i],pixLst[i]); + } + + if(setClipRect) + { + painter->restore(); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeViewDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeViewDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,52 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TABLENODEVIEWDELEGATE_HPP +#define TABLENODEVIEWDELEGATE_HPP + +#include +#include +#include +#include +#include + +#include "NodeViewDelegate.hpp" +#include "VProperty.hpp" + +#include + +class ModelColumn; + +class TableNodeViewDelegate : public NodeViewDelegate +{ + Q_OBJECT +public: + explicit TableNodeViewDelegate(QWidget *parent=0); + ~TableNodeViewDelegate(); + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + +Q_SIGNALS: + void sizeHintChangedGlobal(); + +protected: + void updateSettings(); + + void renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const; + + ModelColumn* columns_; + QPen borderPen_; +}; + +#endif // TABLENODEVIEWDELEGATE_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,126 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TABLENODEVIEW_HPP_ +#define TABLENODEVIEW_HPP_ + +#include +#include +#include + +#include "NodeViewBase.hpp" + +#include "VInfo.hpp" +#include "VProperty.hpp" + +class QComboBox; + +class ActionHandler; +class TableNodeModel; +class TableNodeSortModel; +class NodeFilterDef; +class PropertyMapper; +class TableNodeHeader; + +class TableNodeView : public QTreeView, public NodeViewBase, public VPropertyObserver +{ +Q_OBJECT +public: + explicit TableNodeView(TableNodeSortModel* model,NodeFilterDef* filterDef,QWidget *parent=0); + ~TableNodeView(); + + void reload() {} + void rerender(); + QWidget* realWidget(); + QObject* realObject(); + VInfo_ptr currentSelection(); + void setCurrentSelection(VInfo_ptr n); + void selectFirstServer() {} + void setModel(TableNodeSortModel *model); + + void notifyChange(VProperty* p); + + void readSettings(VSettings*); + void writeSettings(VSettings*); + +public Q_SLOTS: + void slotDoubleClickItem(const QModelIndex&); + void slotContextMenu(const QPoint &position); + void slotViewCommand(VInfo_ptr,QString); + void slotHeaderContextMenu(const QPoint &position); + void slotSizeHintChangedGlobal(); + void slotRerender(); + void slotAddVariableColumn(); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + void infoPanelCommand(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + void headerButtonClicked(QString,QPoint); + +protected: + QModelIndexList selectedList(); + void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); + void adjustBackground(QColor col); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + void setSortingEnabledNoExec(bool b); + void collectVariableNames(std::set& vars); + void changeVariableColumn(QString varName); + + TableNodeSortModel* model_; + ActionHandler* actionHandler_; + TableNodeHeader* header_; + bool needItemsLayout_; + PropertyMapper* prop_; + bool setCurrentIsRunning_; +}; + +class TableNodeHeaderButton +{ +public: + TableNodeHeaderButton(QString id) : id_(id) {} + + QString id() const {return id_;} + void setRect(QRect r) {rect_=r;} + QRect rect() const {return rect_;} + + QString id_; + QRect rect_; +}; + +class TableNodeHeader : public QHeaderView +{ +Q_OBJECT + +public: + explicit TableNodeHeader(QWidget *parent=0); + + QSize sizeHint() const; + void setModel(QAbstractItemModel *model); + +public Q_SLOTS: + void slotSectionResized(int i); + +Q_SIGNALS: + void customButtonClicked(QString,QPoint); + +protected: + void showEvent(QShowEvent *QSize); + void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; + void mousePressEvent(QMouseEvent *event); + + QPixmap customPix_; + mutable QMap customButton_; +}; + +#endif + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,240 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#include "TableNodeWidget.hpp" + +#include +#include + +#include "AbstractNodeModel.hpp" +#include "DashboardDock.hpp" +#include "FilterWidget.hpp" +#include "NodePathWidget.hpp" +#include "NodeViewBase.hpp" +#include "TableFilterWidget.hpp" +#include "TableNodeModel.hpp" +#include "TableNodeSortModel.hpp" +#include "TableNodeView.hpp" +#include "VFilter.hpp" +#include "VSettings.hpp" +#include "WidgetNameProvider.hpp" + +#include + +TableNodeWidget::TableNodeWidget(ServerFilter* serverFilter,bool interactive,QWidget * parent) : + NodeWidget("table",serverFilter,parent), + sortModel_(0) +{ + //Init qt-creator form + setupUi(this); + + bcWidget_=new NodePathWidget(this); + + //This defines how to filter the nodes in the tree. + filterDef_=new NodeFilterDef(serverFilter_,NodeFilterDef::GeneralScope); + + //Build the filter widget + filterW_->build(filterDef_,serverFilter_); + + //pop up the editor to define a filter. Only when the users + //creates a new table view + if(interactive) + { + filterW_->slotEdit(); + } + + //Create the table model. It uses the datahandler to access the data. + TableNodeModel* tModel=new TableNodeModel(serverFilter_,filterDef_,this); + model_=tModel; + + //Create a filter model for the tree. + sortModel_=new TableNodeSortModel(tModel,this); + + //Create the view + QHBoxLayout *hb=new QHBoxLayout(viewHolder_); + hb->setContentsMargins(0,0,0,0); + hb->setSpacing(0); + TableNodeView *tv=new TableNodeView(sortModel_,filterDef_,this); + hb->addWidget(tv); + + //Store the pointer to the (non-QObject) base class of the view!!! + view_=tv; + + //Signals-slots + + connect(view_->realWidget(),SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChangedInView(VInfo_ptr))); + + connect(view_->realWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(popInfoPanel(VInfo_ptr,QString))); + + connect(view_->realWidget(),SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), + this,SLOT(slotSelectionChangedInBc(VInfo_ptr))); + + connect(view_->realWidget(),SIGNAL(headerButtonClicked(QString,QPoint)), + filterW_,SLOT(slotHeaderFilter(QString,QPoint))); + +#if 0 + connect(model_,SIGNAL(clearBegun(const VNode*)), + view_->realWidget(),SLOT(slotSaveExpand(const VNode*))); + + connect(model_,SIGNAL(scanEnded(const VNode*)), + view_->realWidget(),SLOT(slotRestoreExpand(const VNode*))); +#endif + + connect(model_,SIGNAL(rerender()), + view_->realWidget(),SLOT(slotRerender())); + + //This will not emit the trigered signal of the action!! + //Synchronise the action and the breadcrumbs state + actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); + + //The node status filter is exposed via a menu. So we need a reference to it. + states_=filterDef_->nodeState(); + + WidgetNameProvider::nameChildren(this); +} + +TableNodeWidget::~TableNodeWidget() +{ + +} + +void TableNodeWidget::populateDockTitleBar(DashboardDockTitleWidget* tw) +{ + //Builds the menu for the settings tool button + QMenu *menu=new QMenu(this); + menu->setTearOffEnabled(true); + + menu->addAction(actionBreadcrumbs); + +#if 0 + QMenu *menuState=menu->addMenu(tr("Status")); + menuState->setTearOffEnabled(true); + + //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); + stateFilterMenu_=new VParamFilterMenu(menuState,states_,"Status filter", + //VParamFilterMenu::FilterMode,VParamFilterMenu::ColourDecor); + VParamFilterMenu::ShowMode,VParamFilterMenu::ColourDecor); + +#endif + //Sets the menu on the toolbutton + tw->optionsTb()->setMenu(menu); + + //Add the bc to the titlebar + tw->setBcWidget(bcWidget_); + + //Sets the title + //tw->slotUpdateTitle("Table"); + + QList acLst; + + //Edit filter + QAction* acFilterEdit=new QAction(this); + acFilterEdit->setIcon(QPixmap(":viewer/filter_edit.svg")); + acFilterEdit->setToolTip("Edit filter ..."); + acLst << acFilterEdit; + + connect(acFilterEdit,SIGNAL(triggered()), + filterW_,SLOT(slotEdit())); + + //Add variable column + QAction* acVar=new QAction(this); + acVar->setIcon(QPixmap(":viewer/dock_add_variable_column.svg")); + acVar->setToolTip("Add variable column ..."); + acLst << acVar; + + connect(acVar,SIGNAL(triggered()), + view_->realWidget(),SLOT(slotAddVariableColumn())); + + tw->addActions(acLst); +} + + +void TableNodeWidget::slotSelectionChangedInView(VInfo_ptr info) +{ + updateActionState(info); + bcWidget_->setPath(info); + if(broadcastSelection()) + Q_EMIT selectionChanged(info); +} + +void TableNodeWidget::on_actionBreadcrumbs_triggered(bool b) +{ + if(b) + { + bcWidget_->setMode(NodePathWidget::GuiMode); + } + else + { + bcWidget_->setMode(NodePathWidget::TextMode); + } +} + +void TableNodeWidget::rerender() +{ + bcWidget_->rerender(); + view_->rerender(); +} + + +void TableNodeWidget::writeSettings(VComboSettings* vs) +{ + vs->put("type",type_); + vs->put("dockId",id_); + + bcWidget_->writeSettings(vs); + + states_->writeSettings(vs); + filterDef_->writeSettings(vs); + + view_->writeSettings(vs); + + DashboardWidget::writeSettings(vs); +} + +void TableNodeWidget::readSettings(VComboSettings* vs) +{ + std::string type=vs->get("type",""); + if(type != type_) + return; + + //This will not emit the changed signal. So the "observers" will + //not notice the change. + states_->readSettings(vs); + filterDef_->readSettings(vs); + + //The model at this point is inactive (not using its data). We make it active: + // -it will instruct its data provider to filter the data according + // to the current settings + // -it will load and display the data + model_->active(true); + + //-------------------------- + //Breadcrumbs + //-------------------------- + + bcWidget_->readSettings(vs); + + //Synchronise the action and the breadcrumbs state + //This will not emit the trigered signal of the action!! + actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); + + view_->readSettings(vs); + + DashboardWidget::readSettings(vs); +} + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,49 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#ifndef TABLENODEWIDGET_HPP_ +#define TABLENODEWIDGET_HPP_ + +#include "ui_TableNodeWidget.h" + +#include "NodeWidget.hpp" + +class NodeStateFilter; +class TableNodeSortModel; +class VParamFilterMenu; +class VSettings; + +class TableNodeWidget : public NodeWidget, protected Ui::TableNodeWidget +{ +Q_OBJECT + +public: + TableNodeWidget(ServerFilter* servers,bool interactive,QWidget* parent=0); + ~TableNodeWidget(); + + void populateDockTitleBar(DashboardDockTitleWidget* tw); + void rerender(); + + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + +protected Q_SLOTS: + void on_actionBreadcrumbs_triggered(bool b); + void slotSelectionChangedInView(VInfo_ptr info); + +protected: + void detachedChanged() {} + +private: + TableNodeSortModel *sortModel_; + VParamFilterMenu *stateFilterMenu_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TableNodeWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TableNodeWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,75 @@ + + + TableNodeWidget + + + + 0 + 0 + 683 + 491 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + + + + 0 + 0 + + + + + + + + + + + true + + + Breadcrumbs + + + Show breadcrumbs + + + + + + TableFilterWidget + QWidget +
    TableFilterWidget.hpp
    + 1 +
    +
    + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TabWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TabWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TabWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TabWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,403 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TabWidget.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "UserMessage.hpp" + +void IconTabBar::paintEvent(QPaintEvent *e) +{ + QStylePainter painter(this); + for(int i = 0; i < count(); ++i) + { + QStyleOptionTabV2 option; + initStyleOption(&option, i); + painter.drawItemPixmap(option.rect, Qt::AlignTop|Qt::AlignHCenter, option.icon.pixmap(option.iconSize)); + //painter.drawItemText(option.rect, Qt::AlignBottom|Qt::AlignHCenter, palette(), 1, option.text); + } +} + +TabWidget::TabWidget(QWidget* parent) : + QWidget(parent), + beingCleared_(false) +{ + //Main layout + QVBoxLayout* layout = new QVBoxLayout(this); + layout->setSpacing(0); + layout->setContentsMargins(0, 0, 0, 0); + + //Horizontal layout for the tab bar + QHBoxLayout* hb = new QHBoxLayout(this); + hb->setSpacing(0); + hb->setContentsMargins(0, 0, 0, 0); + layout->addLayout(hb); + + //Tab bar + bar_ = new QTabBar(this); + bar_->setObjectName("bar"); + hb->addWidget(bar_, 1); + + bar_->setProperty("nodePanel","1"); + bar_->setMovable(true); + //bar_->setExpanding(true); + + //QString st=bar_->styleSheet(); + //st+="QTabBar::tab{padding: 4px;}"; + //st+="QTabBar::tab {margin-left: 4px;}"; + //st+="QTabBar::tab:selected {font: bold;}"; + //bar_->setStyleSheet(st); + + //Add tab button on the right + addTb_ = new QToolButton(this); + addTb_->setObjectName("addTb"); + addTb_->setAutoRaise(true); + addTb_->setIcon(QPixmap(":/viewer/add_tab.svg")); + addTb_->setToolTip(tr("Open a new tab")); + hb->addWidget(addTb_); + + //Tab list menu + tabListTb_=new QToolButton(this); + tabListTb_->setObjectName("tabListTb"); + tabListTb_->setAutoRaise(true); + tabListTb_->setIcon(QPixmap(":/viewer/menu_arrow_down.svg")); + tabListTb_->setToolTip(tr("List all tabs")); + hb->addWidget(tabListTb_); + + //Stacked widget to store the actual tab widgets + stacked_ = new QStackedWidget(this); + stacked_->setObjectName("stacked"); + stacked_->setMinimumHeight(1); + stacked_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + layout->addWidget(stacked_); + + //Context menu for tha tabs + bar_->setContextMenuPolicy(Qt::CustomContextMenu); + + connect(bar_,SIGNAL(customContextMenuRequested(const QPoint &)), + this,SLOT(slotContextMenu(const QPoint &))); + + connect(bar_,SIGNAL(tabMoved(int,int)), + this,SLOT(tabMoved(int,int))); + + connect(bar_,SIGNAL(currentChanged(int)), + this,SLOT(currentTabChanged(int))); + + connect(bar_,SIGNAL(tabCloseRequested(int)), + this,SLOT(removeTab(int))); + + connect(addTb_,SIGNAL(clicked()), + this,SIGNAL(newTabRequested())); + + connect(tabListTb_,SIGNAL(clicked()), + this,SLOT(slotTabList())); +} + +void TabWidget::slotContextMenu(const QPoint& pos) { + + if (pos.isNull()) + return; + + int index = bar_->tabAt(pos); + if(index < 0 || index > bar_->count()) + return; + + QList lst; + QAction *closeAc=new QAction(QPixmap(":/viewer/close.svg"),"&Close tab",this); + lst << closeAc; + + if(QAction *ac=QMenu::exec(lst,mapToGlobal(pos),closeAc,this)) + { + if(ac == closeAc) + removeTab(index); + } + + qDeleteAll(lst); + + + /*MvQContextItemSet *cms = cmSet(); + if (!cms) + return; + + int index = bar_->tabAt(pos); + + QString selection = MvQContextMenu::instance()->exec(cms->icon(), + mapToGlobal(pos), this, + //QString::number(bar_->count())); + "path=" + folderPath(index)); + if (!selection.isEmpty()) + tabBarCommand(selection, index);*/ +} + +int TabWidget::count() const +{ + return bar_->count(); +} + +int TabWidget::currentIndex() const +{ + return bar_->currentIndex(); +} + +void TabWidget::setCurrentIndex(int index) +{ + bar_->setCurrentIndex(index); +} + +QWidget* TabWidget::widget(int index) const +{ + if (index >= 0 && index < bar_->count()) + { + return stacked_->widget(index); + } + + return 0; +} + +QWidget* TabWidget::currentWidget() const +{ + return widget(bar_->currentIndex()); +} + +int TabWidget::indexOfWidget(QWidget *w) const +{ + for (int i = 0; i < stacked_->count(); i++) + if (w == stacked_->widget(i)) + return i; + + return -1; +} + +void TabWidget::clear() +{ + beingCleared_=true; + while (bar_->count() > 0) { + removeTab(0); + } + beingCleared_=false; +} + +void TabWidget::addTab(QWidget *w, QPixmap pix, QString name) +{ + stacked_->addWidget(w); + bar_->addTab(pix, name); + bar_->setCurrentIndex(count() - 1); + checkTabStatus(); +} + +void TabWidget::removeTab(int index) +{ + if (index >= 0 && index < bar_->count()) { + QWidget *w = stacked_->widget(index); + stacked_->removeWidget(w); + bar_->removeTab(index); + w->hide(); + w->deleteLater(); + + Q_EMIT tabRemoved(); + } + + checkTabStatus(); +} + +void TabWidget::removeOtherTabs(int index) +{ + QWidget *actW = stacked_->widget(index); + + while (bar_->count() > 0) { + if (stacked_->widget(0) != actW) { + removeTab(0); + } else + break; + } + + while (bar_->count() > 1) { + if (stacked_->widget(1) != actW) { + removeTab(1); + } + } + + checkTabStatus(); +} + +void TabWidget::currentTabChanged(int index) +{ + if (stacked_->count() == bar_->count()) { + stacked_->setCurrentIndex(index); + Q_EMIT currentIndexChanged(index); + + checkTabStatus(); + } +} + +void TabWidget::tabMoved(int from, int to) +{ + QWidget *w = stacked_->widget(from); + stacked_->removeWidget(w); + stacked_->insertWidget(to, w); + + //bar_->setCurrentIndex(to); + currentTabChanged(to); +} + +void TabWidget::setTabText(int index, QString txt) +{ + if (index >= 0 && index < bar_->count()) { + bar_->setTabText(index, txt); + } +} + +void TabWidget::setTabToolTip(int index, QString txt) +{ + if (index >= 0 && index < bar_->count()) { + bar_->setTabToolTip(index, txt); + } +} + +void TabWidget::setTabWht(int index, QString txt) +{ + if (index >= 0 && index < bar_->count()) { + bar_->setTabWhatsThis(index, txt); + } +} + +void TabWidget::setTabData(int index, QPixmap pix) +{ + if (index >= 0 && index < bar_->count()) { + bar_->setTabData(index,QIcon(pix)); + } +} + +void TabWidget::setTabIcon(int index, QPixmap pix) +{ + if (index >= 0 && index < bar_->count()) + { + QLabel *lab=static_cast(bar_->tabButton(index,QTabBar::RightSide)); + if(!lab) + { + lab=new QLabel(); + lab->setAlignment(Qt::AlignCenter); + } + else + { + bar_->setTabButton(index,QTabBar::RightSide,0); + } + lab->setPixmap(pix); + lab->setFixedSize(pix.size()); + bar_->setTabButton(index,QTabBar::RightSide,lab); + +#if 0 + QSize maxSize=maxIconSize(); + + if(maxSize.width() < pix.width()) + maxSize.setWidth(pix.width()); + + if(maxSize.height() < pix.height()) + maxSize.setHeight(pix.height()); + + if(maxSize != bar_->iconSize()) + bar_->setIconSize(maxSize); + + bar_->setTabIcon(index, QIcon(pix)); +#endif + + } +} + +#if 0 +QSize TabWidget::maxIconSize() const +{ + QSize maxSize(0,0); + for(int i=0; i < bar_->count(); i++) + { + if(bar_->tabIcon(i).availableSizes().count() > 0) + { + QSize avs=bar_->tabIcon(i).availableSizes().front(); + if(maxSize.width() < avs.width()) + maxSize.setWidth(avs.width()); + + if(maxSize.height() < avs.height()) + maxSize.setHeight(avs.height()); + } + } + return maxSize; +} +#endif + +void TabWidget::checkTabStatus() +{ + if (bar_->count() > 1) + { + bar_->show(); + //bar_->setTabsClosable(true); + addTb_->show(); + tabListTb_->show(); + } + else + { + bar_->hide(); + //bar_->setTabsClosable(false); + addTb_->hide(); + tabListTb_->hide(); + } + + /* + for (int i = 0; i < bar_->count(); i++) + { + if (QWidget *w = bar_->tabButton(i, QTabBar::RightSide)) + { + if (i == bar_->currentIndex()) + w->show(); + else + w->hide(); + } + }*/ +} + +void TabWidget::slotTabList() +{ + QMenu* menu=new QMenu(tabListTb_); + + for(int i=0; i < bar_->count(); i++) + { + QAction *ac=new QAction(menu); + ac->setText(bar_->tabWhatsThis(i)); + ac->setIcon(bar_->tabData(i).value()); + ac->setData(i); + if(i==bar_->currentIndex()) + { + QFont font; + font.setBold(true); + ac->setFont(font); + } + + menu->addAction(ac); + } + + if(QAction *ac=menu->exec(QCursor::pos())) + { + int index=ac->data().toInt(); + if(index >=0 && index < count()) + { + setCurrentIndex(index); + } + } + + menu->clear(); + menu->deleteLater(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TabWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TabWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TabWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TabWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,84 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TABWIDGET_HPP_ +#define TABWIDGET_HPP_ + +#include +#include + +class QMenu; +class QStackedWidget; +class QTabBar; +class QToolButton; +class QVBoxLayout; + +class IconTabBar : public QTabBar +{ +public: + IconTabBar(QWidget* parent=0) : QTabBar(parent) {} +protected: + void paintEvent(QPaintEvent *e); +}; + + +class TabWidget : public QWidget +{ + Q_OBJECT + +public: + explicit TabWidget(QWidget *parent=0); + + int currentIndex() const; + int indexOfWidget(QWidget*) const; + QWidget *widget(int) const; + QWidget *currentWidget() const; + void checkTabStatus(); + void addTab(QWidget *,QPixmap,QString); + void setTabText(int,QString); + void setTabIcon(int,QPixmap); + void setTabToolTip(int,QString); + void setTabWht(int,QString); + void setTabData(int,QPixmap); + int count() const; + void clear(); + bool beingCleared() const {return beingCleared_;} + +public Q_SLOTS: + void removeTab(int); + void removeOtherTabs(int); + void setCurrentIndex(int); + +private Q_SLOTS: + void slotContextMenu(const QPoint&); + void currentTabChanged(int index); + void tabMoved(int from,int to); + void slotTabList(); + +Q_SIGNALS: + void currentIndexChanged(int); + void newTabRequested(); + void tabRemoved(); + +protected: + //virtual MvQContextItemSet* cmSet()=0; + virtual void tabBarCommand(QString,int)=0; + +private: +#if 0 + QSize maxIconSize() const; +#endif + QTabBar *bar_; + QStackedWidget *stacked_; + QToolButton* addTb_; + QToolButton* tabListTb_; + bool beingCleared_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextEditSearchLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextEditSearchLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextEditSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextEditSearchLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,249 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + + +#include "TextEditSearchLine.hpp" + +#include "AbstractTextEditSearchInterface.hpp" + +#include +#include +#include +#include +#include +#include + +//#include "UserMessage.hpp" + +TextEditSearchLine::TextEditSearchLine(QWidget *parent) : + AbstractSearchLine(parent), + interface_(0), + lastFindSuccessful_(false) +{ + connect(matchModeCb_,SIGNAL(currentIndexChanged(int)), + this, SLOT(matchModeChanged(int))); +} + +TextEditSearchLine::~TextEditSearchLine() +{ + +} + +void TextEditSearchLine::setSearchInterface(AbstractTextEditSearchInterface *e) +{ + interface_=e; +} + +bool TextEditSearchLine::findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, QTextCursor::MoveOperation move, int iteration) +{ + QTextDocument::FindFlags flags = findFlags() | extraFlags; + lastFindSuccessful_ = interface_->findString(str,highlightAll,flags,move,iteration,matchModeCb_->currentMatchMode()); + return lastFindSuccessful_; +} + +void TextEditSearchLine::highlightMatches(QString txt) +{ + if(interface_) + { + interface_->enableHighlights(); + if(interface_->highlightsNeedSearch() && !txt.isEmpty()) + { + findString(txt, true, 0, QTextCursor::StartOfWord, 0); // highlight all matches + } + } +} + +void TextEditSearchLine::slotHighlight() +{ + //UserMessage::message(UserMessage::DBG, false," highlight: " + searchLine_->text().toStdString()); + + highlightAllTimer_.stop(); + + if (highlightAll()) + highlightMatches(searchLine_->text()); +} + +//This slot is called as we type in the search string +void TextEditSearchLine::slotFind(QString txt) +{ + if(!interface_) + return; + + //In confirmSearch mode we do not start the search + if(confirmSearch_) + { + toDefaultState(); + return; + } + + if(txt.isEmpty()) + { + highlightAllTimer_.stop(); + toDefaultState(); + return; + } + + highlightAllTimer_.stop(); + bool found = findString(txt, false, 0, QTextCursor::StartOfWord, 0); // find the next match + lastFindSuccessful_ = found; + + if (!isEmpty()) // there is a search term supplied by the user + { + // don't highlight the matches immediately - this can be expensive for large files, + // and we don't want to highlight each time the user types a new character; wait + // a moment and then start the highlight + highlightAllTimer_.setInterval(500); + highlightAllTimer_.disconnect(); + connect(&highlightAllTimer_, SIGNAL(timeout()), this, SLOT(slotHighlight())); + highlightAllTimer_.start(); + } + else + { + clearHighlights(); + } + + updateButtons(found); +} + +void TextEditSearchLine::slotFindNext() +{ + if(!interface_) + return; + + lastFindSuccessful_ = findString(searchLine_->text(), false, 0, QTextCursor::NoMove, 0); + updateButtons(lastFindSuccessful_); +} + +void TextEditSearchLine::slotFindPrev() +{ + if(!interface_) + return; + + lastFindSuccessful_ = findString(searchLine_->text(), false, QTextDocument::FindBackward, QTextCursor::NoMove, 0); + updateButtons(lastFindSuccessful_); +} + +QTextDocument::FindFlags TextEditSearchLine::findFlags() +{ + QTextDocument::FindFlags flags; + + if(caseSensitive()) + { + flags = flags | QTextDocument::FindCaseSensitively; + } + + if(wholeWords()) + { + flags = flags | QTextDocument::FindWholeWords; + } + + return flags; +} + + +// EditorSearchLine::refreshSearch +// performed when the user changes search parameters such as case sensitivity - we want to +// re-do the search from the current point, but if the current selection still matches then +// we'd like it to be found first. + +void TextEditSearchLine::refreshSearch() +{ + if(!interface_) + return; + + // if there's something selected already then move the cursor to the start of the line and search again + interface_->refreshSearch(); + + slotFindNext(); + slotHighlight(); +} + +void TextEditSearchLine::disableHighlights() +{ + if(interface_) + interface_->disableHighlights(); +} + + +void TextEditSearchLine::clearHighlights() +{ + if(interface_) + interface_->clearHighlights(); +} + +void TextEditSearchLine::matchModeChanged(int notUsed) +{ + if(matchModeCb_->currentMatchMode() == StringMatchMode::ContainsMatch) + actionWholeWords_->setEnabled(true); + else + actionWholeWords_->setEnabled(false); + + refreshSearch(); +} + + +void TextEditSearchLine::on_actionCaseSensitive__toggled(bool b) +{ + AbstractSearchLine::on_actionCaseSensitive__toggled(b); + + refreshSearch(); +} + + +void TextEditSearchLine::on_actionWholeWords__toggled(bool b) +{ + AbstractSearchLine::on_actionWholeWords__toggled(b); + + refreshSearch(); +} + +void TextEditSearchLine::on_actionHighlightAll__toggled(bool b) +{ + AbstractSearchLine::on_actionHighlightAll__toggled(b); + + if (b) // user switched on the highlights + slotHighlight(); + else // user switched off the highlights + disableHighlights(); + + if(interface_ && interface_->highlightsNeedSearch()) + refreshSearch(); +} + +void TextEditSearchLine::slotClose() +{ + AbstractSearchLine::slotClose(); + clearHighlights(); +} + +// Called when we load a new node's information into the panel, or +// when we move to the panel from another one. +// If the search box is open, then search for the first matching item; +// if not found, go to the last line. +// If the search box is not open, search for a pre-configured list of +// keywords. If none are found, and the user has clicked on the 'reload' +// button then we just go to the last line of the output +void TextEditSearchLine::searchOnReload(bool userClickedReload) +{ + if (isVisible() && !isEmpty()) + { + slotFindNext(); + slotHighlight(); + if(!lastFindSuccessful()) + interface_->gotoLastLine(); + } + else if(interface_) + { + // search for a highlight any of the pre-defined keywords so that + // the (probably) most important piece of information is highlighted + interface_->automaticSearchForKeywords(userClickedReload); + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextEditSearchLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextEditSearchLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextEditSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextEditSearchLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,60 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ +#define VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ + +#include +#include + +#include "AbstractSearchLine.hpp" + +class AbstractTextEditSearchInterface; + +class TextEditSearchLine : public AbstractSearchLine +{ + Q_OBJECT + +public: + explicit TextEditSearchLine(QWidget *parent); + ~TextEditSearchLine(); + void setSearchInterface(AbstractTextEditSearchInterface*); + void searchOnReload(bool userClickedReload); + bool hasInterface() const {return interface_ != 0;} + +public Q_SLOTS: + void slotFind(QString); + void slotFindNext(); + void slotFindPrev(); + void slotFindNext(bool) {slotFindNext();} + void slotFindPrev(bool) {slotFindPrev();} + void matchModeChanged(int newIndex); + void on_actionCaseSensitive__toggled(bool); + void on_actionWholeWords__toggled(bool); + void on_actionHighlightAll__toggled(bool); + void slotClose(); + void slotHighlight(); + +protected: + QTextDocument::FindFlags findFlags(); + bool findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, QTextCursor::MoveOperation move, int iteration); + void refreshSearch(); + void highlightMatches(QString txt); + void clearHighlights(); + void disableHighlights(); + bool lastFindSuccessful() {return lastFindSuccessful_;} + + AbstractTextEditSearchInterface* interface_; + QTimer highlightAllTimer_; + QColor highlightColour_; + bool lastFindSuccessful_; +}; + +#endif /* VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterAddDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterAddDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterAddDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,118 @@ + + + TextFilterAddDialog + + + + 0 + 0 + 378 + 176 + + + + Dialog + + + + + + + + + + + + + + Rege&xp: + + + label + + + + + + + Match mode: + + + + + + + + + Case sensitive + + + + + + + Add to context menu + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + + buttonBox + accepted() + TextFilterAddDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TextFilterAddDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandler.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandler.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandler.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandler.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,242 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TextFilterHandler.hpp" + +#include +#include + +#include "SessionHandler.hpp" +#include "VSettings.hpp" + +TextFilterHandler* TextFilterHandler::instance_=0; + +//============================================== +// +// TextFilterItem +// +//============================================== + +void TextFilterItem::save(VSettings *vs) const +{ + vs->put("filter", filter_); + vs->putAsBool("matched", matched_); + vs->putAsBool("caseSensitive",caseSensitive_); + vs->putAsBool("contextMenu",contextMenu_); +} + +TextFilterItem TextFilterItem::make(VSettings* vs) +{ + std::string emptyDefault=""; + std::string filter = vs->get("filter", emptyDefault); + bool matched = vs->getAsBool("matched",true); + bool caseSensitive = vs->getAsBool("filter",false); + bool contextMenu = vs->getAsBool("contextMenu",false); + return TextFilterItem(filter,matched,caseSensitive,contextMenu); +} + +bool TextFilterItem::operator ==(const TextFilterItem& o) const +{ + return filter_ == o.filter_ && + matched_ == o.matched_ && caseSensitive_ == o.caseSensitive_; +} + +//============================================== +// +// TextFilterHandler +// +//============================================== + +TextFilterHandler* TextFilterHandler::Instance() +{ + if(!instance_) + instance_=new TextFilterHandler(); + + return instance_; +} + +TextFilterHandler::TextFilterHandler() : + maxLatestNum_(5) +{ + readSettings(); +} + +int TextFilterHandler::indexOf(const std::string& filter,bool matched,bool caseSensitive) const +{ + if(filter.empty()) + return -1; + + TextFilterItem item(filter,matched,caseSensitive); + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i] == item) + return i; + } + + return -1; +} + +bool TextFilterHandler::contains(const std::string& filter,bool matched,bool caseSensitive) const +{ + return indexOf(filter,matched,caseSensitive) != -1; +} + +bool TextFilterHandler::containsExceptOne(int index,const std::string& filter,bool matched,bool caseSensitive) const +{ + if(filter.empty()) + return false; + + TextFilterItem item(filter,matched,caseSensitive); + for(int i=0; i < static_cast(items_.size()); i++) + { + if(i!= index && items_[i] == item) + return true; + } + return false; +} + +bool TextFilterHandler::add(const TextFilterItem& item) +{ + if(item.filter().empty()) + return false; + + items_.push_back(item); + writeSettings(); + return true; +} + + +bool TextFilterHandler::add(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu) +{ + TextFilterItem item(filter,matched,caseSensitive,contextMenu); + return add(item); +} + +void TextFilterHandler::addLatest(const TextFilterItem& item) +{ + if(item.filter().empty()) + return; + + //Remove if exists + std::vector::iterator it=std::find(latest_.begin(),latest_.end(),item) ; + if(it != latest_.end()) + latest_.erase(it); + + //trim size + while(static_cast(latest_.size()) >= maxLatestNum_) + { + latest_.pop_back(); + } + + //add item to front + latest_.insert(latest_.begin(),item); + + writeSettings(); +} + +void TextFilterHandler::addLatest(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu) +{ + TextFilterItem item(filter,matched,caseSensitive,contextMenu); + addLatest(item); +} + +void TextFilterHandler::remove(int index) +{ + if(index < 0 || index >= static_cast(items_.size())) + return; + + items_.erase(items_.begin()+index); + writeSettings(); +} + +void TextFilterHandler::update(int index,const TextFilterItem& item) +{ + if(index < 0 || index >= static_cast(items_.size())) + return; + + items_[index]=item; + writeSettings(); +} + +void TextFilterHandler::allFilters(std::set& v) +{ + v.clear(); + for(std::size_t i = 0; i < items_.size() ; i++) + { + v.insert(items_[i].filter()); + } + for(std::size_t i = 0; i < latest_.size() ; i++) + { + v.insert(latest_[i].filter()); + } +} + +std::string TextFilterHandler::settingsFile() +{ + SessionItem* cs=SessionHandler::instance()->current(); + return cs->textFilterFile(); +} + +void TextFilterHandler::writeSettings() +{ + std::string dummyFileName="dummy"; + std::string settingsFilePath = settingsFile(); + VSettings vs(settingsFilePath); + + std::vector vsItems; + for(std::size_t i = 0; i < items_.size() ; i++) + { + VSettings vsThisItem(dummyFileName); + items_[i].save(&vsThisItem); + vsItems.push_back(vsThisItem); + } + vs.put("saved",vsItems); + + vsItems.clear(); + for(std::size_t i = 0; i < latest_.size() ; i++) + { + VSettings vsThisItem(dummyFileName); + latest_[i].save(&vsThisItem); + vsItems.push_back(vsThisItem); + } + vs.put("latest",vsItems); + + vs.write(); +} + +void TextFilterHandler::readSettings() +{ + std::string settingsFilePath = settingsFile(); + VSettings vs(settingsFilePath); + + bool ok = vs.read(false); // false means we don't abort if the file is not there + + if(ok) + { + std::vector vsItems; + vs.get("saved",vsItems); + for (std::size_t i = 0; i < vsItems.size(); i++) + { + add(TextFilterItem::make(&vsItems[i])); + } + + vsItems.clear(); + vs.get("latest",vsItems); + for (std::size_t i = 0; i < vsItems.size(); i++) + { + addLatest(TextFilterItem::make(&vsItems[i])); + } + } + //If there is no settings file at all we automatically add this filter + else if(!vs.fileExists()) + { + add(TextFilterItem("^\\+\\s",false,false)); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,305 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TextFilterHandlerDialog.hpp" + +#include "TextFilterHandler.hpp" +#include "SessionHandler.hpp" + +#include +#include +#include +#include + +//====================================== +// +// TextFilterAddDialog +// +//====================================== + +TextFilterAddDialog::TextFilterAddDialog(QWidget *parent) : + QDialog(parent) +{ + setupUi(this); + + setWindowTitle(tr("Add item")); + + //match + matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_match.svg")),tr("match"),0); + matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_no_match.svg")),tr("no match"),1); +} + +TextFilterItem TextFilterAddDialog::item() +{ + return TextFilterItem(filterLe_->text().toStdString(),(matchCb_->currentIndex() == 0), + caseCb_->isChecked(),menuCb_->isChecked()); +} + +void TextFilterAddDialog::init(const TextFilterItem& item) +{ + filterLe_->setText(QString::fromStdString(item.filter())); + matchCb_->setCurrentIndex(item.matched()?0:1); + caseCb_->setChecked(item.caseSensitive()); + menuCb_->setChecked(item.contextMenu()); +} + +void TextFilterAddDialog::accept() +{ + TextFilterItem it=item(); + if(TextFilterHandler::Instance()->contains(it.filter(),it.matched(),it.caseSensitive())) + { + QMessageBox::critical(0,tr("Save text filter"), "Cannot save text filter! A text filter with the same regexp: " + + QString::fromStdString(it.filter()) + + " and settings already exists!"); + return; + } + + TextFilterHandler::Instance()->add(it); + QDialog::accept(); +} + + +//====================================== +// +// TextFilterEditDialog +// +//====================================== + +TextFilterEditDialog::TextFilterEditDialog(QWidget *parent) : + TextFilterAddDialog(parent), itemIndex_(-1) +{ + setWindowTitle(tr("Edit item")); +} + +void TextFilterEditDialog::init(int itemIndex,const TextFilterItem& item) +{ + itemIndex_=itemIndex; + TextFilterAddDialog::init(item); +} + +void TextFilterEditDialog::accept() +{ + if(itemIndex_ >= 0) + { + TextFilterItem it=item(); + + if(TextFilterHandler::Instance()->items()[itemIndex_] == it && + TextFilterHandler::Instance()->items()[itemIndex_].contextMenu() == + it.contextMenu()) + { + QDialog::reject(); + return; + } + + if(TextFilterHandler::Instance()->containsExceptOne(itemIndex_,it.filter(),it.matched(),it.caseSensitive())) + { + QMessageBox::critical(0,tr("Save text filter"), "Cannot save text filter! A text filter with the same regexp: " + + QString::fromStdString(it.filter())+ + " and settings already exists!"); + return; + } + + TextFilterHandler::Instance()->update(itemIndex_,it); + } + + QDialog::accept(); +} + +//====================================== +// +// TextFilterHandlerDialog +// +//====================================== + +TextFilterHandlerDialog::TextFilterHandlerDialog(QWidget *parent) : QDialog(parent), applyIndex_(-1) +{ + setupUi(this); + + QAction *sep1=new QAction(this); + sep1->setSeparator(true); + + QAction *sep2=new QAction(this); + sep2->setSeparator(true); + + QAction *sep3=new QAction(this); + sep3->setSeparator(true); + + table_->addAction(actionAdd_); + table_->addAction(sep1); + table_->addAction(actionDuplicate_); + table_->addAction(actionEdit_); + table_->addAction(sep2); + table_->addAction(actionApply_); + table_->addAction(sep3); + table_->addAction(actionRemove_); + + //Add actions for the toolbuttons + addTb_->setDefaultAction(actionAdd_); + editTb_->setDefaultAction(actionEdit_); + removeTb_->setDefaultAction(actionRemove_); + duplicateTb_->setDefaultAction(actionDuplicate_); + applyTb_->setDefaultAction(actionApply_); + + //Init the table + reloadTable(); + + //init + readSettings(); +} + +TextFilterHandlerDialog::~TextFilterHandlerDialog() +{ + writeSettings(); +} + +void TextFilterHandlerDialog::reloadTable() +{ + //The itemlist is fairly small. For simplicity we do not use a model/view solution here just + //a tablewidget. We reload the whole table widget whenerver there is a change in the list. + + table_->clear(); //it removes the headers as well + + QStringList headers; + headers << "Match mode" << "Case sensitive" << "Context menu" << "Regexp (grep)"; + table_->setHorizontalHeaderLabels(headers); + + const std::vector& items=TextFilterHandler::Instance()->items(); + table_->setRowCount(items.size()); + for(size_t i=0; i < items.size(); i++) + { + QString filterTxt=QString::fromStdString(items[i].filter()); + //Replace whitespace with Open Box U+2423 just for better interpretation + filterTxt.replace(QChar(' '),QChar(9251)); + + QTableWidgetItem *filterItem = new QTableWidgetItem(filterTxt); + QTableWidgetItem *matchedItem = new QTableWidgetItem((items[i].matched())?"match":"no match"); + QTableWidgetItem *caseItem = new QTableWidgetItem((items[i].caseSensitive())?"yes":"no"); + QTableWidgetItem *contextItem = new QTableWidgetItem((items[i].contextMenu())?"yes":"no"); + + table_->setItem(i, 0, matchedItem); + table_->setItem(i, 1, caseItem); + table_->setItem(i, 2, contextItem); + table_->setItem(i, 3, filterItem); + } + + if(table_->rowCount() > 0) + table_->setCurrentCell(0,0); + + updateStatus(); +} + +void TextFilterHandlerDialog::editItem() +{ + int r=table_->currentRow(); + if(r >=0 ) + { + TextFilterEditDialog diag(this); + diag.init(r,TextFilterHandler::Instance()->items()[r]); + if(diag.exec() == QDialog::Accepted) + reloadTable(); + } +} + + +void TextFilterHandlerDialog::on_actionEdit__triggered() +{ + editItem(); +} + +void TextFilterHandlerDialog::on_actionDuplicate__triggered() +{ + int r=table_->currentRow(); + if(r >=0 ) + { + TextFilterAddDialog diag(this); + diag.init(TextFilterHandler::Instance()->items()[r]); + if(diag.exec() == QDialog::Accepted) + reloadTable(); + } +} + +void TextFilterHandlerDialog::on_actionRemove__triggered() +{ + int r=table_->currentRow(); + if(r >= 0) + { + TextFilterHandler::Instance()->remove(r); + reloadTable(); + } +} + +void TextFilterHandlerDialog::on_actionAdd__triggered() +{ + TextFilterAddDialog diag(this); + if(diag.exec() == QDialog::Accepted) + reloadTable(); +} + +void TextFilterHandlerDialog::on_table__doubleClicked(const QModelIndex& index) +{ + editItem(); +} + +void TextFilterHandlerDialog::on_actionApply__triggered() +{ + int r=table_->currentRow(); + { + applyIndex_=table_->currentRow(); + close(); + } +} + +void TextFilterHandlerDialog::updateStatus() +{ + bool hasSelected=table_->currentRow() >=0; + + actionEdit_->setEnabled(hasSelected); + actionDuplicate_->setEnabled(hasSelected); + actionRemove_->setEnabled(hasSelected); + actionApply_->setEnabled(hasSelected); +} + +QString TextFilterHandlerDialog::settingsFile() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + return QString::fromStdString(cs->qtSettingsFile("TextFilterHandlerDialog")); +} + +void TextFilterHandlerDialog::writeSettings() +{ + Q_ASSERT(settingsFile().isEmpty() == false); + QSettings settings(settingsFile(),QSettings::NativeFormat); + + //We have to clear it not to remember all the previous windows + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void TextFilterHandlerDialog::readSettings() +{ + Q_ASSERT(settingsFile().isEmpty() == false); + QSettings settings(settingsFile(),QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(520,330)); + } + + settings.endGroup(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,82 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TEXTFILTERHANDLERDIALOG_HPP +#define TEXTFILTERHANDLERDIALOG_HPP + +#include + +#include "ui_TextFilterAddDialog.h" +#include "ui_TextFilterHandlerDialog.h" + +class TextFilterItem; + +class TextFilterAddDialog : public QDialog, private Ui::TextFilterAddDialog +{ +Q_OBJECT + +public: + explicit TextFilterAddDialog(QWidget* parent=0); + void init(const TextFilterItem& item); + +public Q_SLOTS: + void accept(); + +protected: + TextFilterItem item(); +}; + +class TextFilterEditDialog : public TextFilterAddDialog +{ +Q_OBJECT + +public: + explicit TextFilterEditDialog(QWidget* parent=0); + void init(int itemIndex,const TextFilterItem& item); + +public Q_SLOTS: + void accept(); + +protected: + int itemIndex_; +}; + +class TextFilterHandlerDialog : public QDialog, private Ui::TextFilterHandlerDialog +{ + Q_OBJECT + +public: + explicit TextFilterHandlerDialog(QWidget *parent = 0); + ~TextFilterHandlerDialog(); + + void setItemToSaveAs(QString name,QString filter,bool matched,bool caseSensitive); + int applyIndex() const {return applyIndex_;} + +protected Q_SLOTS: + void on_actionAdd__triggered(); + void on_actionEdit__triggered(); + void on_actionDuplicate__triggered(); + void on_actionRemove__triggered(); + void on_actionApply__triggered(); + void on_table__doubleClicked(const QModelIndex& index); + +private: + void reloadTable(); + bool addItem(); + void editItem(); + void updateStatus(); + QString settingsFile(); + void writeSettings(); + void readSettings(); + + int applyIndex_; +}; + +#endif // TEXTFILTERHANDLERDIALOG_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandlerDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandlerDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,287 @@ + + + TextFilterHandlerDialog + + + + 0 + 0 + 697 + 525 + + + + Text Filter Manager + + + + + + + + + + Qt::ActionsContextMenu + + + QAbstractItemView::NoEditTriggers + + + true + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 4 + + + true + + + false + + + false + + + + Match mode + + + + + Case sensitive + + + + + Context menu + + + + + Regexp + + + + + + + + + + + + + + + + 0 + 0 + + + + Add + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + &Duplicate + + + + + + + + 0 + 0 + + + + &Edit + + + Qt::ToolButtonTextBesideIcon + + + + + + + + 0 + 0 + + + + Apply + + + + + + + Qt::Horizontal + + + + + + + + 0 + 0 + + + + &Remove + + + Qt::ToolButtonTextBesideIcon + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + &Duplicate + + + Duplicate item + + + Ctrl+D + + + + + + :/viewer/configure.svg:/viewer/configure.svg + + + &Edit + + + Edit item + + + Ctrl+E + + + + + + :/viewer/close.svg:/viewer/close.svg + + + &Remove + + + Remove item + + + Del + + + + + + :/viewer/images/add.svg:/viewer/images/add.svg + + + &Add + + + Add item + + + Ctrl+N + + + + + A&pply + + + + + + + + + buttonBox + accepted() + TextFilterHandlerDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + TextFilterHandlerDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandler.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandler.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterHandler.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterHandler.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,76 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TEXTFILTERHANDLER_HPP +#define TEXTFILTERHANDLER_HPP + +#include +#include +#include + +class VSettings; + +class TextFilterItem +{ +public: + TextFilterItem(const std::string& filter,bool matched=true,bool caseSensitive=false,bool contextMenu=true) : + filter_(filter), matched_(matched), caseSensitive_(caseSensitive), contextMenu_(contextMenu) {} + + const std::string& filter() const {return filter_;} + bool caseSensitive() const {return caseSensitive_;} + bool matched() const {return matched_;} + bool contextMenu() const {return contextMenu_;} + void setContextMenu(bool cm) {contextMenu_=cm;} + void save(VSettings *vs) const; + + + static TextFilterItem make(VSettings* vs); + bool operator ==(const TextFilterItem& o) const; + +public: + std::string filter_; + bool matched_; + bool caseSensitive_; + bool contextMenu_; +}; + +class TextFilterHandler +{ +public: + static TextFilterHandler* Instance(); + + bool contains(const std::string& filter,bool matched,bool caseSensitive) const; + bool containsExceptOne(int index,const std::string& filter,bool matched,bool caseSensitive) const; + bool add(const TextFilterItem&); + bool add(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu); + void addLatest(const TextFilterItem&); + void addLatest(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu); + const std::vector& items() const {return items_;} + const std::vector& latestItems() const {return latest_;} + void update(int,const TextFilterItem&); + void remove(int); + void allFilters(std::set&); + int indexOf(const std::string& filter,bool matched,bool caseSensitive) const; + +protected: + TextFilterHandler(); + + std::string settingsFile(); + void readSettings() ; + void writeSettings(); + + static TextFilterHandler* instance_; + const int maxLatestNum_; + std::vector items_; + std::vector latest_; +}; + + +#endif // TEXTFILTERHANDLER_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,488 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TextFilterWidget.hpp" +#include "TextFilterHandlerDialog.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TextFormat.hpp" +#include "ViewerUtil.hpp" + +TextFilterWidget::TextFilterWidget(QWidget *parent) : + QWidget(parent), + status_(EditStatus), + statusTb_(0), + optionTb_(0) +{ + setupUi(this); + + setProperty("textFilter","1"); + + //match + matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_match.svg")),tr("match"),0); + matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_no_match.svg")),tr("no match"),1); + matchCb_->setItemData(0,tr("Show only the lines matching the filter experssion"),Qt::ToolTipRole); + matchCb_->setItemData(1,tr("Show only the lines not matching the filter experssion"),Qt::ToolTipRole); + + //Editor +#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) + le_->setPlaceholderText(tr(" Regexp (grep)")); +#endif + +#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) + le_->setClearButtonEnabled(true); +#endif + + oriBrush_=QBrush(le_->palette().brush(QPalette::Base)); + redBrush_=ViewerUtil::lineEditRedBg(); + greenBrush_=ViewerUtil::lineEditGreenBg(); + + completer_=new QCompleter(this); + le_->setCompleter(completer_); + + QIcon icon; + icon.addPixmap(QPixmap(":/viewer/close_grey.svg"),QIcon::Normal); + icon.addPixmap(QPixmap(":/viewer/close_red.svg"),QIcon::Active); + closeTb_->setIcon(icon); + + refreshCompleter(); + + //Initially it is hidden + hide(); +} + +bool TextFilterWidget::isActive() const +{ + return !le_->text().isEmpty(); +} + +bool TextFilterWidget::isCaseSensitive() const +{ + return caseCb_->isChecked(); +} + +bool TextFilterWidget::isMatched() const +{ + return matchCb_->currentIndex() == 0; +} + +QString TextFilterWidget::filterText() const +{ + return le_->text(); +} + +void TextFilterWidget::init(const TextFilterItem& item) +{ + matchCb_->setCurrentIndex(item.matched()?0:1); + caseCb_->setChecked(item.caseSensitive()); + le_->setText(QString::fromStdString(item.filter())); +} + +void TextFilterWidget::setExternalButtons(QToolButton* statusTb,QToolButton* optionTb) +{ + Q_ASSERT(statusTb); + Q_ASSERT(optionTb); + + statusTb_=statusTb; + optionTb_=optionTb; + + statusTb_->setChecked(false); + + connect(statusTb_,SIGNAL(toggled(bool)), + this,SLOT(slotStatusTb(bool))); + + connect(optionTb_,SIGNAL(clicked()), + this,SLOT(slotOptionTb())); +} + +void TextFilterWidget::setEnabledExternalButtons(bool b) +{ + if(statusTb_) statusTb_->setEnabled(b); + if(optionTb_) optionTb_->setEnabled(b); +} + +void TextFilterWidget::refreshCompleter() +{ + QStringList lst; + std::set vals; + TextFilterHandler::Instance()->allFilters(vals); + + for(std::set::const_iterator it=vals.begin(); it != vals.end(); ++it) + { + lst << QString::fromStdString(*it); + } + le_->setCompleter(new QCompleter(lst,le_)); +} + +void TextFilterWidget::slotFilterEditor() +{ + +} + +void TextFilterWidget::buildMenu(QToolButton *tb) +{ + QMenu* menu=new QMenu(tb); + + QAction *manageAc=new QAction(menu); + manageAc->setText(tr("Manage filters ...")); + manageAc->setIcon(QPixmap(":/viewer/configure.svg")); + menu->addAction(manageAc); + + QAction *saveAc=0; + if(isVisible()) + { + saveAc=new QAction(menu); + saveAc->setText(tr("Save filter")); + saveAc->setIcon(QPixmap(":/viewer/filesaveas.svg")); + if(!isActive() || isCurrentSaved()) saveAc->setEnabled(false); + menu->addAction(saveAc); + } + + QAction *sep=new QAction(menu); + sep->setSeparator(true); + menu->addAction(sep); + + QAction *clearAc=new QAction(menu); + clearAc->setText(tr("Clear filter")); + if(!isActive()) clearAc->setEnabled(false); + menu->addAction(clearAc); + + QAction *sep1=new QAction(menu); + sep1->setSeparator(true); + menu->addAction(sep1); + + addMenuSection(menu,TextFilterHandler::Instance()->items(),tr("Saved"),"s"); + addMenuSection(menu,TextFilterHandler::Instance()->latestItems(),tr("Recent"),"r"); + + if(QAction *ac=menu->exec(QCursor::pos())) + { + //Start manage filters dialogue + if(ac == manageAc) + { + TextFilterHandlerDialog diag; + diag.exec(); + int pos=diag.applyIndex(); + if(pos >=0 && TextFilterHandler::Instance()->latestItems().size()) + { + TextFilterItem item=TextFilterHandler::Instance()->items()[pos]; + if(!item.filter().empty()) + { + init(item); + Q_EMIT runRequested(QString::fromStdString(item.filter()),item.matched(),item.caseSensitive()); + } + } + else + { + refreshCompleter(); + } + } + //Save current filter + else if(ac == saveAc) + { + std::string filter=filterText().toStdString(); + bool matchMode=isMatched(); + bool caseSensitive=isCaseSensitive(); + + int pos=TextFilterHandler::Instance()->indexOf(filter,matchMode,caseSensitive); + if(pos != -1) + { + TextFilterItem it=TextFilterHandler::Instance()->items()[pos]; + + //Enable context menu for already saved items + if(!it.contextMenu()) + { + it.setContextMenu(true); + TextFilterHandler::Instance()->update(pos,it); + } + return; + } + + TextFilterHandler::Instance()->add(filter,matchMode,caseSensitive,true); + refreshCompleter(); + } + //Clear current filter + else if(ac == clearAc) + { + le_->clear(); + setStatus(EditStatus); + refreshCompleter(); + } + //Load a filter + else + { + QStringList id=ac->data().toString().split("_"); + if(id.count() == 2) + { + std::size_t pos=id[1].toInt(); + TextFilterItem item("",""); + if(id[0] == "s") + { + if(pos >=0 && TextFilterHandler::Instance()->items().size()) + { + item=TextFilterHandler::Instance()->items()[pos]; + } + } + + else if(id[0] == "r") + { + if(pos >=0 && TextFilterHandler::Instance()->latestItems().size()) + { + item=TextFilterHandler::Instance()->latestItems()[pos]; + } + } + + if(!item.filter().empty()) + { + init(item); + Q_EMIT runRequested(QString::fromStdString(item.filter()),item.matched(),item.caseSensitive()); + } + } + } + + } + + menu->clear(); + menu->deleteLater(); +} + +void TextFilterWidget::addMenuSection(QMenu* menu,const std::vector& items,QString title,QString data) +{ + if(items.empty()) + return; + + QAction *sep1=new QAction(menu); + sep1->setSeparator(true); + menu->addAction(sep1); + + if(!title.isEmpty()) + { + QAction* acTitle = new QAction(menu); + acTitle->setText(title); + QFont f=acTitle->font(); + f.setBold(true); + acTitle->setFont(f); + menu->addAction(acTitle); + } + + for(std::size_t i=0 ; i < items.size(); i++) + { + if(data != "s" || items[i].contextMenu()) + { + QAction* ac=new QAction(this); + + QString txt=QString::fromStdString(items[i].filter()); + //Replace whitespace with Open Box U+2423 just for better interpretation + txt.replace(QChar(' '),QChar(9251)); + txt+=" (" + QString(items[i].caseSensitive()?"cs":"ci") + ")"; + + ac->setText(txt); + ac->setData(data + "_" + QString::number(i)); //set an id for the action + + if(items[i].matched()) + ac->setIcon(QPixmap(":/viewer/filter_match.svg")); + else + ac->setIcon(QPixmap(":/viewer/filter_no_match.svg")); + + menu->addAction(ac); + } + } +} + +void TextFilterWidget::runIt() +{ + QString t=le_->text(); + if(!t.isEmpty()) + Q_EMIT runRequested(t,isMatched(),isCaseSensitive()); +} + +void TextFilterWidget::slotStatusTb(bool b) +{ + if(b) + { + show(); + setEditFocus(); + } + else + { + hide(); + adjustToolTip(); + } +} + +void TextFilterWidget::closeIt() +{ + //clear + le_->clear(); + setStatus(EditStatus); + refreshCompleter(); + + //hide + statusTb_->setChecked(false); + Q_EMIT closeRequested(); //this will clear the filter +} + + +void TextFilterWidget::on_closeTb__clicked() +{ + closeIt(); +} + +void TextFilterWidget::on_le__returnPressed() +{ + runIt(); +} + +void TextFilterWidget::on_le__textChanged() +{ + if(status_ != EditStatus) + setStatus(EditStatus); + + if(!isActive()) + Q_EMIT clearRequested(); +} + +void TextFilterWidget::on_matchCb__currentIndexChanged(int) +{ + runIt(); +} + +void TextFilterWidget::on_caseCb__stateChanged(int) +{ + runIt(); +} + +void TextFilterWidget::slotOptionTb() +{ + Q_ASSERT(optionTb_); + buildMenu(optionTb_); +} + +void TextFilterWidget::setEditFocus() +{ + le_->setFocus(); +} + +void TextFilterWidget::adjustToolTip() +{ + if(!statusTb_) + return; + + QString filterDesc; + if(status_ == FoundStatus || status_ == NotFoundStatus) + { + if(!isVisible()) + filterDesc+=tr("Toggle to show text filter bar
    ----------------------------------------
    "); + else + filterDesc+=tr("Toggle to hide text filter bar. The filter remains active (if defined).
    ----------------------------------------
    "); + + filterDesc+=tr("Current filter:") + + "
     regexp: " + filterText() + + "
     mode: " + (isMatched()?"match":"no match") + ", " + + + (isCaseSensitive()?"case sensitive":"case insensitive") + + "

    "; + } + + switch(status_) + { + case EditStatus: + statusTb_->setToolTip(tr(isVisible()?"Toggle to hide text filter bar. The filter remains active (if defined).":"Toggle to show text filter bar")); + break; + case FoundStatus: + statusTb_->setToolTip(filterDesc + tr("There ") + + Viewer::formatText("are lines",QColor(100,220,120)) + + tr(" matching the filter in the output file")); + break; + case NotFoundStatus: + statusTb_->setToolTip(filterDesc + tr("There ") + + Viewer::formatText("are no lines",QColor(255,95,95)) + + tr(" matching the filter in the output file")); + break; + default: + break; + } +} + +void TextFilterWidget::setStatus(FilterStatus status,bool force) +{ + if(force || status_ != status) + { + status_=status; + + QBrush br=oriBrush_; + QPalette p=le_->palette(); + switch(status_) + { + case EditStatus: + br=oriBrush_; + if(statusTb_) + { + statusTb_->setIcon(QPixmap(":/viewer/filter_decor.svg")); + } + break; + case FoundStatus: + br=greenBrush_; + if(statusTb_) + { + statusTb_->setIcon(QPixmap(":/viewer/filter_decor_green.svg")); + } + addCurrentToLatest(); + break; + case NotFoundStatus: + br=redBrush_; + if(statusTb_) + { + statusTb_->setIcon(QPixmap(":/viewer/filter_decor_red.svg")); + } + break; + default: + break; + } + + p.setBrush(QPalette::Base,br); + le_->setPalette(p); + + adjustToolTip(); + } +} + +void TextFilterWidget::addCurrentToLatest() +{ + TextFilterHandler::Instance()->addLatest(filterText().toStdString(), + isMatched(),isCaseSensitive(),true); +} + +bool TextFilterWidget::isCurrentSaved() const +{ + return TextFilterHandler::Instance()->contains(filterText().toStdString(), + isMatched(),isCaseSensitive()); +} + +void TextFilterWidget::paintEvent(QPaintEvent *) +{ + QStyleOption opt; + opt.init(this); + QPainter p(this); + style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); +} + +void TextFilterWidget::showEvent(QShowEvent *) +{ + adjustToolTip(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,85 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TEXTFILTERWIDGET_HPP +#define TEXTFILTERWIDGET_HPP + +#include "ui_TextFilterWidget.h" + +#include +#include +#include + +#include "TextFilterHandler.hpp" + +class TextFilterCompleterModel; + +class TextFilterWidget : public QWidget, private Ui::TextFilterWidget +{ +Q_OBJECT + +public: + explicit TextFilterWidget(QWidget *parent=0); + ~TextFilterWidget() {} + + enum FilterStatus {EditStatus,FoundStatus,NotFoundStatus}; + void setStatus(FilterStatus,bool force=false); + + void setEditFocus(); + void buildMenu(QToolButton *tb); + void setExternalButtons(QToolButton* statusTb,QToolButton* optionTb); + void setEnabledExternalButtons(bool); + QString filterText() const; + bool isActive() const; + bool isCaseSensitive() const; + bool isMatched() const; + void closeIt(); + +public Q_SLOTS: + void slotFilterEditor(); + void on_le__textChanged(); + void on_le__returnPressed(); + void on_closeTb__clicked(); + void on_matchCb__currentIndexChanged(int); + void on_caseCb__stateChanged(int); + void slotOptionTb(); + void slotStatusTb(bool); + +Q_SIGNALS: + void runRequested(QString,bool,bool); + void clearRequested(); + void closeRequested(); + void hideRequested(); + void statusChanged(FilterStatus); + +protected: + void paintEvent(QPaintEvent *); + void showEvent(QShowEvent *); + +private: + void init(const TextFilterItem& item); + void runIt(); + void refreshCompleter(); + void addCurrentToLatest(); + bool isCurrentSaved() const; + void adjustToolTip(); + void addMenuSection(QMenu* menu,const std::vector& items, + QString title,QString data); + + FilterStatus status_; + QBrush oriBrush_; + QBrush redBrush_; + QBrush greenBrush_; + QCompleter* completer_; + TextFilterCompleterModel* completerModel_; + QToolButton* statusTb_; + QToolButton* optionTb_; +}; + +#endif // TEXTFILTERWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TextFilterWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextFilterWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,95 @@ + + + TextFilterWidget + + + + 0 + 0 + 400 + 300 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + Filter: + + + + + + + + + + + + + Qt::Horizontal + + + + 6 + 20 + + + + + + + + Case sensitive + + + + + + + Close text filter bar and <b>clear</b> filter + + + ... + + + true + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,133 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "syntaxhighlighter.hpp" + +#include "TextPagerEdit.hpp" +#include "TextPagerDocument.hpp" +#include "TextPagerLayout_p.hpp" +#include "TextPagerDocument_p.hpp" + +SyntaxHighlighter::SyntaxHighlighter(QObject *parent) + : QObject(parent), d(new Private) +{ +} + +SyntaxHighlighter::SyntaxHighlighter(TextPagerEdit *parent) + : QObject(parent), d(new Private) +{ + if (parent) { + parent->addSyntaxHighlighter(this); + } +} + +SyntaxHighlighter::~SyntaxHighlighter() +{ + delete d; +} + +void SyntaxHighlighter::setTextEdit(TextPagerEdit *doc) +{ + Q_ASSERT(doc); + doc->addSyntaxHighlighter(this); +} +TextPagerEdit *SyntaxHighlighter::textEdit() const +{ + return d->textEdit; +} + + +TextPagerDocument * SyntaxHighlighter::document() const +{ + return d->textEdit ? d->textEdit->document() : 0; +} + +void SyntaxHighlighter::rehighlight() +{ + if (d->textEdit) { + Q_ASSERT(d->textLayout); + d->textLayout->layoutDirty = true; + d->textEdit->viewport()->update(); + } +} + +void SyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) +{ + ASSUME(d->textEdit); + Q_ASSERT(start >= 0); + Q_ASSERT(start + count <= d->currentBlock.size()); + d->formatRanges.append(QTextLayout::FormatRange()); + QTextLayout::FormatRange &range = d->formatRanges.last(); + range.start = start; + range.length = count; + range.format = format; +} + +void SyntaxHighlighter::setFormat(int start, int count, const QColor &color) +{ + QTextCharFormat format; + format.setForeground(color); + setFormat(start, count, format); +} + +void SyntaxHighlighter::setFormat(int start, int count, const QFont &font) +{ + QTextCharFormat format; + format.setFont(font); + setFormat(start, count, format); +} + +QTextCharFormat SyntaxHighlighter::format(int pos) const +{ + QTextCharFormat ret; + Q_FOREACH(const QTextLayout::FormatRange &range, d->formatRanges) { + if (range.start <= pos && range.start + range.length > pos) { + ret.merge(range.format); + } else if (range.start > pos) { + break; + } + } + return ret; +} + + +int SyntaxHighlighter::previousBlockState() const +{ + return d->previousBlockState; +} + +int SyntaxHighlighter::currentBlockState() const +{ + return d->currentBlockState; +} + +void SyntaxHighlighter::setCurrentBlockState(int s) +{ + d->previousBlockState = d->currentBlockState; + d->currentBlockState = s; // ### These don't entirely follow QSyntaxHighlighter's behavior +} + +int SyntaxHighlighter::currentBlockPosition() const +{ + return d->currentBlockPosition; +} + +QTextBlockFormat SyntaxHighlighter::blockFormat() const +{ + return d->blockFormat; +} + +void SyntaxHighlighter::setBlockFormat(const QTextBlockFormat &format) +{ + d->blockFormat = format; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/syntaxhighlighter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 + +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef SYNTAXHIGHLIGHTER_HPP__ +#define SYNTAXHIGHLIGHTER_HPP__ + +#include +#include +#include +#include +#include +#include +#include + +class TextPagerEdit; +class TextPagerLayout; +class TextPagerDocument; +class SyntaxHighlighter : public QObject +{ + Q_OBJECT +public: + SyntaxHighlighter(QObject *parent = 0); + SyntaxHighlighter(TextPagerEdit *parent); + ~SyntaxHighlighter(); + void setTextEdit(TextPagerEdit *doc); + TextPagerEdit *textEdit() const; + TextPagerDocument *document() const; + virtual void highlightBlock(const QString &text) = 0; + QString currentBlock() const { return d->currentBlock; } + void setFormat(int start, int count, const QTextCharFormat &format); + void setFormat(int start, int count, const QColor &color); + inline void setColor(int start, int count, const QColor &color) + { setFormat(start, count, color); } + inline void setBackground(int start, int count, const QBrush &brush) + { QTextCharFormat format; format.setBackground(brush); setFormat(start, count, format); } + inline void setBackgroundColor(int start, int count, const QColor &color) + { setBackground(start, count, color); } + void setFormat(int start, int count, const QFont &font); + inline void setFont(int start, int count, const QFont &font) + { setFormat(start, count, font); } + QTextBlockFormat blockFormat() const; + void setBlockFormat(const QTextBlockFormat &format); + QTextCharFormat format(int pos) const; + int previousBlockState() const; + int currentBlockState() const; + void setCurrentBlockState(int s); + int currentBlockPosition() const; +public Q_SLOTS: + void rehighlight(); +private: + struct Private { + Private() : textEdit(0), textLayout(0), previousBlockState(0), currentBlockState(0), + currentBlockPosition(-1) {} + TextPagerEdit *textEdit; + TextPagerLayout *textLayout; + int previousBlockState, currentBlockState, currentBlockPosition; + QList formatRanges; + QTextBlockFormat blockFormat; + QString currentBlock; + } *d; + + friend class TextPagerEdit; + friend class TextPagerLayout; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,781 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "TextPagerCursor.hpp" +#include "TextPagerCursor_p.hpp" +#include "TextPagerDocument.hpp" +#include "TextPagerDocument_p.hpp" +#include "TextPagerEdit_p.hpp" +#include "TextPagerLayout_p.hpp" +#include "TextPagerEdit.hpp" + +class SelectionChangedEmitter +{ +public: + SelectionChangedEmitter(TextPagerEdit *t) + : selectionStart(-1), selectionEnd(-1), textEdit(t) + { + if (textEdit) { + selectionStart = textEdit->textCursor().selectionStart(); + selectionEnd = textEdit->textCursor().selectionEnd(); + } + } + + ~SelectionChangedEmitter() + { + if (textEdit) { + const TextPagerCursor &cursor = textEdit->textCursor(); + if (((selectionStart != selectionEnd) != cursor.hasSelection()) + || (selectionStart != selectionEnd + && (selectionStart != cursor.selectionStart() + || selectionEnd != cursor.selectionEnd()))) { + QMetaObject::invokeMethod(textEdit, "selectionChanged"); + } + } + } +private: + int selectionStart, selectionEnd; + TextPagerEdit *textEdit; +}; + +TextPagerCursor::TextPagerCursor() + : d(0), textEdit(0) +{ +} + +TextPagerCursor::TextPagerCursor(const TextPagerDocument *document, int pos, int anc) + : d(0), textEdit(0) +{ + if (document) { + const int documentSize = document->d->documentSize; + if (pos < 0 || pos > documentSize || anc < -1 || anc > documentSize) { +#ifndef LAZYTEXTEDIT_AUTOTEST + qWarning("Invalid cursor data %d %d - %d\n", + pos, anc, documentSize); + Q_ASSERT(0); +#endif + return; + } + d = new TextCursorSharedPrivate; + d->document = const_cast(document); + d->position = pos; + d->anchor = anc == -1 ? pos : anc; + d->document->d->textCursors.insert(d); + } +} + +TextPagerCursor::TextPagerCursor(const TextPagerEdit *edit, int pos, int anc) + : d(0), textEdit(0) +{ + if (edit) { + TextPagerDocument *document = edit->document(); + const int documentSize = document->d->documentSize; + if (pos < 0 || pos > documentSize || anc < -1 || anc > documentSize) { +#ifndef LAZYTEXTEDIT_AUTOTEST + qWarning("Invalid cursor data %d %d - %d\n", + pos, anc, documentSize); + Q_ASSERT(0); +#endif + return; + } + d = new TextCursorSharedPrivate; + d->document = const_cast(document); + d->position = pos; + d->anchor = anc == -1 ? pos : anc; + d->document->d->textCursors.insert(d); + } +} + +TextPagerCursor::TextPagerCursor(const TextPagerCursor &cursor) + : d(cursor.d), textEdit(0) +{ + ref(); +} + +TextPagerCursor &TextPagerCursor::operator=(const TextPagerCursor &other) +{ + deref(); + d = other.d; + textEdit = 0; + ref(); + return *this; +} + +TextPagerCursor::~TextPagerCursor() +{ + deref(); +} + +bool TextPagerCursor::isNull() const +{ + return !d || !d->document; +} + +TextPagerDocument *TextPagerCursor::document() const +{ + return d ? d->document : 0; +} + +void TextPagerCursor::setSelection(int pos, int length) // can be negative +{ + setPosition(pos + length); + if (length != 0) + setPosition(pos, KeepAnchor); +} + +void TextPagerCursor::setPosition(int pos, MoveMode mode) +{ + Q_ASSERT(!isNull()); + d->overrideColumn = -1; + if (pos < 0 || pos > d->document->documentSize()) { + clearSelection(); + return; + } else if (pos == d->position && (mode == KeepAnchor || d->anchor == d->position)) { + return; + } + + SelectionChangedEmitter emitter(textEdit); + detach(); + cursorChanged(false); + +#if 0 + Link *link = d->document->links(pos, 1).value(0, 0); + if (link && link->position < pos && link->position + link->size > pos) { // inside a link + pos = (pos > d->position ? link->position + link->size : link->position); + } +#endif + + if (mode ==TextPagerCursor::MoveAnchor) { + clearSelection(); + } + + d->position = pos; + if (mode == MoveAnchor) { + d->anchor = pos; + } + cursorChanged(true); +} + +int TextPagerCursor::position() const +{ + return isNull() ? -1 : d->position; +} + +int TextPagerCursor::anchor() const +{ + return isNull() ? -1 : d->anchor; +} + +bool TextPagerCursor::movePosition(TextPagerCursor::MoveOperation op,TextPagerCursor::MoveMode mode, int n) +{ + if (!d || !d->document || n <= 0) + return false; + + switch (op) { + case Start: + case StartOfLine: + case End: + case EndOfLine: + n = 1; + break; + default: + break; + } + + if (n > 1) { + while (n > 0 && movePosition(op, mode, 1)) + --n; + return n == 0; + } + detach(); + + switch (op) { + case NoMove: + return true; + + case PreviousCharacter: + case NextCharacter: + return movePosition(op ==TextPagerCursor::PreviousCharacter + ?TextPagerCursor::Left :TextPagerCursor::Right, mode); + + case End: + case Start: + setPosition(op ==TextPagerCursor::Start ? 0 : d->document->documentSize(), mode); + break; + + case Up: + case Down: { + TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 1); // should I use 1? + Q_ASSERT(textLayout); + int index; + bool currentIsLast; + const QTextLine currentLine = textLayout->lineForPosition(d->position, 0, + &index, + ¤tIsLast); + Q_ASSERT(textLayout->lines.size() <= 1 || (index != -1 && currentLine.isValid())); + if (!currentLine.isValid()) + return false; + const int col = columnNumber(); + int targetLinePos; + if (op == Up) { + targetLinePos = d->position - col - 1; +// qDebug() << "I was at column" << col << "and position" +// << d->position << "so naturally I can find the previous line around" +// << (d->position - col - 1); + } else { + targetLinePos = d->position + currentLine.textLength() - col + + (currentIsLast ? 1 : 0); +// qDebug() << "currentLine.textLength" << currentLine.textLength() << "col" << col +// << "currentIsLast" << currentIsLast; + // ### probably need to add only if last line in layout + } + if (targetLinePos < 0) { + if (d->position == 0) { + return false; + } else { + setPosition(0, mode); + return true; + } + } else if (targetLinePos >= d->document->documentSize()) { + if (d->position == d->document->documentSize()) { + return false; + } else { + setPosition(d->document->documentSize(), mode); + return true; + } + } + int offsetInLine; + + const QTextLine targetLine = textLayout->lineForPosition(targetLinePos, &offsetInLine); + if (!targetLine.isValid()) + return false; + targetLinePos -= offsetInLine; // targetLinePos should now be at col 0 + +// qDebug() << "finding targetLine at" << targetLinePos +// << d->document->read(targetLinePos, 7) +// << "d->position" << d->position +// << "col" << col +// << "offsetInLine" << offsetInLine; + + int gotoCol = qMax(d->overrideColumn, col); + if (gotoCol > targetLine.textLength()) { + d->overrideColumn = qMax(d->overrideColumn, gotoCol); + gotoCol = targetLine.textLength(); + } + const int overrideColumn = d->overrideColumn; + setPosition(targetLinePos + gotoCol, mode); + d->overrideColumn = overrideColumn; + break; } + + case EndOfLine: + case StartOfLine: { + int offset; + bool lastLine; + TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 1); + QTextLine line = textLayout->lineForPosition(position(), &offset, + 0, &lastLine); + if (!line.isValid()) + return false; + if (op ==TextPagerCursor::StartOfLine) { + setPosition(position() - offset, mode); + } else { + int pos = position() - offset + line.textLength(); + if (!lastLine) + --pos; + setPosition(pos, mode); + } + break; } + + case StartOfBlock: { + TextDocumentIterator it(d->document->d, d->position); + const QLatin1Char newline('\n'); + while (it.hasPrevious() && it.previous() != newline) ; + if (it.hasPrevious()) + it.next(); + setPosition(it.position(), mode); + break; } + case EndOfBlock: { + TextDocumentIterator it(d->document->d, d->position); + const QLatin1Char newline('\n'); + while (it.current() != newline && it.hasNext() && it.next() != newline) ; + setPosition(it.position(), mode); + break; } + + case StartOfWord: + case PreviousWord: + case WordLeft: { + TextDocumentIterator it(d->document->d, d->position); + + while (it.hasPrevious()) { + const QChar ch = it.previous(); + if (d->document->isWordCharacter(ch, it.position())) + break; + } + while (it.hasPrevious()) { + const QChar ch = it.previous(); + if (!d->document->isWordCharacter(ch, it.position())) + break; + } + if (it.hasPrevious()) + it.next(); + setPosition(it.position(), mode); + d->overrideColumn = -1; + break; } + + case NextWord: + case WordRight: + case EndOfWord: { + TextDocumentIterator it(d->document->d, d->position); + while (it.hasNext()) { + const QChar ch = it.next(); + if (d->document->isWordCharacter(ch, it.position())) + break; + } + while (it.hasNext()) { + const QChar ch = it.next(); + if (!d->document->isWordCharacter(ch, it.position())) + break; + } + setPosition(it.position(), mode); + d->overrideColumn = -1; + break; } + + case PreviousBlock: + movePosition(TextPagerCursor::StartOfBlock, mode); + movePosition(TextPagerCursor::Left, mode); + movePosition(TextPagerCursor::StartOfBlock, mode); + break; + + case NextBlock: + movePosition(TextPagerCursor::EndOfBlock, mode); + movePosition(TextPagerCursor::Right, mode); + return true; + + case Left: + case Right: + d->overrideColumn = -1; + setPosition(qBound(0, position() + (op ==TextPagerCursor::Left ? -1 : 1), + d->document->documentSize()), mode); + break; + }; + + return true; +} + +void TextPagerCursor::select(SelectionType selection) +{ + if (!d || !d->document) + return; + + clearSelection(); + + switch (selection) { + case LineUnderCursor: + movePosition(StartOfLine); + movePosition(EndOfLine, KeepAnchor); + break; + case WordUnderCursor: + movePosition(StartOfWord); + movePosition(EndOfWord, KeepAnchor); + break; + case BlockUnderCursor: + movePosition(StartOfBlock); + // also select the paragraph separator + if (movePosition(PreviousBlock)) { + movePosition(EndOfBlock); + movePosition(NextBlock, KeepAnchor); + } + movePosition(EndOfBlock, KeepAnchor); + break; + } +} + +bool TextPagerCursor::hasSelection() const +{ + return !isNull() && d->anchor != d->position; +} + +void TextPagerCursor::clearSelection() +{ + Q_ASSERT(!isNull()); + if (hasSelection()) { + detach(); + SelectionChangedEmitter emitter(textEdit); + d->anchor = d->position; + } +} + +int TextPagerCursor::selectionStart() const +{ + return qMin(d->anchor, d->position); +} + +int TextPagerCursor::selectionEnd() const +{ + return qMax(d->anchor, d->position); +} + +int TextPagerCursor::selectionSize() const +{ + return selectionEnd() - selectionStart(); +} + + +QString TextPagerCursor::selectedText() const +{ + if (isNull() || d->anchor == d->position) + return QString(); + + const int min = qMin(d->anchor, d->position); + const int max = qMax(d->anchor, d->position); + return d->document->read(min, max - min); +} + +bool TextPagerCursor::atBlockStart() const +{ + Q_ASSERT(!isNull()); + return atStart() || d->document->read(d->position - 1, 1).at(0) == '\n'; +} + +bool TextPagerCursor::atBlockEnd() const +{ + Q_ASSERT(!isNull()); + return atEnd() || d->document->read(d->position, 1).at(0) == '\n'; // ### is this right? +} + +bool TextPagerCursor::atStart() const +{ + Q_ASSERT(!isNull()); + return d->position == 0; +} + +bool TextPagerCursor::atEnd() const +{ + Q_ASSERT(!isNull()); + return d->position == d->document->documentSize(); +} + + +bool TextPagerCursor::operator!=(const TextPagerCursor &rhs) const +{ + return !(*this == rhs); +} + +bool TextPagerCursor::operator<(const TextPagerCursor &rhs) const +{ + if (!d) + return true; + + if (!rhs.d) + return false; + + Q_ASSERT_X(d->document == rhs.d->document, "TextCursor::operator<", + "cannot compare cursors attached to different documents"); + + return d->position < rhs.d->position; +} + +bool TextPagerCursor::operator<=(const TextPagerCursor &rhs) const +{ + return *this < rhs || *this == rhs; +} + +bool TextPagerCursor::operator==(const TextPagerCursor &rhs) const +{ + if (isCopyOf(rhs)) + return true; + + if (!d || !rhs.d) + return false; + + return (d->position == rhs.d->position + && d->anchor == rhs.d->anchor + && d->document == rhs.d->document); +} + +bool TextPagerCursor::operator>=(const TextPagerCursor &rhs) const +{ + return *this > rhs || *this == rhs; +} + +bool TextPagerCursor::operator>(const TextPagerCursor &rhs) const +{ + if (!d) + return false; + + if (!rhs.d) + return true; + + Q_ASSERT_X(d->document == rhs.d->document, "TextCursor::operator>=", + "cannot compare cursors attached to different documents"); + + return d->position > rhs.d->position; +} + +bool TextPagerCursor::isCopyOf(const TextPagerCursor &other) const +{ + return d == other.d; +} + +int TextPagerCursor::columnNumber() const +{ + Q_ASSERT(d && d->document); + TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 0); + int col; + textLayout->lineForPosition(d->position, &col); + return col; +} + +int TextPagerCursor::lineNumber() const +{ + Q_ASSERT(d && d->document); + return d->document->lineNumber(position()); +} + +void TextPagerCursor::detach() +{ + Q_ASSERT(d); + int refint = d->ref.fetchAndAddRelaxed(0); // required to get past a bug in Qt 5.2.1 (see Ubuntu 14.04) + if (refint > 1) { + d->ref.deref(); + TextCursorSharedPrivate *p = new TextCursorSharedPrivate; + p->position = d->position; + p->overrideColumn = d->overrideColumn; + p->anchor = d->anchor; + p->document = d->document; + d->document->d->textCursors.insert(p); + d = p; + } +} + +bool TextPagerCursor::ref() +{ + return d && d->ref.ref(); +} + +bool TextPagerCursor::deref() +{ + TextCursorSharedPrivate *dd = d; + d = 0; + if (dd && !dd->ref.deref()) { + if (dd->document) { + const bool removed = dd->document->d->textCursors.remove(dd); + Q_ASSERT(removed); + Q_UNUSED(removed) + } + delete dd; + return false; + } + return true; +} + +void TextPagerCursor::cursorChanged(bool ensureVisible) +{ + if (textEdit) { + if (textEdit->d->cursorBlinkTimer.isActive()) + textEdit->d->cursorVisible = true; + if (ensureVisible) { + Q_EMIT textEdit->cursorPositionChanged(d->position); + textEdit->ensureCursorVisible(); + } + const QRect r = textEdit->cursorBlockRect(*this) & textEdit->viewport()->rect(); + if (!r.isNull()) { + textEdit->viewport()->update(r); + } + } +} + +bool TextPagerCursor::cursorMoveKeyEvent(QKeyEvent *e) +{ + MoveMode mode = MoveAnchor; + MoveOperation op = NoMove; + + if (e == QKeySequence::MoveToNextChar) { + op = Right; + } else if (e == QKeySequence::MoveToPreviousChar) { + op = Left; + } else if (e == QKeySequence::SelectNextChar) { + op = Right; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectPreviousChar) { + op = Left; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectNextWord) { + op = WordRight; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectPreviousWord) { + op = WordLeft; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectStartOfLine) { + op = StartOfLine; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectEndOfLine) { + op = EndOfLine; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectStartOfBlock) { + op = StartOfBlock; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectEndOfBlock) { + op = EndOfBlock; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectStartOfDocument) { + op = Start; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectEndOfDocument) { + op = End; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectPreviousLine) { + op = Up; + mode = KeepAnchor; + } else if (e == QKeySequence::SelectNextLine) { + op = Down; + mode = KeepAnchor; +#if 0 + { + QTextBlock block = cursor.block(); + QTextLine line = currentTextLine(cursor); + if (!block.next().isValid() + && line.isValid() + && line.lineNumber() == block.layout()->lineCount() - 1) + op = End; + } +#endif + } else if (e == QKeySequence::MoveToNextWord) { + op = WordRight; + } else if (e == QKeySequence::MoveToPreviousWord) { + op = WordLeft; + } else if (e == QKeySequence::MoveToEndOfBlock) { + op = EndOfBlock; + } else if (e == QKeySequence::MoveToStartOfBlock) { + op = StartOfBlock; + } else if (e == QKeySequence::MoveToNextLine) { + op = Down; + } else if (e == QKeySequence::MoveToPreviousLine) { + op = Up; + } else if (e == QKeySequence::MoveToStartOfLine) { + op = StartOfLine; + } else if (e == QKeySequence::MoveToEndOfLine) { + op = EndOfLine; + } else if (e == QKeySequence::MoveToStartOfDocument) { + op = Start; + } else if (e == QKeySequence::MoveToEndOfDocument) { + op = End; + } else if (e == QKeySequence::MoveToNextPage || e == QKeySequence::MoveToPreviousPage + || e == QKeySequence::SelectNextPage || e == QKeySequence::SelectPreviousPage) { + const MoveMode mode = (e == QKeySequence::MoveToNextPage + || e == QKeySequence::MoveToPreviousPage + ? MoveAnchor : KeepAnchor); + const MoveOperation operation = (e == QKeySequence::MoveToNextPage + || e == QKeySequence::SelectNextPage + ? Down : Up); + int visibleLines = 10; + if (textEdit) + visibleLines = textEdit->d->visibleLines; + for (int i=0; iviewport()->width() : d->viewportWidth; +} + +void TextPagerCursor::setViewportWidth(int width) +{ + if (textEdit) { + qWarning("It makes no sense to set the viewportWidth of a text cursor that belongs to a text edit. The actual viewportWidth will be used"); + return; + } + d->viewportWidth = width; +} + +QChar TextPagerCursor::cursorCharacter() const +{ + Q_ASSERT(d && d->document); + return d->document->readCharacter(d->anchor); +} + +QString TextPagerCursor::cursorLine() const +{ + Q_ASSERT(d && d->document); + int layoutIndex = -1; + TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 2); // should I use 1? + + QTextLine line = textLayout->lineForPosition(position(), 0, &layoutIndex); + Q_ASSERT(line.isValid()); // ### could this be legitimate? + Q_ASSERT(textLayout); + return textLayout->textLayouts.at(layoutIndex)->text() + .mid(line.textStart(), line.textLength()); + // ### there is a bug here. It doesn't seem to use the right + // ### viewportWidth even though it is the textEdit's cursor +} + +int TextPagerCursor::lineHeight() const +{ + int res = -1; + Q_ASSERT(d && d->document); + int layoutIndex = -1; + TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 2); + QTextLine line = textLayout->lineForPosition(position(), 0, &layoutIndex); + if (line.isValid()) + res = line.height(); + return res; +} + + +QString TextPagerCursor::wordUnderCursor() const +{ + Q_ASSERT(!isNull()); + return d->document->d->wordAt(d->position); +} + +QString TextPagerCursor::paragraphUnderCursor() const +{ + Q_ASSERT(!isNull()); + return d->document->d->paragraphAt(d->position); +} + +QDebug operator<<(QDebug dbg, const TextPagerCursor &cursor) +{ + QString ret = QString::fromLatin1("TextCursor("); + if (cursor.isNull()) { + ret.append(QLatin1String("null)")); + dbg.maybeSpace() << ret; + } else { + if (!cursor.hasSelection()) { + dbg << "anchor/position:" << cursor.anchor() << "character:" << cursor.cursorCharacter(); + } else { + dbg << "anchor:" << cursor.anchor() << "position:" << cursor.position() + << "selectionSize:" << cursor.selectionSize(); + QString selectedText; + if (cursor.selectionSize() > 10) { + selectedText = cursor.document()->read(cursor.selectionStart(), 7); + selectedText.append("..."); + } else { + selectedText = cursor.selectedText(); + } + dbg << "selectedText:" << selectedText; + } + } + return dbg.space(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,140 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERCURSOR_HPP__ +#define TEXTPAGERCURSOR_HPP__ + +#include +#include +#include +#include + +class TextPagerEdit; +class TextPagerLayout; +class TextPagerDocument; +class TextCursorSharedPrivate; +class TextPagerCursor +{ +public: + TextPagerCursor(); + explicit TextPagerCursor(const TextPagerDocument *document, int pos = 0, int anchor = -1); + explicit TextPagerCursor(const TextPagerEdit *document, int pos = 0, int anchor = -1); + TextPagerCursor(const TextPagerCursor &cursor); + TextPagerCursor &operator=(const TextPagerCursor &other); + ~TextPagerCursor(); + + TextPagerDocument *document() const; + bool isNull() const; + inline bool isValid() const { return !isNull(); } + + enum MoveMode { + MoveAnchor, + KeepAnchor + }; + + void setPosition(int pos, MoveMode mode = MoveAnchor); + int position() const; + + void setSelection(int pos, int length); // can be negative + + int viewportWidth() const; + void setViewportWidth(int width); + + int anchor() const; + + QChar cursorCharacter() const; + QString cursorLine() const; + int lineHeight() const; + + QString wordUnderCursor() const; + QString paragraphUnderCursor() const; + + enum MoveOperation { + NoMove, + Start, + Up, + StartOfLine, + StartOfBlock, + StartOfWord, + PreviousBlock, + PreviousCharacter, + PreviousWord, + Left, + WordLeft, + End, + Down, + EndOfLine, + EndOfWord, + EndOfBlock, + NextBlock, + NextCharacter, + NextWord, + Right, + WordRight + }; + + bool movePosition(MoveOperation op, MoveMode = MoveAnchor, int n = 1); + + enum SelectionType { + WordUnderCursor, + LineUnderCursor, + BlockUnderCursor + }; + + void select(SelectionType selection); + + bool hasSelection() const; + + void clearSelection(); + int selectionStart() const; + int selectionEnd() const; + int selectionSize() const; + inline int selectionLength() const { return selectionSize(); } + + QString selectedText() const; + + bool atBlockStart() const; + bool atBlockEnd() const; + bool atStart() const; + bool atEnd() const; + + bool operator!=(const TextPagerCursor &rhs) const; + bool operator<(const TextPagerCursor &rhs) const; + bool operator<=(const TextPagerCursor &rhs) const; + bool operator==(const TextPagerCursor &rhs) const; + bool operator>=(const TextPagerCursor &rhs) const; + bool operator>(const TextPagerCursor &rhs) const; + + bool isCopyOf(const TextPagerCursor &other) const; + + int columnNumber() const; + int lineNumber() const; +private: + bool cursorMoveKeyEvent(QKeyEvent *e); + void cursorChanged(bool ensureCursorVisible); + void detach(); + bool ref(); + bool deref(); + + TextCursorSharedPrivate *d; + TextPagerEdit *textEdit; + friend class TextPagerEdit; + friend class TextLayoutCacheManager; + friend class TextPagerDocument; + friend class TextDocumentPrivate; +}; + +QDebug operator<<(QDebug dbg, const TextPagerCursor &cursor); + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor_p.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor_p.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerCursor_p.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerCursor_p.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,215 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERCURSOR_P_HPP__ +#define TEXTPAGERCURSOR_P_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include "TextPagerCursor_p.hpp" +#include "TextPagerEdit.hpp" +#include "TextPagerEdit_p.hpp" + +static inline bool match(const TextPagerCursor &cursor, const TextPagerLayout *layout, int lines) +{ + Q_ASSERT(cursor.document() == layout->document); + if (cursor.viewportWidth() != -1 && cursor.viewportWidth() != layout->viewport) { + return false; + } + if (layout->viewportPosition > cursor.position() + || layout->layoutEnd < cursor.position()) { + return false; + } + int index = -1; + if (!layout->layoutForPosition(cursor.position(), 0, &index)) { + // ### need an interface for this if I am going to have a mode + // ### that doesn't break lines + return false; + } + + if (index < lines && layout->viewportPosition > 0) { + return false; // need more margin before the cursor + } else if (layout->textLayouts.size() - index - 1 < lines && layout->layoutEnd < layout->document->documentSize()) { + return false; // need more margin after cursor + } + return true; +} + +class TextLayoutCacheManager : public QObject +{ + Q_OBJECT +public: + static TextLayoutCacheManager *instance() + { + static TextLayoutCacheManager *inst = new TextLayoutCacheManager(QCoreApplication::instance()); + return inst; + } + // ### this class doesn't react to TextSections added or removed. I + // ### don't think it needs to since it's only being used for + // ### cursor movement which shouldn't be impacted by these things + + static TextPagerLayout *requestLayout(const TextPagerCursor &cursor, int margin) + { + Q_ASSERT(cursor.document()); + if (cursor.textEdit && match(cursor, cursor.textEdit->d, margin)) { + return cursor.textEdit->d; + } + + TextPagerDocument *doc = cursor.document(); + Q_ASSERT(doc); + QList &layouts = instance()->cache[doc]; + Q_ASSERT(cursor.document()); + Q_FOREACH(TextPagerLayout *l, layouts) { + if (match(cursor, l, margin)) { + return l; + } + } + if (layouts.size() < instance()->maxLayouts) { + if (layouts.isEmpty()) { + connect(cursor.document(), SIGNAL(charactersAdded(int, int)), + instance(), SLOT(onCharactersAddedOrRemoved(int))); + connect(cursor.document(), SIGNAL(charactersRemoved(int, int)), + instance(), SLOT(onCharactersAddedOrRemoved(int))); + connect(cursor.document(), SIGNAL(destroyed(QObject*)), + instance(), SLOT(onDocumentDestroyed(QObject*))); + } + layouts.append(new TextPagerLayout(doc)); + } + TextPagerLayout *l = layouts.last(); + l->viewport = cursor.viewportWidth(); + if (l->viewport == -1) + l->viewport = INT_MAX - 1024; // prevent overflow in comparisons. + if (cursor.textEdit) { + l->textEdit = cursor.textEdit; + l->suppressTextEditUpdates = true; + l->lineBreaking = cursor.textEdit->lineBreaking(); + l->sections = cursor.textEdit->d->sections; + l->font = cursor.textEdit->font(); + l->syntaxHighlighters = cursor.textEdit->syntaxHighlighters(); +// l->extraSelections = cursor.textEdit->extraSelections(); + // ### can the extra selections impact layout? If so they + // ### need to be in the actual textLayout shouldn't need + // ### to care about the actual selection + } + int startPos = (cursor.position() == 0 + ? 0 + : qMax(0, doc->find(QLatin1Char('\n'), cursor.position() - 1, TextPagerDocument::FindBackward).anchor())); + // We start at the beginning of the current line + int linesAbove = margin; + if (startPos > 0) { + while (linesAbove > 0) { + const TextPagerCursor c = doc->find(QLatin1Char('\n'), startPos - 1, TextPagerDocument::FindBackward); + if (c.isNull()) { + startPos = 0; + break; + } + startPos = c.anchor(); + ASSUME(c.anchor() == 0 || c.cursorCharacter() == QLatin1Char('\n')); + ASSUME(c.anchor() == 0 || doc->readCharacter(c.anchor()) == QLatin1Char('\n')); + ASSUME(c.cursorCharacter() == doc->readCharacter(c.anchor())); + + --linesAbove; + } + } + + int linesBelow = margin; + int endPos = cursor.position(); + if (endPos < doc->documentSize()) { + while (linesBelow > 0) { + const TextPagerCursor c = doc->find(QLatin1Char('\n'), endPos + 1); + if (c.isNull()) { + endPos = doc->documentSize(); + break; + } + endPos = c.anchor(); + --linesBelow; + } + } + if (startPos > 0) + ++startPos; // in this case startPos points to the newline before it + l->viewportPosition = startPos; + l->layoutDirty = true; + ASSUME(l->viewportPosition == 0 || doc->readCharacter(l->viewportPosition - 1) == QLatin1Char('\n')); + l->relayoutByPosition(endPos - startPos + 100); // ### fudged a couple of lines likely + ASSUME(l->viewportPosition < l->layoutEnd + || (l->viewportPosition == l->layoutEnd && l->viewportPosition == doc->documentSize())); + ASSUME(l->textLayouts.size() > margin * 2 || l->viewportPosition == 0 || l->layoutEnd == doc->documentSize()); + return l; + } +private Q_SLOTS: + void onDocumentDestroyed(QObject *o) + { + qDeleteAll(cache.take(static_cast(o))); + } + + void onCharactersAddedOrRemoved(int pos) + { + QList &layouts = cache[qobject_cast(sender())]; + ASSUME(!layouts.isEmpty()); + for (int i=layouts.size() - 1; i>=0; --i) { + TextPagerLayout *l = layouts.at(i); + if (pos <= l->layoutEnd) { + delete l; + layouts.removeAt(i); + } + } + if (layouts.isEmpty()) { + disconnect(sender(), 0, this, 0); + cache.remove(qobject_cast(sender())); + } + } +private: + TextLayoutCacheManager(QObject *parent) + : QObject(parent) + { + maxLayouts = qMax(1, qgetenv("TEXTCURSOR_MAX_CACHED_TEXTLAYOUTS").toInt()); + } + + int maxLayouts; + QHash > cache; +}; + +class TextPagerLayout; +struct TextCursorSharedPrivate +{ +public: + TextCursorSharedPrivate() : ref(1), position(-1), anchor(-1), + overrideColumn(-1), viewportWidth(-1), + document(0) + {} + + ~TextCursorSharedPrivate() + { + int refint = ref.fetchAndAddRelaxed(0); // required to get past a bug in Qt 5.2.1 (see Ubuntu 14.04) + ASSUME(refint == 0); + } + + void invalidate() + { + + } + + mutable QAtomicInt ref; + int position, anchor, overrideColumn, viewportWidth; + + TextPagerDocument *document; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1789 @@ +#include "TextPagerDocument.hpp" +#include "TextPagerCursor.hpp" +#include "TextPagerCursor_p.hpp" +#include "TextPagerDocument_p.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define TEXTDOCUMENT_FIND_DEBUG +#define TEXTDOCUMENT_USE_FILE_LOCK + + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + #include +#endif + +// #define DEBUG_CACHE_HITS + +#ifndef TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE +#define TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE 1000 +#endif + +#ifndef TEXTDOCUMENT_MAX_INTERVAL +#define TEXTDOCUMENT_MAX_INTERVAL 1000 +#endif + +#ifdef TEXTDOCUMENT_FIND_SLEEP +static void findSleep(const TextPagerDocument *document) +{ + Q_ASSERT(document); + const int duration = document->property("TEXTDOCUMENT_FIND_SLEEP").toInt(); + if (duration > 0) { + usleep(duration * 1000); + } +} +#endif + +TextPagerDocument::TextPagerDocument(QObject *parent) + : QObject(parent), d(new TextDocumentPrivate(this)) +{ +} + +TextPagerDocument::~TextPagerDocument() +{ + { + QWriteLocker locker(d->readWriteLock); + Q_FOREACH(TextCursorSharedPrivate *cursor, d->textCursors) { + cursor->document = 0; + } + Chunk *c = d->first; + while (c) { + if (!(d->options & KeepTemporaryFiles) && !c->swap.isEmpty()) + QFile::remove(c->swap); + Chunk *tmp = c; + c = c->next; + delete tmp; + } + Q_FOREACH(TextPagerSection *section, d->sections) { + section->d.document = 0; + section->d.textEdit = 0; + delete section; + } + if (d->ownDevice) + delete d->device.data(); + } + delete d->readWriteLock; + delete d; +} + +bool TextPagerDocument::load(QIODevice *device, DeviceMode mode, QTextCodec *codec) +{ + QWriteLocker locker(d->readWriteLock); + Q_ASSERT(device); + if (!device->isReadable()) + return false; + + Options options = d->options; + if (options & ConvertCarriageReturns && mode == Sparse) { + qWarning("TextPagerDocument::load() ConvertCarriageReturns is incompatible with Sparse"); + options &= ~ConvertCarriageReturns; + } + if ((options & (AutoDetectCarriageReturns|ConvertCarriageReturns)) == (AutoDetectCarriageReturns|ConvertCarriageReturns)) + options &= ~AutoDetectCarriageReturns; + + Q_FOREACH(TextPagerSection *section, d->sections) { + Q_EMIT sectionRemoved(section); + section->d.document = 0; + delete section; + } + d->sections.clear(); + + if (d->documentSize > 0) { + Q_EMIT charactersRemoved(0, d->documentSize); + } + + Chunk *c = d->first; + while (c) { + Chunk *tmp = c; + c = c->next; + delete tmp; + } + + d->textCodec = codec; + d->documentSize = device->size(); + if (d->documentSize <= d->chunkSize && mode == Sparse && !(options & NoImplicitLoadAll)) + mode = LoadAll; +#if 0 + if (codec && mode == Sparse) { + + qWarning("Sparse mode doesn't really work with unicode data yet. I am working on it.\n--\nAnders"); + } +#endif + d->first = d->last = 0; + + if (d->device) { + if (d->ownDevice && d->device.data() != device) // this is done when saving to the same file + delete d->device.data(); + } + + d->ownDevice = false; + d->device = device; + d->deviceMode = mode; +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + d->cachedChunk = 0; + d->cachedChunkPos = -1; + d->cachedChunkData.clear(); +#endif +#ifndef NO_TEXTDOCUMENT_READ_CACHE + d->cachePos = -1; + d->cache.clear(); +#endif + + switch (d->deviceMode) { + case LoadAll: { + device->seek(0); + QTextStream ts(device); + if (d->textCodec) + ts.setCodec(d->textCodec); + Chunk *current = 0; + d->documentSize = 0; // in case of unicode + do { + Chunk *c = new Chunk; + c->data = ts.read(d->chunkSize); + if (options & AutoDetectCarriageReturns) { + if (c->data.contains(QLatin1Char('\n'))) { + options |= ConvertCarriageReturns; + } + options &= ~AutoDetectCarriageReturns; + } + if (options & ConvertCarriageReturns) + c->data.remove(QLatin1Char('\r')); + d->documentSize += c->data.size(); + if (current) { + current->next = c; + c->previous = current; + } else { + d->first = c; + } + current = c; + } while (!ts.atEnd()); + + d->last = current; + break; } + + case Sparse: { + int index = 0; + Chunk *current = 0; + do { + Chunk *chunk = new Chunk; + chunk->from = index; + chunk->length = qMin(d->documentSize - index, d->chunkSize); + if (!current) { + d->first = chunk; + } else { + chunk->previous = current; + current->next = chunk; + } + current = chunk; + index += chunk->length; + } while (index < d->documentSize); + + d->last = current; + break; } + } +// if (d->first) +// d->first->firstLineIndex = 0; + Q_EMIT charactersAdded(0, d->documentSize); + Q_EMIT documentSizeChanged(d->documentSize); + Q_EMIT textChanged(); + + return true; +} + +bool TextPagerDocument::load(const QString &fileName, DeviceMode mode, QTextCodec *codec) +{ + if (mode == LoadAll) { + QFile from(fileName); + return from.open(QIODevice::ReadOnly) && load(&from, mode, codec); + } else { + QFile *file = new QFile(fileName); + if (file->open(QIODevice::ReadOnly) && load(file, mode, codec)) { + d->ownDevice = true; + return true; + } else { + delete file; + d->ownDevice = false; + return false; + } + } +} + +void TextPagerDocument::clear() +{ + setText(QString()); +} + +QString TextPagerDocument::read(int pos, int size) const +{ + QReadLocker locker(d->readWriteLock); + Q_ASSERT(size >= 0); + if (size == 0 || pos == d->documentSize) { + return QString(); + } + Q_ASSERT(pos < d->documentSize); + +#ifndef NO_TEXTDOCUMENT_READ_CACHE +#ifdef DEBUG_CACHE_HITS + static int hits = 0; + static int misses = 0; +#endif + if (d->cachePos != -1 && pos >= d->cachePos && d->cache.size() - (pos - d->cachePos) >= size) { +#ifdef DEBUG_CACHE_HITS + qWarning() << "read hits" << ++hits << "misses" << misses; +#endif + return d->cache.mid(pos - d->cachePos, size); + } +#ifdef DEBUG_CACHE_HITS + qWarning() << "read hits" << hits << "misses" << ++misses; +#endif +#endif + + QString ret(size, '\0'); + int written = 0; + int offset; + Chunk *c = d->chunkAt(pos, &offset); + Q_ASSERT(c); + int chunkPos = pos - offset; + + while (written < size && c) { + const int max = qMin(size - written, c->size() - offset); + const QString data = d->chunkData(c, chunkPos); + chunkPos += data.size(); + ret.replace(written, max, data.constData() + offset, max); + written += max; + offset = 0; + c = c->next; + } + + if (written < size) { + ret.truncate(written); + } + Q_ASSERT(!c || written == size); +#ifndef NO_TEXTDOCUMENT_READ_CACHE + d->cachePos = pos; + d->cache = ret; +#endif + return ret; +} + +QStringRef TextPagerDocument::readRef(int pos, int size) const +{ + QReadLocker locker(d->readWriteLock); + int offset; + Chunk *c = d->chunkAt(pos, &offset); + if (c && pos + offset + size <= c->size()) { + const QString string = d->chunkData(c, pos - offset); + return string.midRef(offset, size); + } + return QStringRef(); +} + +#if 0 +static bool isSameFile(const QIODevice *left, const QIODevice *right) +{ + if (left == right) + return true; + if (const QFile *lf = qobject_cast(left)) { + if (const QFile *rf = qobject_cast(right)) { + return QFileInfo(*lf) == QFileInfo(*rf); + } + } + return false; +} +#endif + +int TextPagerDocument::documentSize() const +{ + QReadLocker locker(d->readWriteLock); + return d->documentSize; +} + +int TextPagerDocument::chunkCount() const +{ + QReadLocker locker(d->readWriteLock); + Chunk *c = d->first; + int count = 0; + while (c) { + ++count; + c = c->next; + } + return count; +} + +int TextPagerDocument::instantiatedChunkCount() const +{ + QReadLocker locker(d->readWriteLock); + Chunk *c = d->first; + int count = 0; + while (c) { + if (!c->data.isEmpty()) + ++count; + c = c->next; + } + return count; +} + +int TextPagerDocument::swappedChunkCount() const +{ + QReadLocker locker(d->readWriteLock); + Chunk *c = d->first; + int count = 0; + while (c) { + if (!c->swap.isEmpty()) + ++count; + c = c->next; + } + return count; +} + +TextPagerDocument::DeviceMode TextPagerDocument::deviceMode() const +{ + QReadLocker locker(d->readWriteLock); + return d->deviceMode; +} + +QTextCodec * TextPagerDocument::textCodec() const +{ + QReadLocker locker(d->readWriteLock); + return d->textCodec; +} + +class FindScope +{ +public: + FindScope(TextDocumentPrivate::FindState *s) : state(s) { if (state) *state = TextDocumentPrivate::Finding; } + ~FindScope() { if (state) *state = TextDocumentPrivate::NotFinding; } + TextDocumentPrivate::FindState *state; +}; + +static void initFind(const TextPagerCursor &cursor, bool reverse, int *start, int *limit) +{ + if (cursor.hasSelection()) { + *start = cursor.selectionStart(); + *limit = cursor.selectionEnd(); + if (reverse) { + qSwap(*start, *limit); + } + } else { + *start = cursor.position(); + *limit = (reverse ? 0 : cursor.document()->documentSize()); + } + + +} + +static void initFindForLines(const TextPagerCursor &cursor, bool reverse, int *start, int *limit) +{ + *start = cursor.position(); + if(*limit == -1) + *limit = (reverse ? 0 : cursor.document()->documentSize()); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "initial position" << "pos:" << *start << "anchor:" << cursor.anchor(); +#endif + + //Change the search position according to the selection and search + //direction + if(cursor.anchor() >=0) + { + int ca=cursor.anchor(); + + if(!reverse) + { + if(*start < ca) + *start=ca; + } + else + { + if(*start > ca && ca >0) + *start=ca-1; + else if(*start >0) + *start=(*start)-1; + } + } + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "modified position" << "pos:" << *start << "anchor:" << cursor.anchor(); +#endif + +} + +TextPagerCursor TextPagerDocument::find(const QRegExp ®exp, const TextPagerCursor &cursor, FindMode flags,int limit) const +{ +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "---> TextPagerDocument::find" << "regexp" << regexp; +#endif + + if(documentSize() == 0) + return TextPagerCursor(); + + if(regexp.isEmpty()) + return TextPagerCursor(); + + QReadLocker locker(d->readWriteLock); + if (flags & FindWholeWords) { + qWarning("FindWholeWords doesn't work with regexps. Instead use an actual RegExp for this"); + } + if (flags & FindCaseSensitively) { + qWarning("FindCaseSensitively doesn't work with regexps. Instead use an QRegExp::caseSensitivity for this"); + } + /* if (flags & FindWrap && cursor.hasSelection()) { + qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); + flags &= ~FindWrap; + }*/ + + const bool reverse = flags & FindBackward; + int pos; + ::initFindForLines(cursor, reverse, &pos, &limit); + + if(reverse) { + if(pos < limit) + return TextPagerCursor(); + } else { + if(pos > limit) + return TextPagerCursor(); + } + + if (pos == d->documentSize) { + if (reverse) { + --pos; + } else if (!(flags & FindWrap)) { + return TextPagerCursor(); + } + } + + //const TextDocumentIterator::Direction direction = (reverse + // ? TextDocumentIterator::Left + // : TextDocumentIterator::Right); + TextDocumentIterator it(d, pos); + if (reverse) { + it.setMinBoundary(limit); + } else { + it.setMaxBoundary(limit); + } + const QLatin1Char newline('\n'); + bool ok = true; + //int progressInterval = 0; + const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); + QTime lastProgressTime; + if (flags & FindAllowInterrupt) { + //progressInterval = qMax(1, (reverse + // ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) + // : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); + //maxFindLength = (reverse ? pos : d->documentSize - pos); + lastProgressTime.start(); + } + + QString line; + int index; + int from; +#ifdef TEXTDOCUMENT_FIND_DEBUG + QTime lap; + lap.start(); +#endif + + //Loop over the lines in the document. Since we can have millions of lines it has to be + //extremely fast. + do { +#ifdef TEXTDOCUMENT_FIND_SLEEP + findSleep(this); +#endif + + //This clears the string without reallocation + line.resize(0); + + if(((reverse)?it.prevLine(line):it.nextLine(line)) == 0) + { + ok=false; + if(line.isEmpty()) + break; + } + + //We only search for the first occurrence. So the FindAll flag is ignored. + + //QRegExp::lastIndexIn() is 3 times slower than QRegExp::indexIn()!! So we always call + //indexIn() first the lastIndexIn() if we need the reverse order. + + if((index = regexp.indexIn(line, 0)) != -1) { + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << line; +#endif + + if(reverse) { + index = regexp.lastIndexIn(line, line.size()); + from = it.position(); //-line.size(); + + if(from + index < limit) { + ok = false; + } else { + const TextPagerCursor ret(this, from + index, from + index + regexp.matchedLength()); +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); + qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); +#endif + return ret; + } + } else { + + from = it.position()-line.size()+1; + + if(from + index + regexp.matchedLength() > limit) { + ok = false; + } else { + + //we need to remove the newline char from the end of the matching text + QString captured=regexp.capturedTexts().first(); + if(index + regexp.matchedLength() == line.size() && line.endsWith(newline)) + { + captured.chop(1); + } + + const TextPagerCursor ret(this, from + index + captured.size(), from + index); +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "current:" << it.current() << it.position() << line.size(); + qDebug() << "captured:" << index << captured; + qDebug() << "cursor:" << ret.selectedText(); +#endif + Q_ASSERT(ret.selectedText() == captured); +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); + qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); +#endif + return ret; + } + } + } + + + /*if (progressInterval != 0) { + const int progress = qAbs(it.position() - lastProgress); + if (progress >= progressInterval + || (lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { + const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); + Q_EMIT findProgress(progress * 100.0, it.position()); + if (d->findState == TextDocumentPrivate::AbortFind) { + return TextPagerCursor(); + } + lastProgress = it.position(); + lastProgressTime.restart(); + } + }*/ + + } while (ok); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); + qDebug() << "iterator position:" << it.position(); +#endif + + if (flags & FindWrap) { + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "---> end of doc reached. FindWrap will start."; +#endif + //Q_ASSERT(!cursor.hasSelection()); + if (reverse) { + if (cursor.position() + 1 < d->documentSize) { + return find(regexp, TextPagerCursor(this, d->documentSize), flags & ~FindWrap, cursor.position()); + } + } else if (cursor.position() > 0) { + return find(regexp, TextPagerCursor(this, 0), flags & ~FindWrap, cursor.position()); + } + } + + return TextPagerCursor(); +} + +TextPagerCursor TextPagerDocument::find(const QString &in, const TextPagerCursor &cursor, FindMode flags, int limit) const +{ +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "---> TextPagerDocument::find" << "string:" << in; +#endif + + if (in.isEmpty()) { + return TextPagerCursor(); + } + + QReadLocker locker(d->readWriteLock); + + const bool reverse = flags & FindBackward; + const bool caseSensitive = flags & FindCaseSensitively; + const bool wholeWords = flags & FindWholeWords; + + /* if (flags & FindWrap && cursor.hasSelection()) { + qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); + flags &= ~FindWrap; + } +*/ + const QLatin1Char newline('\n'); + int pos; + ::initFindForLines(cursor, reverse, &pos, &limit); + + //Sanity check!! + Q_ASSERT(pos >= 0 && pos <= d->documentSize); + Q_ASSERT(limit >= 0 && limit <= d->documentSize); + + if(reverse) { + if(pos < limit) + return TextPagerCursor(); + } else { + if(pos > limit) + return TextPagerCursor(); + } + + if (pos == d->documentSize) { + if (reverse) { + --pos; + } else if (!(flags & FindWrap)) { + return TextPagerCursor(); + } + } + + // ### what if one searches for a string with non-word characters in it and FindWholeWords? + //const TextDocumentIterator::Direction direction = (reverse ? TextDocumentIterator::Left : TextDocumentIterator::Right); + QString word = caseSensitive ? in : in.toLower(); + + TextDocumentIterator it(d, pos); + if (reverse) { + it.setMinBoundary(limit); + } else { + it.setMaxBoundary(limit); + } + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "current character:" << it.current(); +#endif + + if (!caseSensitive) + it.setConvertToLowerCase(true); + + bool ok = true; + //QChar ch = it.current(); + //int progressInterval = 0; + const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); + QTime lastProgressTime; + if (flags & FindAllowInterrupt) { + //progressInterval = qMax(1, (reverse + // ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) + // : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); + //maxFindLength = (reverse ? pos : d->documentSize - pos); + lastProgressTime.start(); + } + + QString line; + int index; + int from; +#ifdef TEXTDOCUMENT_FIND_DEBUG + QTime lap; + lap.start(); +#endif + + do { +#ifdef TEXTDOCUMENT_FIND_SLEEP + findSleep(this); +#endif + /* if (progressInterval != 0) { + const int progress = qAbs(it.position() - lastProgress); + if (progress >= progressInterval + || (progress % 10 == 0 && lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { + const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); + Q_EMIT findProgress(progress * 100.0, it.position()); + if (d->findState == TextDocumentPrivate::AbortFind) { + return TextPagerCursor(); + } + lastProgress = it.position(); + lastProgressTime.restart(); + } + }*/ + + //This clears the string without reallocation + line.resize(0); + + if(((reverse)?it.prevLine(line):it.nextLine(line)) == 0) + { + ok=false; + if(line.isEmpty()) + break; + } + + if(!caseSensitive) + line=line.toLower(); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + //qDebug() << line; +#endif + + if((index=line.indexOf(word)) != -1) { + + //Backward: + //The iterator is positioned at the linebreak character of the previous line, or at + //the start of the document + if(reverse) { + from = it.position(); + index=line.lastIndexOf(word); + } + //Forward: + //The iterator is positioned at the linebreak character at the end of the line or at + //the end of the document + else { + from = it.position()-line.size()+1; + } + + while(ok && index != -1) { + + const int startPos=from + index; + const int endPos=from + index + word.size(); + + if(!reverse && endPos > limit) { + ok = false; + break; + } + + bool found=true; + if(wholeWords) { + + if(TextDocumentIterator::Left != d->wordBoundariesAt(startPos)) { + found = false; + } + if(found) { + if(TextDocumentIterator::Right != d->wordBoundariesAt(endPos-1)) { + found = false; + } + } + } + + if(found) { + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << line; +#endif + //Backward + if(reverse) { + TextPagerCursor ret(this, startPos, endPos); +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); + qDebug() << "current:" << it.current() << it.position() << line.size(); + qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); +#endif + return ret; + //Forward + } else { + + TextPagerCursor ret(this, endPos,startPos); +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); + qDebug() << "current:" << it.current() << it.position() << line.size(); + qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); +#endif + return ret; + } + + //If it is not a wholeword we try to match every other match int he same line + } else { + + if(reverse) { + index=line.lastIndexOf(word,index-1); + } else { + index=line.indexOf(word,index+word.size()); + } + + } + } //while + + } + +#if 0 + bool found = ch == word.at(wordIndex); + if (found && wholeWords && (wordIndex == 0 || wordIndex == word.size() - 1)) { + Q_ASSERT(word.size() > 1); + const uint requiredBounds = ((wordIndex == 0) != reverse) + ? TextDocumentIterator::Left + : TextDocumentIterator::Right; + const uint bounds = d->wordBoundariesAt(it.position()); + if (requiredBounds & ~bounds) { + found = false; + } + } + if (found) { + if (++wordIndex == word.size()) { + const int pos = it.position() - (reverse ? 0 : word.size() - 1); + // the iterator reads one past the last matched character so we have to account for that here + const TextPagerCursor ret(this, pos + wordIndex, pos); + if (flags & FindAll) { + Q_EMIT entryFound(ret); + if (d->findState == TextDocumentPrivate::AbortFind) + return TextPagerCursor(); + wordIndex = 0; + } else { + return ret; + } + } + } else if (wordIndex != 0) { + wordIndex = 0; + continue; + } + ch = it.nextPrev(direction, ok); + +#endif + + } while (ok); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "total time" << lap.elapsed(); +#endif + + if (flags & FindWrap) { + //Q_ASSERT(!cursor.hasSelection()); + if (reverse) { + + if (cursor.position() + 1 < d->documentSize) { + return find(in, TextPagerCursor(this, d->documentSize), flags & ~FindWrap,cursor.position()); + } + } else if (cursor.position() > 0) { + return find(in, TextPagerCursor(this, 0), flags & ~FindWrap,cursor.position()); + } + } + + return TextPagerCursor(); +} + +TextPagerCursor TextPagerDocument::find(const QChar &chIn, const TextPagerCursor &cursor, FindMode flags) const +{ + QReadLocker locker(d->readWriteLock); + if (flags & FindWrap && cursor.hasSelection()) { + qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); + flags &= ~FindWrap; + } + + const bool reverse = flags & FindBackward; + int pos; + int limit; + ::initFind(cursor, reverse, &pos, &limit); + if (pos == d->documentSize) { + if (reverse) { + --pos; + } else if (!(flags & FindWrap)) { + return TextPagerCursor(); + } + } + Q_ASSERT(pos >= 0 && pos <= d->documentSize); + + const bool caseSensitive = flags & FindCaseSensitively; + const bool wholeWords = flags & FindWholeWords; + const QChar ch = (caseSensitive ? chIn : chIn.toLower()); + TextDocumentIterator it(d, pos); + if (reverse) { + it.setMinBoundary(limit); + } else { + it.setMaxBoundary(limit); + } + const TextDocumentIterator::Direction dir = (reverse + ? TextDocumentIterator::Left + : TextDocumentIterator::Right); + int lastProgress = pos; + const int initialPos = pos; + int maxFindLength = 0; + int progressInterval = 0; + const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); + QTime lastProgressTime; + if (flags & FindAllowInterrupt) { + progressInterval = qMax(1, (reverse + ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) + : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); + maxFindLength = (reverse ? pos : d->documentSize - pos); + lastProgressTime.start(); + } + + QChar c = it.current(); + bool ok = true; + do { +#ifdef TEXTDOCUMENT_FIND_SLEEP + findSleep(this); +#endif + if (((caseSensitive ? c : c.toLower()) == ch) + && (!wholeWords || (d->wordBoundariesAt(it.position()) == TextDocumentIterator::Both))) { + const TextPagerCursor ret(this, it.position() + 1, it.position()); + if (flags & FindAll) { + Q_EMIT entryFound(ret); + if (d->findState == TextDocumentPrivate::AbortFind) + return TextPagerCursor(); + } else { + return ret; + } + } + c = it.nextPrev(dir, ok); +// qDebug() << "progressInterval" << progressInterval << qAbs(it.position() - lastProgress) +// << lastProgressTime.elapsed() << TEXTDOCUMENT_MAX_INTERVAL; + if (progressInterval != 0) { + const int progress = qAbs(it.position() - lastProgress); + if (progress >= progressInterval + || (progress % 10 == 0 && lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { + const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); + Q_EMIT findProgress(progress * 100.0, it.position()); + if (d->findState == TextDocumentPrivate::AbortFind) { + return TextPagerCursor(); + } + lastProgress = it.position(); + lastProgressTime.restart(); + } + } + } while (ok); + + if (flags & FindWrap) { + Q_ASSERT(!cursor.hasSelection()); + if (reverse) { + if (cursor.position() + 1 < d->documentSize) { + return find(ch, TextPagerCursor(this, cursor.position(), d->documentSize), flags & ~FindWrap); + } + } else if (cursor.position() > 0) { + return find(ch, TextPagerCursor(this, 0, cursor.position()), flags & ~FindWrap); + } + } + + return TextPagerCursor(); +} + +static inline int count(const QString &string, int from, int size, const QChar &ch) +{ + Q_ASSERT(from + size <= string.size()); + const ushort needle = ch.unicode(); + const ushort *haystack = string.utf16() + from; + int num = 0; + for (int i=0; ireadWriteLock); + Q_ASSERT(section); + Q_ASSERT(section->document() == this); + + QList::iterator first = qLowerBound(d->sections.begin(), d->sections.end(), section, compareTextSection); + Q_ASSERT(first != d->sections.end()); + const QList::iterator last = qUpperBound(d->sections.begin(), d->sections.end(), section, compareTextSection); + + while (first != last) { + if (*first == section) { + Q_EMIT sectionRemoved(section); + d->sections.erase(first); + break; + } + ++first; + } + + // Moved this to the end as the slots called by sectionRemoved (presently) rely + // on section->d.document to be valid. + section->d.textEdit = 0; + section->d.document = 0; +} + +QList TextPagerDocument::sections(int pos, int size, TextPagerSection::TextSectionOptions flags) const +{ + QReadLocker locker(d->readWriteLock); + return d->getSections(pos, size, flags, 0); +} + +void TextPagerDocument::insertTextSection(TextPagerSection *section) +{ + QWriteLocker locker(d->readWriteLock); + Q_ASSERT(!d->sections.contains(section)); + QList::iterator it = qLowerBound::iterator>(d->sections.begin(), d->sections.end(), + section, compareTextSection); + d->sections.insert(it, section); + Q_EMIT sectionAdded(section); +} + +TextPagerSection *TextPagerDocument::insertTextSection(int pos, int size, + const QTextCharFormat &format, const QVariant &data) +{ + QWriteLocker locker(d->readWriteLock); + Q_ASSERT(pos >= 0); + Q_ASSERT(size >= 0); + Q_ASSERT(pos < d->documentSize); + + TextPagerSection *l = new TextPagerSection(pos, size, this, format, data); + QList::iterator it = qLowerBound::iterator>(d->sections.begin(), d->sections.end(), l, compareTextSection); + d->sections.insert(it, l); + Q_EMIT sectionAdded(l); + return l; +} + +bool TextPagerDocument::abortSave() +{ + if (d->saveState == TextDocumentPrivate::Saving) { + d->saveState = TextDocumentPrivate::AbortSave; + return true; + } + return false; +} + +bool TextPagerDocument::abortFind() const +{ + if (d->findState == TextDocumentPrivate::Finding) { + d->findState = TextDocumentPrivate::AbortFind; + return true; + } + return false; +} + + +QChar TextPagerDocument::readCharacter(int pos) const +{ + QReadLocker locker(d->readWriteLock); + if (pos == d->documentSize) + return QChar(); + Q_ASSERT(pos >= 0 && pos < d->documentSize); +#ifndef NO_TEXTDOCUMENT_READ_CACHE +#ifdef DEBUG_CACHE_HITS + static int hits = 0; + static int misses = 0; +#endif + if (pos >= d->cachePos && pos < d->cachePos + d->cache.size()) { +#ifdef DEBUG_CACHE_HITS + qWarning() << "readCharacter hits" << ++hits << "misses" << misses; +#endif + return d->cache.at(pos - d->cachePos); + } +#ifdef DEBUG_CACHE_HITS + qWarning() << "readCharacter hits" << hits << "misses" << ++misses; +#endif +#endif + + int offset; + Chunk *c = d->chunkAt(pos, &offset); + return d->chunkData(c, pos - offset).at(offset); +} + +void TextPagerDocument::setText(const QString &text) +{ + // ### could optimize this to avoid detach and copy if text.size() <= chunkSize + // ### but is it worth it? + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + QTextStream ts(&buffer); + if (d->textCodec) + ts.setCodec(d->textCodec); + ts << text; + buffer.close(); + buffer.open(QIODevice::ReadOnly); + const bool ret = load(&buffer, LoadAll); + Q_ASSERT(ret); + Q_UNUSED(ret); +} + +int TextPagerDocument::chunkSize() const +{ + QReadLocker locker(d->readWriteLock); + return d->chunkSize; +} + +void TextPagerDocument::setChunkSize(int size) +{ + QWriteLocker locker(d->readWriteLock); + Q_ASSERT(d->chunkSize > 0); + d->chunkSize = size; +} + +int TextPagerDocument::currentMemoryUsage() const +{ + QReadLocker locker(d->readWriteLock); + Chunk *c = d->first; + int used = 0; + while (c) { + used += c->data.size() * sizeof(QChar); + c = c->next; + } + return used; +} + +bool TextPagerDocument::isModified() const +{ + // ### should it lock for read? + return d->modified; +} + +int TextPagerDocument::lineNumber(int position) const +{ + QReadLocker locker(d->readWriteLock); + d->hasChunksWithLineNumbers = true; // does this need to be a write lock? + int offset; + Chunk *c = d->chunkAt(position, &offset); + d->updateChunkLineNumbers(c, position - offset); + Q_ASSERT(c->firstLineIndex != -1); + Q_ASSERT(d->first->firstLineIndex != -1); + const int extra = (offset == 0 ? 0 : d->countNewLines(c, position - offset, offset)); + +//#ifdef QT_DEBUG +#if 0 + if (position <= 16000) { + const QString data = read(0, position); + // if we're on a newline it shouldn't count so we do read(0, position) + // not read(0, position + 1); + const int count = data.count(QLatin1Char('\n')); + if (count != c->firstLineIndex + extra) { + qDebug() << "TextPagerDocument::lineNumber returns" << (c->firstLineIndex + extra) + << "should have returned" << (count + 1) + << "for index" << position; + } + } +#endif + + return c->firstLineIndex + extra; +} + +int TextPagerDocument::columnNumber(int position) const +{ + TextPagerCursor cursor(this, position); + return cursor.isNull() ? -1 : cursor.columnNumber(); +} + +int TextPagerDocument::lineNumber(const TextPagerCursor &cursor) const +{ + return cursor.document() == this ? lineNumber(cursor.position()) : -1; +} + +int TextPagerDocument::columnNumber(const TextPagerCursor &cursor) const +{ + return cursor.document() == this ? cursor.columnNumber() : -1; +} + +TextPagerCursor TextPagerDocument::findLine(int lineNum, const TextPagerCursor &cursor) const +{ + if(lineNum <=0) + return TextPagerCursor(); + + lineNum--; + + Q_ASSERT(cursor.position() >=0); + + int current=lineNumber(cursor); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "findLine --> line:" << lineNum << "current:" << current; +#endif + + if(lineNum == current) + return cursor; + + int offset; + Chunk *c = d->chunkAt(cursor.position(), &offset); + + Q_ASSERT(c != NULL); + + int pos=cursor.position() - offset; //points to the chunks beginning + d->updateChunkLineNumbers(c, pos); + +#ifdef TEXTDOCUMENT_FIND_DEBUG + qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; +#endif + + if(lineNum < current) { + + while(c->firstLineIndex > lineNum && c->previous) { + pos-=c->size(); + c=c->previous; + d->updateChunkLineNumbers(c,pos); +#ifdef TEXTDOCUMENT_FIND_DEBUG + //qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; +#endif + } + + } else if(lineNum > current) { + + while(c->firstLineIndex < lineNum && c->next) { + pos+=c->size(); + c=c->next; + d->updateChunkLineNumbers(c,pos); +#ifdef TEXTDOCUMENT_FIND_DEBUG + //qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; +#endif + } + + Q_ASSERT(c != NULL && c->previous != NULL); + + c=c->previous; + pos-=c->size(); + } + +#ifdef TEXTDOCUMENT_FIND_DEBUG + if(c) qDebug() << "chunk found - first line:" << c->firstLineIndex << "pos:" << pos; + else qDebug() << "chunk not found"; +#endif + if(c && c->firstLineIndex != -1 && c->firstLineIndex <= lineNum) { + + current=c->firstLineIndex; + if(current == lineNum) + return TextPagerCursor(this,pos); + + QChar newline('\n'); + int index=0; + QString data=d->chunkData(c,-1); + while((index=data.indexOf(newline,index)) != -1) { +#ifdef TEXTDOCUMENT_FIND_DEBUG + //qDebug() << "chunk found - line:" << current << "index:" << index; +#endif + if(current == lineNum) { + TextPagerCursor ret(this,pos+index); + return ret; + } + index++; + current++; + } + } + + return TextPagerCursor(); +} + + +void TextPagerDocument::setOptions(Options opt) +{ + d->options = opt; + if ((d->options & Locking) != (d->readWriteLock != 0)) { + if (d->readWriteLock) { + delete d->readWriteLock; + } else { + d->readWriteLock = new QReadWriteLock(QReadWriteLock::Recursive); + } + } +} + +TextPagerDocument::Options TextPagerDocument::options() const +{ + return d->options; +} + +bool TextPagerDocument::isWordCharacter(const QChar &ch, int /*index*/) const +{ + // from qregexp. + return ch.isLetterOrNumber() || ch.isMark() || ch == QLatin1Char('_'); +} + +QString TextPagerDocument::swapFileName(Chunk *chunk) +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QReadLocker locker(d->readWriteLock); + QString file = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first(); + file.reserve(file.size() + 24); + QTextStream ts(&file); + ts << QLatin1Char('/') << QLatin1String("lte_") << chunk << QLatin1Char('_') + << this << QLatin1Char('_') << QCoreApplication::applicationPid(); + return file; +#endif + return QString(); +} + +//=========================================================================== +// +// TextDocumentPrivate +// +//=========================================================================== + +Chunk *TextDocumentPrivate::chunkAt(int p, int *offset) const +{ + Q_ASSERT(p <= documentSize); + Q_ASSERT(p >= 0); + Q_ASSERT(first); + Q_ASSERT(last); + if (p == documentSize) { + if (offset) + *offset = last->size(); + return last; + } +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + Q_ASSERT(!cachedChunk || cachedChunkPos != -1); + if (cachedChunk && p >= cachedChunkPos && p < cachedChunkPos + cachedChunkData.size()) { + if (offset) + *offset = p - cachedChunkPos; + return cachedChunk; + } +#endif + int pos = p; + Chunk *c = first; + + Q_FOREVER { + const int size = c->size(); + if (pos < size) { + break; + } + pos -= size; + c = c->next; + Q_ASSERT(c); + } + + if (offset) + *offset = pos; + + Q_ASSERT(c); + return c; +} + + +/* Evil double meaning of pos here. If it's -1 we don't cache it. */ +QString TextDocumentPrivate::chunkData(const Chunk *chunk, int chunkPos) const +{ +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE +#ifdef DEBUG_CACHE_HITS + static int hits = 0; + static int misses = 0; +#endif + if (chunk == cachedChunk) { +#ifdef DEBUG_CACHE_HITS + qWarning() << "chunkData hits" << ++hits << "misses" << misses; +#endif + Q_ASSERT(cachedChunkData.size() == chunk->size()); + return cachedChunkData; + } else +#endif + if (chunk->from == -1) { + return chunk->data; + } else if (!device && chunk->swap.isEmpty()) { + // Can only happen if the device gets deleted behind our back when in Sparse mode + return QString().fill(QLatin1Char(' '), chunk->size()); + } else { + QIODevice *dev = device.data(); +#if 0 + QFile file; + if (!chunk->swap.isEmpty()) { + file.setFileName(chunk->swap); + if (!file.open(QIODevice::ReadOnly)) { + qWarning("TextDocumentPrivate::chunkData() Can't open file for reading '%s'", qPrintable(chunk->swap)); + return QString().fill(QLatin1Char(' '), chunk->size()); + } + dev = &file; + } +#endif + QTextStream ts(dev); + //if (textCodec) + // ts.setCodec(textCodec); +// if (!chunk->swap.isEmpty()) { +// qDebug() << "reading stuff from swap" << chunk << chunk->from << chunk->size() << chunk->swap; +// } + ts.seek(chunk->from); + +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + + if (chunkPos != -1) { + cachedChunkData = ts.read(chunk->length); + + Q_ASSERT(cachedChunkData.size() == chunk->size()); + + cachedChunk = const_cast(chunk); + cachedChunkPos = chunkPos; + + return cachedChunkData; +/*#ifdef QT_DEBUG + if (chunkPos != chunk->pos()) { + qWarning() << chunkPos << chunk->pos(); + } + Q_ASSERT(chunkPos == chunk->pos()); +#endif*/ + } else { + const QString data = ts.read(chunk->length); + return data; + } + +#else + const QString data = ts.read(chunk->length); + return data; +#endif + } + //Q_ASSERT(data.size() == chunk->size()); +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE +#ifdef DEBUG_CACHE_HITS + qWarning() << "chunkData hits" << hits << "misses" << ++misses; +#endif +#endif + +#if 0 + if (chunkPos != -1) { + cachedChunk = const_cast(chunk); + cachedChunkData = data; + cachedChunkPos = chunkPos; +#ifdef QT_DEBUG + if (chunkPos != chunk->pos()) { + qWarning() << chunkPos << chunk->pos(); + } + Q_ASSERT(chunkPos == chunk->pos()); +#endif + } + + //return data; + } +#endif + return QString(); +} + +int TextDocumentPrivate::chunkIndex(const Chunk *c) const +{ + int index = 0; + while (c->previous) { + ++index; + c = c->previous; + } + return index; +} + +void TextDocumentPrivate::instantiateChunk(Chunk *chunk) +{ + if (chunk->from == -1 && chunk->swap.isEmpty()) + return; + chunk->data = chunkData(chunk, -1); +// qDebug() << "instantiateChunk" << chunk << chunk->swap; + chunk->swap.clear(); +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + // Don't want to cache this chunk since it's going away. If it + // already was cached then sure, but otherwise don't + if (chunk == cachedChunk) { + cachedChunk = 0; + cachedChunkPos = -1; + cachedChunkData.clear(); + } +#endif + chunk->from = chunk->length = -1; +} + +void TextDocumentPrivate::removeChunk(Chunk *c) +{ + Q_ASSERT(c); + if (c == first) { + first = c->next; + } else { + c->previous->next = c->next; + } + if (c == last) { + last = c->previous; + } else { + c->next->previous = c->previous; + } +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + if (c == cachedChunk) { + cachedChunk = 0; + cachedChunkPos = -1; + cachedChunkData.clear(); + } +#endif + if (!first) { + Q_ASSERT(!last); + first = last = new Chunk; + } + + delete c; +} + +QString TextDocumentPrivate::wordAt(int position, int *start) const +{ + TextDocumentIterator from(this, position); + if (!q->isWordCharacter(from.current(), position)) { + if (start) + *start = -1; + return QString(); + } + + while (from.hasPrevious()) { + const QChar ch = from.previous(); + if (!q->isWordCharacter(ch, from.position())) { + // ### could just peek rather than going one too far + from.next(); + break; + } + } + TextDocumentIterator to(this, position); + while (to.hasNext()) { + const QChar ch = to.next(); + if (!q->isWordCharacter(ch, to.position())) + break; + } + + if (start) + *start = from.position(); + return q->read(from.position(), to.position() - from.position()); +} + +QString TextDocumentPrivate::paragraphAt(int position, int *start) const +{ + const QLatin1Char newline('\n'); + TextDocumentIterator from(this, position); + while (from.hasPrevious() && from.previous() != newline) + ; + TextDocumentIterator to(this, position); + while (to.hasNext() && to.next() != newline) + ; + if (start) + *start = from.position(); + return q->read(from.position(), to.position() - from.position()); +} + +uint TextDocumentPrivate::wordBoundariesAt(int pos) const +{ + Q_ASSERT(pos >= 0 && pos < documentSize); + uint ret = 0; + if (pos == 0 || !q->isWordCharacter(q->readCharacter(pos - 1), pos - 1)) { + ret |= TextDocumentIterator::Left; + } + if (pos + 1 == documentSize || !q->isWordCharacter(q->readCharacter(pos + 1), pos + 1)) { + ret |= TextDocumentIterator::Right; + } + return ret; +} + +void TextDocumentPrivate::updateChunkLineNumbers(Chunk *c, int chunkPos) const +{ + Q_ASSERT(c); + if (c->firstLineIndex == -1) { + Chunk *cc = c; + int pos = chunkPos; + while (cc->previous && cc->previous->firstLineIndex == -1) { + //pos -= cc->size(); + //Here chunkPos points to the position (the beginning) of the chunk so + //the line above was incorrect had to be changed like this: + pos -=cc->previous->size(); + cc = cc->previous; + } + // cc is at the first chunk that has firstLineIndex != -1 + Q_ASSERT(!cc->previous || cc->previous->firstLineIndex != -1); + Q_ASSERT(cc->firstLineIndex == 1 || cc->firstLineIndex == -1); + // special case for first chunk + do { + const int size = cc->size(); + if (!cc->previous) { + cc->firstLineIndex = 0; + } else { + const int prevSize = cc->previous->size(); + const int lineCount = countNewLines(cc->previous, pos - prevSize, prevSize); + Q_ASSERT(cc->previous->firstLineIndex != -1); + cc->firstLineIndex = cc->previous->firstLineIndex + lineCount; + } + pos += size; + cc = cc->next; + } while (cc && cc != c->next); + countNewLines(c, chunkPos, c->size()); + } + Q_ASSERT(c->firstLineIndex != -1); +} + +static inline QList dumpNewLines(const QString &string, int from, int size) +{ + QList ret; + for (int i=from; ifirstLineIndex << chunkPos << size +// << c->size(); + int ret = 0; +#ifndef TEXTDOCUMENT_LINENUMBER_CACHE + if (size == c->size()) { + if (c->lines == -1) { + c->lines = ::count(chunkData(c, chunkPos), 0, size, QLatin1Char('\n')); +// qDebug() << "counting" << c->lines << "in" << chunkIndex(c) +// << "Size" << size << "chunkPos" << chunkPos; + } + ret = c->lines; + } else { + ret = ::count(chunkData(c, chunkPos), 0, size, QLatin1Char('\n')); + } +#else +// qDebug() << size << ret << c->lineNumbers << chunkPos +// << dumpNewLines(chunkData(c, chunkPos), 0, c->size()); + static const int lineNumberCacheInterval = TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL; + if (c->lineNumbers.isEmpty()) { + const QString data = chunkData(c, chunkPos); + const int s = data.size(); + c->lineNumbers.fill(0, (s + lineNumberCacheInterval - 1) / lineNumberCacheInterval); +// qDebug() << data.size() << c->lineNumbers.size() << lineNumberCacheInterval; + + for (int i=0; ilineNumbers[i / lineNumberCacheInterval]; +// qDebug() << "found one at" << i << "put it in" << (i / lineNumberCacheInterval) +// << "chunkPos" << chunkPos; + if (i < size) + ++ret; + } + } + } else { + for (int i=0; ilineNumbers.size(); ++i) { + if (i * lineNumberCacheInterval > size) { + break; + } else if (c->lineNumbers.at(i) == 0) { + // nothing in this area + continue; + } else if ((i + 1) * lineNumberCacheInterval > size) { + ret += ::count(chunkData(c, chunkPos), i * lineNumberCacheInterval, + size - i * lineNumberCacheInterval, QChar('\n')); + // partly + break; + } else { + ret += c->lineNumbers.at(i); + } + } + } +#endif + return ret; +} + +void TextDocumentPrivate::swapOutChunk(Chunk *c) +{ + if (!c->swap.isEmpty()) + return; + Q_ASSERT(!c->data.isEmpty()); + c->from = 0; + c->length = c->data.size(); + c->swap = q->swapFileName(c); + QFile file(c->swap); + if (!file.open(QIODevice::WriteOnly)) { + qWarning("TextDocumentPrivate::chunkData() Can't open file for writing '%s'", qPrintable(c->swap)); + c->swap.clear(); + return; + } + QTextStream ts(&file); + if (textCodec) + ts.setCodec(textCodec); + ts << c->data; + c->data.clear(); +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + // ### do I want to do this? + if (cachedChunk == c) { + cachedChunk = 0; + cachedChunkPos = -1; + cachedChunkData.clear(); + } +#endif +} + +static inline bool match(int pos, int left, int size) +{ + return pos >= left && pos < left + size; +} + +static inline bool match(int pos, int size, const TextPagerSection *section, TextPagerSection::TextSectionOptions flags) +{ + const int sectionPos = section->position(); + const int sectionSize = section->size(); + + if (::match(sectionPos, pos, size) && ::match(sectionPos + sectionSize - 1, pos, size)) { + return true; + } else if (flags & TextPagerSection::IncludePartial) { + const int boundaries[] = { pos, pos + size - 1 }; + for (int i=0; i<2; ++i) { + if (::match(boundaries[i], sectionPos, sectionSize)) + return true; + } + } + return false; +} + +static inline void filter(QList §ions, const TextPagerEdit *filter) +{ + if (filter) { + for (int i=sections.size() - 1; i>=0; --i) { + if (!::matchSection(sections.at(i), filter)) + sections.removeAt(i); + } + } +} + +QList TextDocumentPrivate::getSections(int pos, int size, TextPagerSection::TextSectionOptions flags, const TextPagerEdit *filter) const +{ + if (size == -1) + size = documentSize - pos; + QList ret; + if (pos == 0 && size == documentSize) { + ret = sections; + ::filter(ret, filter); + return ret; + } + // binary search. TextPagerSections are sorted in order of position + if (sections.isEmpty()) { + return ret; + } + + const TextPagerSection tmp(pos, size, static_cast(0), QTextCharFormat(), QVariant()); + QList::const_iterator it = qLowerBound(sections.begin(), sections.end(), &tmp, compareTextSection); + if (flags & TextPagerSection::IncludePartial && it != sections.begin()) { + QList::const_iterator prev = it; + do { + if (::match(pos, size, *--prev, flags)) + ret.append(*prev); + } while (prev != sections.begin()); + } + while (it != sections.end()) { + if (::match(pos, size, *it, flags)) { + ret.append(*it); + } else { + break; + } + ++it; + } + ::filter(ret, filter); + return ret; +} + +void TextDocumentPrivate::textEditDestroyed(TextPagerEdit *edit) +{ + QMutableListIterator i(sections); + while (i.hasNext()) { + TextPagerSection *section = i.next(); + if (section->textEdit() == edit) { + section->d.document = 0; + // Make sure we also remove it from the list of sections so it + // isn't deleted in the TextDocument destructor too. + i.remove(); + delete section; + } + } +} + +void TextPagerDocument::lockForRead() +{ + Q_ASSERT(d->readWriteLock); + d->readWriteLock->lockForRead(); +} + +void TextPagerDocument::lockForWrite() +{ + Q_ASSERT(d->readWriteLock); + d->readWriteLock->lockForWrite(); +} + +bool TextPagerDocument::tryLockForRead() +{ + Q_ASSERT(d->readWriteLock); + return d->readWriteLock->tryLockForRead(); +} + +bool TextPagerDocument::tryLockForWrite() +{ + Q_ASSERT(d->readWriteLock); + return d->readWriteLock->tryLockForWrite(); +} + +void TextPagerDocument::unlock() +{ + Q_ASSERT(d->readWriteLock); + d->readWriteLock->unlock(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,176 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERDOCUMENT_HPP__ +#define TEXTPAGERDOCUMENT_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TextPagerCursor.hpp" +#include "TextPagerSection.hpp" + +class Chunk; +class TextDocumentPrivate; +class TextPagerDocument : public QObject +{ + Q_OBJECT + Q_PROPERTY(int documentSize READ documentSize) + Q_PROPERTY(int chunkCount READ chunkCount) + Q_PROPERTY(int instantiatedChunkCount READ instantiatedChunkCount) + Q_PROPERTY(int swappedChunkCount READ swappedChunkCount) + Q_PROPERTY(int chunkSize READ chunkSize WRITE setChunkSize) + Q_ENUMS(DeviceMode) + Q_FLAGS(Options) + Q_FLAGS(FindMode) + +public: + TextPagerDocument(QObject *parent = 0); + ~TextPagerDocument(); + + enum DeviceMode { + Sparse, + LoadAll + }; + + enum Option { + NoOptions = 0x0000, + SwapChunks = 0x0001, + KeepTemporaryFiles = 0x0002, + ConvertCarriageReturns = 0x0004, // incompatible with Sparse and must be set before loading + AutoDetectCarriageReturns = 0x0010, + NoImplicitLoadAll = 0x0020, + Locking = 0x0040, + DefaultOptions = AutoDetectCarriageReturns + }; + Q_DECLARE_FLAGS(Options, Option); + + Options options() const; + void setOptions(Options opt); + inline void setOption(Option opt, bool on = true) { setOptions(on ? (options() | opt) : (options() &= ~opt)); } + + inline bool load(QIODevice *device, DeviceMode mode, const QByteArray &codecName) + { return load(device, mode, QTextCodec::codecForName(codecName)); } + inline bool load(const QString &fileName, DeviceMode mode, const QByteArray &codecName) + { return load(fileName, mode, QTextCodec::codecForName(codecName)); } + bool load(QIODevice *device, DeviceMode mode = Sparse, QTextCodec *codec = 0); + bool load(const QString &fileName, DeviceMode mode = Sparse, QTextCodec *codec = 0); + + void clear(); + DeviceMode deviceMode() const; + + QTextCodec *textCodec() const; + + void setText(const QString &text); + QString read(int pos, int size) const; + QStringRef readRef(int pos, int size) const; + QChar readCharacter(int index) const; + + int documentSize() const; + int chunkCount() const; + int instantiatedChunkCount() const; + int swappedChunkCount() const; + + void lockForRead(); + void lockForWrite(); + bool tryLockForRead(); + bool tryLockForWrite(); + void unlock(); + + enum FindModeFlag { + FindNone = 0x00000, + FindBackward = 0x00001, + FindCaseSensitively = 0x00002, + FindWholeWords = 0x00004, + FindAllowInterrupt = 0x00008, + FindWrap = 0x00010, + FindAll = 0x00020 + }; + Q_DECLARE_FLAGS(FindMode, FindModeFlag); + + int chunkSize() const; + void setChunkSize(int pos); + + QIODevice *device() const; + + TextPagerCursor find(const QRegExp &rx, const TextPagerCursor &cursor, FindMode flags = 0,int limit = -1) const; + TextPagerCursor find(const QString &ba, const TextPagerCursor &cursor, FindMode flags = 0, int limit = -1) const; + TextPagerCursor find(const QChar &ch, const TextPagerCursor &cursor, FindMode flags = 0) const; + TextPagerCursor findLine(int lineNum, const TextPagerCursor &cursor) const; + + inline TextPagerCursor find(const QRegExp &rx, int pos = 0, FindMode flags = 0) const + { return find(rx, TextPagerCursor(this, pos), flags); } + inline TextPagerCursor find(const QString &ba, int pos = 0, FindMode flags = 0) const + { return find(ba, TextPagerCursor(this, pos), flags); } + inline TextPagerCursor find(const QChar &ch, int pos = 0, FindMode flags = 0) const + { return find(ch, TextPagerCursor(this, pos), flags); } + + + QList sections(int from = 0, int size = -1, TextPagerSection::TextSectionOptions opt = 0) const; + inline TextPagerSection *sectionAt(int pos) const { return sections(pos, 1, TextPagerSection::IncludePartial).value(0); } + TextPagerSection *insertTextSection(int pos, int size, const QTextCharFormat &format = QTextCharFormat(), + const QVariant &data = QVariant()); + void insertTextSection(TextPagerSection *section); + void takeTextSection(TextPagerSection *section); + int currentMemoryUsage() const; + + bool isModified() const; + + int lineNumber(int position) const; + int columnNumber(int position) const; + int lineNumber(const TextPagerCursor &cursor) const; + int columnNumber(const TextPagerCursor &cursor) const; + virtual bool isWordCharacter(const QChar &ch, int index) const; + +public Q_SLOTS: + bool abortSave(); + bool abortFind() const; + +Q_SIGNALS: + void entryFound(const TextPagerCursor &cursor) const; + void textChanged(); + void sectionAdded(TextPagerSection *section); + void sectionRemoved(TextPagerSection *removed); + void charactersAdded(int from, int count); + void charactersRemoved(int from, int count); + void saveProgress(qreal progress); + void findProgress(qreal progress, int position) const; + void documentSizeChanged(int size); + void modificationChanged(bool modified); + +protected: + virtual QString swapFileName(Chunk *chunk); + +private: + TextDocumentPrivate *d; + friend class TextPagerEdit; + friend class TextPagerCursor; + friend class TextDocumentPrivate; + friend class TextPagerLayout; + friend class TextPagerSection; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerDocument::FindMode); +Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerDocument::Options); + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument_p.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument_p.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerDocument_p.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerDocument_p.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,703 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERDOCUMENT_P_HPP__ +#define TEXTPAGERDOCUMENT_P_HPP__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef ASSUME +#ifdef FATAL_ASSUMES +#define ASSUME(cond) Q_ASSERT(cond) +#elif defined Q_OS_SOLARIS +#define ASSUME(cond) if (!(cond)) qWarning("Failed assumption %s:%d %s", __FILE__, __LINE__, #cond); +#else +#define ASSUME(cond) if (!(cond)) qWarning("Failed assumption %s:%d (%s) %s", __FILE__, __LINE__, __FUNCTION__, #cond); +#endif +#endif + +#define Q_COMPARE_ASSERT(left, right) if (left != right) { qWarning() << left << right; Q_ASSERT(left == right); } + +#include "TextPagerDocument.hpp" +#ifdef NO_TEXTDOCUMENT_CACHE +#define NO_TEXTDOCUMENT_CHUNK_CACHE +#define NO_TEXTDOCUMENT_READ_CACHE +#endif + +#if defined TEXTDOCUMENT_LINENUMBER_CACHE && !defined TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL +#define TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL 100 +#endif + +#ifdef QT_DEBUG +#define _UI_TEXTPAGER_ITERATOR_DEBUG +#endif + +static inline bool matchSection(const TextPagerSection *section, const TextPagerEdit *textEdit) +{ + if (!textEdit) { + return true; + } else if (!section->textEdit()) { + return true; + } else { + return textEdit == section->textEdit(); + } +} + + +struct Chunk { + Chunk() : previous(0), next(0), from(-1), length(0), firstLineIndex(-1) +#ifndef TEXTDOCUMENT_LINENUMBER_CACHE + , lines(-1) +#endif + {} + + mutable QString data; + Chunk *previous, *next; + int size() const { return data.isEmpty() ? length : data.size(); } +#ifdef QT_DEBUG + int pos() const { int p = 0; Chunk *c = previous; while (c) { p += c->size(); c = c->previous; }; return p; } +#endif + mutable int from, length; // Not used when all is loaded + mutable int firstLineIndex; +#ifdef TEXTDOCUMENT_LINENUMBER_CACHE + mutable QVector lineNumbers; + // format is how many endlines in the area from (n * + // TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL) to + // ((n + 1) * TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL) +#else + mutable int lines; +#endif + QString swap; +}; + + +// should really use this stuff for all of this stuff + +static inline QPair intersection(int index1, int size1, int index2, int size2) +{ + QPair ret; + ret.first = qMax(index1, index2); + const int right = qMin(index1 + size1, index2 + size2); + ret.second = right - ret.first; + if (ret.second <= 0) + return qMakePair(-1, 0); + return ret; +} + +static inline bool compareTextSection(const TextPagerSection *left, const TextPagerSection *right) +{ + // don't make this compare document. Look at ::sections() + return left->position() < right->position(); +} + +class TextDocumentIterator; +struct DocumentCommand { + enum Type { + None, + Inserted, + Removed + }; + + DocumentCommand(Type t, int pos = -1, const QString &string = QString()) + : type(t), position(pos), text(string), joinStatus(NoJoin) + {} + + const Type type; + int position; + QString text; + + enum JoinStatus { + NoJoin, + Forward, + Backward + } joinStatus; +}; + +struct TextPagerSection; +struct TextCursorSharedPrivate; +struct TextDocumentPrivate : public QObject +{ + Q_OBJECT +public: + TextDocumentPrivate(TextPagerDocument *doc) + : q(doc), first(0), last(0), +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + cachedChunk(0), cachedChunkPos(-1), +#endif +#ifndef NO_TEXTDOCUMENT_READ_CACHE + cachePos(-1), +#endif + documentSize(0), + saveState(NotSaving), findState(NotFinding), ownDevice(false), modified(false), + deviceMode(TextPagerDocument::Sparse), chunkSize(64*1024), //chunkSize(1024*64), //chunkSize(16384), + //undoRedoStackCurrent(0), modifiedIndex(-1), undoRedoEnabled(true), ignoreUndoRedo(false), + //collapseInsertUndo(false), + hasChunksWithLineNumbers(false), textCodec(0), options(TextPagerDocument::DefaultOptions), + readWriteLock(0), cursorCommand(false) + { + first = last = new Chunk; + } + + TextPagerDocument *q; + QSet textCursors; + mutable Chunk *first, *last; + +#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE + mutable Chunk *cachedChunk; + mutable int cachedChunkPos; + mutable QString cachedChunkData; // last uninstantiated chunk's read from file +#endif +#ifndef NO_TEXTDOCUMENT_READ_CACHE + mutable int cachePos; + mutable QString cache; // results of last read(). Could span chunks +#endif + + int documentSize; + enum SaveState { NotSaving, Saving, AbortSave } saveState; + enum FindState { NotFinding, Finding, AbortFind } mutable findState; + QList sections; + QPointer device; + bool ownDevice, modified; + TextPagerDocument::DeviceMode deviceMode; + int chunkSize; + +#if 0 + QList undoRedoStack; + int undoRedoStackCurrent, modifiedIndex; + bool undoRedoEnabled, ignoreUndoRedo, collapseInsertUndo; +#endif + + bool hasChunksWithLineNumbers; + QTextCodec *textCodec; + TextPagerDocument::Options options; + QReadWriteLock *readWriteLock; + bool cursorCommand; + +#ifdef QT_DEBUG + mutable QSet iterators; +#endif + +#if 0 + void joinLastTwoCommands(); +#endif + + void removeChunk(Chunk *c); + QString chunkData(const Chunk *chunk, int pos) const; + int chunkIndex(const Chunk *c) const; + + // evil API. pos < 0 means don't cache + + void updateChunkLineNumbers(Chunk *c, int pos) const; + int countNewLines(Chunk *c, int chunkPos, int index) const; + + void instantiateChunk(Chunk *chunk); + Chunk *chunkAt(int pos, int *offset) const; + +#if 0 + void clearRedo(); + void undoRedo(bool undo); +#endif + + QString wordAt(int position, int *start = 0) const; + QString paragraphAt(int position, int *start = 0) const; + + uint wordBoundariesAt(int pos) const; + + friend class TextPagerDocument; + void swapOutChunk(Chunk *c); + QList getSections(int from, int size, TextPagerSection::TextSectionOptions opt, const TextPagerEdit *filter) const; + inline TextPagerSection *sectionAt(int pos, const TextPagerEdit *filter) const { return getSections(pos, 1, TextPagerSection::IncludePartial, filter).value(0); } + void textEditDestroyed(TextPagerEdit *edit); +Q_SIGNALS: + void sectionFormatChanged(TextPagerSection *section); + void sectionCursorChanged(TextPagerSection *section); + +#if 0 + void undoRedoCommandInserted(DocumentCommand *cmd); + void undoRedoCommandRemoved(DocumentCommand *cmd); + void undoRedoCommandTriggered(DocumentCommand *cmd, bool undo); + void undoRedoCommandFinished(DocumentCommand *cmd); +#endif + +private: + friend class TextPagerSection; +}; + +// should not be kept as a member. Invalidated on inserts/removes +class TextDocumentIterator +{ +public: + TextDocumentIterator(const TextDocumentPrivate *d, int p) + : doc(d), pos(p), min(0), max(-1), convert(false), newline('\n') + { + Q_ASSERT(doc); + + end_=end(); + +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + chunk = doc->chunkAt(p, &offset); + Q_ASSERT(chunk); + const int chunkPos = p - offset; + chunkData = doc->chunkData(chunk, chunkPos); + //chunkLines = chunkData.split('\n'); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); +#ifdef QT_DEBUG + if (p != d->documentSize) { + if (doc->q->readCharacter(p) != chunkData.at(offset)) { + qDebug() << "got" << chunkData.at(offset) << "at" << offset << "in" << chunk << "of size" << chunkData.size() + << "expected" << doc->q->readCharacter(p) << "at document pos" << pos; + } + Q_ASSERT(chunkData.at(offset) == doc->q->readCharacter(p)); + } else { + Q_ASSERT(chunkData.size() == offset); + } +#endif +#endif + +#ifdef QT_DEBUG + doc->iterators.insert(this); +#endif + } +#ifdef QT_DEBUG + ~TextDocumentIterator() + { + Q_ASSERT(doc->iterators.remove(this)); + } +#endif + + int end() const + { + return max != -1 ? max : doc->documentSize; + } + + void setMinBoundary(int bound) + { + min = bound; + Q_ASSERT(pos >= min); + } + + void setMaxBoundary(int bound) + { + max = bound; + Q_ASSERT(pos <= max); + + //We suppose that the document size does not change + end_=end(); + } + + + inline bool hasNext() const + { + return pos < end(); + } + + inline bool hasPrevious() const + { + return pos > min; + } + + inline int position() const + { + return pos; + } + + inline QChar current() const + { + Q_ASSERT(doc); +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(chunk); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); + if (pos == end()) + return QChar(); + +//Calling readCharacter is very expensive during find!!! So we try not to call it!!! +#if 0 +#ifdef QT_DEBUG + if (doc->q->readCharacter(pos) != chunkData.at(offset)) { + qDebug() << "got" << chunkData.at(offset) << "at" << offset << "in" << chunk << "of size" << chunkData.size() + << "expected" << doc->q->readCharacter(pos) << "at document pos" << pos; + } +#endif +#endif +#if 0 + ASSUME(doc->q->readCharacter(pos) == chunkData.at(offset)); +#endif + return convert ? chunkData.at(offset).toLower() : chunkData.at(offset); + //return convert ? chunkData[offset].toLower() : chunkData[offset]; +#else + return convert ? doc->q->readCharacter(pos).toLower() : doc->q->readCharacter(pos); +#endif + } + + inline QChar next() + { + Q_ASSERT(doc); + ++pos; +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(chunk); + if (++offset >= chunkData.size() && chunk->next) { // special case for offset == chunkData.size() and !chunk->next + offset = 0; + chunk = chunk->next; + Q_ASSERT(chunk); + chunkData = doc->chunkData(chunk, pos); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); + } +#endif + return current(); + } + + inline QChar previous() + { + Q_ASSERT(doc); + Q_ASSERT(hasPrevious()); + --pos; + Q_ASSERT(pos >= min); + Q_ASSERT(pos <= end()); +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(chunk); + if (--offset < 0) { + chunk = chunk->previous; + Q_ASSERT(chunk); + chunkData = doc->chunkData(chunk, pos - chunk->size() + 1); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); + offset = chunkData.size() - 1; + } +#endif + return current(); + } + + enum Direction { None = 0x0, Left = 0x1, Right = 0x2, Both = Left|Right }; + inline QChar nextPrev(Direction dir, bool &ok) + { + if (dir == Left) { + if (!hasPrevious()) { + ok = false; + return QChar(); + } + ok = true; + return previous(); + } else { + if (!hasNext()) { + ok = false; + return QChar(); + } + ok = true; + return next(); + } + } + +// Gets the previous line preceding the current iterator position. It is only +// used for TextPagerDocument::find and was added to speed up the original find +// algorithm. it is only implemented for cached textdocument iterators! + + inline unsigned char prevLine(QString& str) + { +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(doc); + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << "prevLine --->" << pos << offset; //<< chunkData; +#endif + + //If we are at the start + if(pos <= min) + return 0; + + //Get the QString in the chunk as a const pointer. It is faster to iterate through it than + //calling QString::at() + const QChar *data = chunkData.constData(); + data+=offset; + + Q_ASSERT(*data == chunkData.at(offset)); + + //The current character is probably a newline (the + //result of the previous call) so we need to take a step back + if(*data == newline) + { + --pos; + --offset; + + //See if we need the previous chunk + if(offset < 0) { + //The previous chunk must exist + bool b=loadPrevChunk(); + Q_ASSERT( b == true); + data = chunkData.constData(); + data+=offset; + } else { + --data; + } + + if(pos == min) + return 0; + } + + //Mark the line end position within the chunk + int to=offset; + + //We will go backwards until we find a newline + while(*data != newline) + { + //Q_ASSERT(*data == chunkData.at(offset)); +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << pos << offset << chunkData.at(offset) << to; +#endif + if(pos <= min) { + if(to != offset) { + if(str.size() == 0) { + str=chunkData.mid(offset,to-offset+1); + } else { + str.prepend(chunkData.mid(offset,to-offset+1)); + } + } + return 0; + } + + --pos; + + //If we need the previous chunk + if(--offset < 0) { + + //Copy the line portion from this chunk to the result + if(str.size() == 0) + str=chunkData.mid(0,to); + else + str.prepend(chunkData.mid(0,to)); + + //Get previous chunk + bool b=loadPrevChunk(); + Q_ASSERT(b==true); + + //Initialise the search positions + data = chunkData.constData(); + data+=offset; + to=offset; + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << "change" << pos << offset << *data << chunkData.at(offset); +#endif + } else { + --data; + } + } + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << pos << offset << to; +#endif + + //offset is either a newline charter or 0 (the start of the document) + + if(to != offset) { + if(str.size() == 0) { + str=chunkData.mid(offset,to-offset+1); + } else { + str.prepend(chunkData.mid(offset,to-offset+1)); + } + } +#endif + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << "line:" << str; +#endif + + return 1; + } + + // Gets the next line following- the current iterator position. It is only + // used for TextPagerDocument::find and was added to speed up the original find + // algorithm. it is only implemented for cached textdocument iterators! + + inline unsigned char nextLine(QString& str) + { +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(doc); + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << "nextLine --->" << pos << offset << chunkData.size() << chunkData.at(offset); +#endif + int posEnd=end(); + if(pos >= posEnd) + return 0; + + //Get the QString in the chunk as a const pointer. It is faster to iterate through it than + //calling QString::at() + const QChar *data = chunkData.constData(); + data+=offset; + + Q_ASSERT(*data == chunkData.at(offset)); + + //The current character is probably a newline (the + //result of the previous call) so we need to take a step forward + if(*data == newline) + { + ++pos; + ++offset; + + //See if we need the next chunk + if(offset >= chunkData.size()) { + //Has next chunk + if(loadNextChunk()) { + data = chunkData.constData(); + Q_ASSERT(offset == 0); + } else { + pos--; + offset--; + return 0; + } + + } else { + ++data; + } + + if(pos >= posEnd) + return 0; + + } + + //Mark the line start position within the chunk + int from=offset; + + //We will go forward until we find a newline + while(*data != newline) + { +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << pos << offset << chunkData.at(offset); +#endif + if(pos >= posEnd) + { + if(str.size() == 0) + str=chunkData.mid(from,offset-from+1); + else + str.append(chunkData.mid(from,offset-from+1)); + + return 0; + } + + ++pos; + + //If we need the next chunk + if(++offset >= chunkData.size()) { + + //Copy the line portion from this chunk to the result + if(str.size() == 0) + str=chunkData.mid(from,offset-from); + else + str.append(chunkData.mid(from,offset-from)); + + //Get next chunk + if(loadNextChunk()) { + //Initialise the search positions + data = chunkData.constData(); + from=0; + } else { + pos--; + offset--; + return 0; + } + } + else { + ++data; + } + } + + if(from != offset) { + if(str.size() == 0) + { + str=chunkData.mid(from,offset-from+1); + } + else + str.append(chunkData.mid(from,offset-from+1)); + } + +#endif + +#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG + //qDebug() << "line:" << str; +#endif + + return 1; + } + + + inline bool loadPrevChunk() + { +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(chunk); + if (chunk->previous) { + chunk = chunk->previous; + Q_ASSERT(chunk); + chunkData = doc->chunkData(chunk, pos - chunk->size() + 1); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); + offset = chunkData.size() - 1; + return true; + } +#endif + return false; + } + + inline bool loadNextChunk() + { +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + Q_ASSERT(chunk); + if (chunk->next) { + chunk = chunk->next; + Q_ASSERT(chunk); + chunkData = doc->chunkData(chunk, pos); + Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); + offset = 0; + return true; + } +#endif + return false; + } + + + inline bool convertToLowerCase() const + { + return convert; + } + + inline void setConvertToLowerCase(bool on) + { + convert = on; + } + +private: + const TextDocumentPrivate *doc; + int pos; + int min, max; + int end_; // to speed things up +#ifndef NO_TEXTDOCUMENTITERATOR_CACHE + int offset; + QString chunkData; + QStringList chunkLines; + Chunk *chunk; +#endif + bool convert; + const QChar newline; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1938 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include +#include +#include +#include "TextPagerEdit.hpp" +#include "TextPagerEdit_p.hpp" +#include "TextPagerCursor_p.hpp" +#include "TextPagerDocument_p.hpp" +#include "TextPagerSearchHighlighter.hpp" +#include "UiLog.hpp" +#include "VConfig.hpp" + +#include + +//#define _UI_TEXTPAGER_DEBUG +//#define DEBUG_TEXTPAGER_LASTPAGESIZE +//#define DEBUG_TEXTPAGER + +#ifdef DEBUG_TEXTPAGER +bool doLog = false; +QString logFileName; +#endif + +/*! + Constructs an empty TextPagerEdit with parent \a parent. +*/ + +TextPagerEdit::TextPagerEdit(QWidget *parent) : + QAbstractScrollArea(parent), + d(new TextEditPrivate(this)), + useSearchHighlight_(false), + showLineNum_(false), + lineNumArea_(0), + fontProp_(NULL) +{ + viewport()->setCursor(Qt::IBeamCursor); + + setProperty("pager","1"); + +#ifdef DEBUG_TEXTPAGER + if (logFileName.isEmpty()) + logFileName = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz.log"); +#endif + + connect(verticalScrollBar(), SIGNAL(valueChanged(int)), d, SLOT(onScrollBarValueChanged(int))); + connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), d, SLOT(onScrollBarActionTriggered(int))); + connect(verticalScrollBar(), SIGNAL(sliderReleased()), d, SLOT(updateScrollBar())); + + setDocument(new TextPagerDocument(this)); + setViewportMargins(0, 0, 0, 0); + struct { + QString text; + const char *member; + QKeySequence::StandardKey key; + } shortcuts[] = { + { tr("Copy"), SLOT(copy()), QKeySequence::Copy }, + { tr("Select All"), SLOT(selectAll()), QKeySequence::SelectAll }, + { QString(), 0, QKeySequence::UnknownKey } }; + for (int i=0; shortcuts[i].member; ++i) { + d->actions[i] = new QAction(shortcuts[i].text, this); + d->actions[i]->setShortcutContext(Qt::WidgetShortcut); + d->actions[i]->setShortcut(QKeySequence(shortcuts[i].key)); + d->actions[i]->setShortcutContext(Qt::WidgetShortcut); + connect(d->actions[i], SIGNAL(triggered(bool)), this, shortcuts[i].member); + addAction(d->actions[i]); + } + //connect(this, SIGNAL(undoAvailableChanged(bool)), d->actions[UndoAction], SLOT(setEnabled(bool))); + //connect(this, SIGNAL(redoAvailableChanged(bool)), d->actions[RedoAction], SLOT(setEnabled(bool))); + //d->actions[UndoAction]->setEnabled(false); + //d->actions[RedoAction]->setEnabled(false); + setContextMenuPolicy(Qt::ActionsContextMenu); + //setCursorVisible(true); // starts blinking + connect(this, SIGNAL(selectionChanged()), d, SLOT(onSelectionChanged())); + + + setReadOnly(true); + + searchHighlight_=new TextPagerSearchHighlighter(this); +} + + +/*! + returns the current cursor position +*/ + +int TextPagerEdit::cursorPosition() const +{ + return d->textCursor.position(); +} + +/*! + ensures that \a cursor is visible with \a linesMargin lines of + margin on each side (if possible) +*/ + +void TextPagerEdit::ensureCursorVisible(const TextPagerCursor &cursor, int linesMargin) +{ + linesMargin = qMin(d->visibleLines, linesMargin); // ### could this be a problem down at the bottom of the document? + Q_ASSERT(cursor.document() == document()); + TextPagerCursor above = cursor; + for (int i=0; iviewportPosition) { + d->updateViewportPosition(above.position(), TextPagerLayout::Backward); + return; + } + + TextPagerCursor below = cursor; + for (int i=0; i d->lastVisibleCharacter) { + for (int i=0; ivisibleLines; ++i) { + below.movePosition(TextPagerCursor::Up); + d->updateViewportPosition(below.position(), TextPagerLayout::Forward); + } + } +} + +/*! + returns the QAction * for \a type. +*/ + +QAction *TextPagerEdit::action(ActionType type) const +{ + return d->actions[type]; +} + +/*! + Called when the textEdit is deleted +*/ + +TextPagerEdit::~TextPagerEdit() +{ + if (d->document) { + Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, d->syntaxHighlighters) { + if (syntaxHighlighter->parent() == this) + disconnect(syntaxHighlighter, 0, d, 0); + syntaxHighlighter->d->textEdit = 0; + syntaxHighlighter->d->textLayout = 0; + } + disconnect(d->document, 0, this, 0); + disconnect(d->document, 0, d, 0); + disconnect(d->document->d, 0, this, 0); + disconnect(d->document->d, 0, d, 0); + + if (d->document->parent() == this) { + delete d->document; + d->document = 0; + } else { + d->document->d->textEditDestroyed(this); + } + // to make sure we don't do anything drastic on shutdown + } + delete d; + + if(fontProp_) + fontProp_->removeObserver(this); +} + +/*! + returns the text edit's document. document() always returns a + valid TextPagerDocument * + \sa setDocument +*/ + + +TextPagerDocument *TextPagerEdit::document() const +{ + return d->document; +} + +/*! + sets the text edit's document to \a doc. The previous document + will be deleted if it's parented to the text edit. + + If \a doc is 0 a default TextPagerDocument will be set. + \sa document +*/ + +void TextPagerEdit::setDocument(TextPagerDocument *doc) +{ + if (doc == d->document) + return; + + if (d->document) { + disconnect(d->document, 0, this, 0); + disconnect(d->document, 0, d, 0); + disconnect(d->document->d, 0, this, 0); + disconnect(d->document->d, 0, d, 0); + if (d->document->parent() == this) + delete d->document; + } + if (!doc) + doc = new TextPagerDocument(this); + + d->sections.clear(); + d->buffer.clear(); + d->sectionsDirty = true; + d->document = doc; + d->sectionPressed = 0; + d->layoutDirty = true; + qDeleteAll(d->unusedTextLayouts); + d->unusedTextLayouts.clear(); + qDeleteAll(d->textLayouts); + d->textLayouts.clear(); + viewport()->setCursor(Qt::IBeamCursor); + viewport()->setMouseTracking(true); + d->sectionCount = 0; + + d->textCursor = TextPagerCursor(doc); + d->textCursor.textEdit = this; + + /*connect(d->document->d, SIGNAL(undoRedoCommandInserted(DocumentCommand *)), + d, SLOT(onDocumentCommandInserted(DocumentCommand *))); + connect(d->document, SIGNAL(sectionAdded(TextPagerSection *)), + d, SLOT(onTextSectionAdded(TextPagerSection *))); + connect(d->document, SIGNAL(sectionRemoved(TextPagerSection *)), + d, SLOT(onTextSectionRemoved(TextPagerSection *))); + connect(d->document->d, SIGNAL(undoRedoCommandRemoved(DocumentCommand *)), + d, SLOT(onDocumentCommandRemoved(DocumentCommand *))); + connect(d->document->d, SIGNAL(undoRedoCommandTriggered(DocumentCommand *, bool)), + d, SLOT(onDocumentCommandTriggered(DocumentCommand *, bool)));*/ + + connect(d->document, SIGNAL(charactersAdded(int, int)), + d, SLOT(onCharactersAddedOrRemoved(int, int))); + connect(d->document, SIGNAL(charactersRemoved(int, int)), + d, SLOT(onCharactersAddedOrRemoved(int, int))); + + /*connect(d->document, SIGNAL(textChanged()), this, SIGNAL(textChanged())); + connect(d->document, SIGNAL(undoAvailableChanged(bool)), + this, SIGNAL(undoAvailableChanged(bool))); + connect(d->document, SIGNAL(redoAvailableChanged(bool)), + this, SIGNAL(redoAvailableChanged(bool)));*/ + + connect(d->document, SIGNAL(documentSizeChanged(int)), d, SLOT(onDocumentSizeChanged(int))); + connect(d->document, SIGNAL(destroyed(QObject*)), d, SLOT(onDocumentDestroyed())); + connect(d->document->d, SIGNAL(sectionFormatChanged(TextPagerSection *)), + d, SLOT(onTextSectionFormatChanged(TextPagerSection *))); + connect(d->document->d, SIGNAL(sectionCursorChanged(TextPagerSection *)), + d, SLOT(onTextSectionCursorChanged(TextPagerSection *))); + + d->onDocumentSizeChanged(d->document->documentSize()); + + viewport()->update(); +} + +/*! + returns the textCursor's width (in pixels) + \sa setCursorWidth +*/ + +int TextPagerEdit::cursorWidth() const +{ + return d->cursorWidth; +} + +/*! + sets the cursorWidth of the text edit to \a cw pixels. + \a cw must be a valid integer larger than 0. + + \sa setCursorVisible +*/ + +void TextPagerEdit::setCursorWidth(int cw) +{ + Q_ASSERT(d->cursorWidth > 0); + d->cursorWidth = cw; + viewport()->update(); +} + +/*! + Loads the contenst of \a dev into the text edit's document. + Equivalent to calling document()->load(\a dev, \a mode); + + \sa setCursorVisible +*/ + +#if 0 +bool TextPagerEdit::load(QIODevice *dev, TextPagerDocument::DeviceMode mode, QTextCodec *codec) +{ +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile f(logFileName); + f.open(QIODevice::WriteOnly); + QDataStream ds(&f); + if (QFile *ff = qobject_cast(dev)) { + ds << ff->fileName(); + } else { + ds << QString::fromLatin1(dev->metaObject()->className()); + } + } +#endif + //we have to check to font here because the initial setting in setFontProperty doe not have any effect + updateFont(); + return d->document->load(dev, mode, codec); +} +#endif + +bool TextPagerEdit::load(const QString &file, TextPagerDocument::DeviceMode mode, QTextCodec *codec) +{ +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile f(logFileName); + f.open(QIODevice::WriteOnly); + QDataStream ds(&f); + ds << file; + } +#endif + + UiLog().dbg() << "TextPagerEdit::load fileName" << file; + //we have to check to font here because the initial setting in setFontProperty doe not have any effect + updateFont(); + bool ret=d->document->load(file, mode, codec); + UiLog().dbg() << " cursor: " << textCursor().position(); + return ret; +} + +void TextPagerEdit::setText(const QString &txt) +{ + //we have to check to font here because the initial setting in setFontProperty doe not have any effect + updateFont(); + d->document->setText(txt); +} + +enum SelectionAddStatus { + Invalid, + Before, + After, + Success +}; + + + + + +static inline SelectionAddStatus addSelection(int layoutStart, int layoutLength, + const TextPagerCursor &cursor, QTextLayout::FormatRange *format) +{ + Q_ASSERT(format); + if (!cursor.hasSelection()) + return Invalid; + if (cursor.selectionEnd() < layoutStart) + return Before; + if (cursor.selectionStart() > layoutStart + layoutLength) + return After; + + format->start = qMax(0, cursor.selectionStart() - layoutStart); + format->length = qMin(layoutLength - format->start, + cursor.selectionEnd() - layoutStart - format->start); + return Success; +} + +void TextPagerEdit::paintEvent(QPaintEvent *e) +{ + d->updateScrollBarPosition(); + d->relayout(); + if (d->updateScrollBarPageStepPending) { + d->updateScrollBarPageStepPending = false; + d->updateScrollBarPageStep(); + } + + QPainter p(viewport()); + + const QRect er = e->rect(); + p.translate(-horizontalScrollBar()->value(), 0); + p.setFont(font()); + QVector selections; + selections.reserve(d->extraSelections.size() + 1); + int textLayoutOffset = d->viewportPosition; + + const QTextLayout *cursorLayout = d->cursorVisible ? d->layoutForPosition(d->textCursor.position()) : 0; + int extraSelectionIndex = 0; + QTextLayout::FormatRange selectionRange; + selectionRange.start = -1; + Q_FOREACH(QTextLayout *l, d->textLayouts) { + const int textSize = l->text().size(); + const QRect r = l->boundingRect().toRect(); + if (r.intersects(er)) { + const QBrush background = d->blockFormats.value(l).background(); + if (background.style() != Qt::NoBrush) { + p.fillRect(r, background); + } + if (::addSelection(textLayoutOffset, textSize, d->textCursor, &selectionRange) == Success) { + selectionRange.format.setBackground(palette().highlight()); + selectionRange.format.setForeground(palette().highlightedText()); + } + int lowestIncompleteSelection = -1; + while (extraSelectionIndex < d->extraSelections.size()) { + QTextLayout::FormatRange range; + const SelectionAddStatus s = ::addSelection(textLayoutOffset, textSize, + d->extraSelections.at(extraSelectionIndex). + cursor, &range); + if (s == Success) { + range.format = d->extraSelections.at(extraSelectionIndex).format; + selections.append(range); + + const TextPagerCursor &cursor = d->extraSelections.at(extraSelectionIndex).cursor; + int lastPos = cursor.position() + cursor.selectionSize(); + if (lastPos > textLayoutOffset+textSize && lowestIncompleteSelection < 0) { + lowestIncompleteSelection = extraSelectionIndex; + } + } else if (s == After) { + break; + } + ++extraSelectionIndex; + } + if (lowestIncompleteSelection > -1) { + extraSelectionIndex = lowestIncompleteSelection; + } + + // is this the current line? + /*if(cursorLayout == l) + { + p.fillRect(r, QColor(216,228,239)); // highlight the current line + }*/ + + + if (selectionRange.start != -1) { + // The last range in the vector has priority, that + // should probably be the real selection + selections.append(selectionRange); + selectionRange.start = -1; + } + + l->draw(&p, QPoint(0, 0), selections); + if (!selections.isEmpty()) + selections.clear(); + if (cursorLayout == l) { + cursorLayout->drawCursor(&p, QPoint(0, 0), d->textCursor.position() - textLayoutOffset, + d->cursorWidth); + } + } else if (r.top() > er.bottom()) { + break; + } + textLayoutOffset += l->text().size() + 1; + } + + + //paintLineNumberArea(e); +} + +void TextPagerEdit::scrollContentsBy(int dx, int dy) +{ + Q_UNUSED(dx); + Q_UNUSED(dy); +// viewport()->update(); + viewport()->scroll(dx, dy); // seems to jitter more +} + +int TextPagerEdit::viewportPosition() const +{ + return d->viewportPosition; +} + +void TextPagerEdit::mousePressEvent(QMouseEvent *e) +{ + d->inMouseEvent = true; + if (e->button() == Qt::LeftButton) { +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); + } +#endif + if (d->tripleClickTimer.isActive()) { + d->tripleClickTimer.stop(); + d->textCursor.movePosition(TextPagerCursor::StartOfBlock); + d->textCursor.movePosition(TextPagerCursor::EndOfBlock, TextPagerCursor::KeepAnchor); + } else { + const bool shift = e->modifiers() & Qt::ShiftModifier; + if (!shift) { + clearSelection(); + } + int pos = textPositionAt(e->pos()); + if (pos == -1) + pos = d->document->documentSize() - 1; + d->sectionPressed = d->document->d->sectionAt(pos, this); + setCursorPosition(pos, shift ? TextPagerCursor::KeepAnchor : TextPagerCursor::MoveAnchor); + } + + e->accept(); + + //The cursor changed so wee need to update the selected line number + if(lineNumArea_ && showLineNum_) + lineNumArea_->update(); + } + else { + QAbstractScrollArea::mousePressEvent(e); + } +} + +void TextPagerEdit::mouseDoubleClickEvent(QMouseEvent *e) +{ + if (e->button() == Qt::LeftButton) { +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); + } +#endif + const int pos = textPositionAt(e->pos()); + if (pos == d->textCursor.position()) { + d->tripleClickTimer.start(qApp->doubleClickInterval(), d); + if (d->document->isWordCharacter(d->textCursor.cursorCharacter(), + d->textCursor.position())) { + // ### this is not quite right + d->textCursor.movePosition(TextPagerCursor::StartOfWord); + d->textCursor.movePosition(TextPagerCursor::EndOfWord, TextPagerCursor::KeepAnchor); + return; + } + } + mousePressEvent(e); + } +} + +void TextPagerEdit::mouseMoveEvent(QMouseEvent *e) +{ + if (e->buttons() == Qt::NoButton) { + d->updateCursorPosition(e->pos()); + } else if (e->buttons() == Qt::LeftButton && d->document->documentSize()) { +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); + } +#endif + const QRect r = viewport()->rect(); + d->lastMouseMove = e->pos(); + e->accept(); + if (e->y() < r.top()) { + if (d->atBeginning()) { + d->updateHorizontalPosition(); + d->autoScrollTimer.stop(); + return; + } + } else if (e->y() > r.bottom()) { + if (d->atEnd()) { + d->updateHorizontalPosition(); + d->autoScrollTimer.stop(); + return; + } + } else { + int pos = textPositionAt(QPoint(qBound(0, d->lastMouseMove.x(), r.right()), d->lastMouseMove.y())); + if (pos == -1) + pos = d->document->documentSize(); + d->autoScrollTimer.stop(); + setCursorPosition(pos, TextPagerCursor::KeepAnchor); + + //The cursor changed so wee need to update the selected line number + if(lineNumArea_ && showLineNum_) + lineNumArea_->update(); + + return; + } + const int distance = qMax(r.top() - d->lastMouseMove.y(), d->lastMouseMove.y() - r.bottom()); + Q_ASSERT(distance != 0); + enum { MinimumTimeOut = 3 }; + int timeout = qMax(MinimumTimeOut, 100 - distance); + enum { Margin = 3 }; + if (qApp->desktop()->screenGeometry(this).bottom() - mapToGlobal(d->lastMouseMove).y() <= Margin) { + timeout = MinimumTimeOut; + } + d->autoScrollLines = 1 + ((100 - timeout) / 30); + if (d->autoScrollTimer.isActive()) { + d->pendingTimeOut = timeout; + } else { + d->pendingTimeOut = -1; + d->autoScrollTimer.start(timeout, d); + } + } else { + QAbstractScrollArea::mouseMoveEvent(e); + } +} + +void TextPagerEdit::mouseReleaseEvent(QMouseEvent *e) +{ + d->inMouseEvent = false; + if (e->button() == Qt::LeftButton) { +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); + } +#endif + d->autoScrollTimer.stop(); + d->pendingTimeOut = -1; + e->accept(); + if (d->sectionPressed && sectionAt(e->pos()) == d->sectionPressed) { + Q_EMIT sectionClicked(d->sectionPressed, e->pos()); + } + d->sectionPressed = 0; + } else { + QAbstractScrollArea::mouseReleaseEvent(e); + } +} + +void TextPagerEdit::resizeEvent(QResizeEvent *e) +{ +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->size(); + } +#endif + QAbstractScrollArea::resizeEvent(e); + if(e->oldSize().height() != e->size().height()) + d->adjustVerticalScrollBar(); + d->updateScrollBarPageStepPending = true; + d->layoutDirty = true; +} + +int TextPagerEdit::textPositionAt(const QPoint &pos) const +{ + if (!viewport()->rect().contains(pos)) + return -1; + + QPoint realPos=pos; + realPos+=QPoint(horizontalScrollBar()->value(),0); + + //Adjust the horizontal position + /* if(pos.x() > horizontalScrollBar()->value()+viewport()->rect().width()) + { + int hval=realCrect.left()-r.width()/2; + horizontalScrollBar()->setValue((hval < horizontalScrollBar()->maximum())? + hval: + horizontalScrollBar()->maximum()); + } + else if(realCrect.right() < horizontalScrollBar()->value()) + { + int hval=realCrect.left()-r.width()/2; + horizontalScrollBar()->setValue((hval > horizontalScrollBar()->minimum())? + hval: + horizontalScrollBar()->minimum()); + } +}*/ + + + + + + + return d->textPositionAt(realPos); +} + +bool TextPagerEdit::readOnly() const +{ + return d->readOnly; +} + +void TextPagerEdit::setReadOnly(bool rr) +{ + d->readOnly = rr; + + setCursorVisible(!rr); + + /* //d->actions[PasteAction]->setEnabled(!rr); + //d->actions[CutAction]->setEnabled(!rr); + //d->actions[PasteAction]->setVisible(!rr); + //d->actions[CutAction]->setVisible(!rr); + + const bool redoWasAvailable = isRedoAvailable(); + const bool undoWasAvailable = isUndoAvailable(); + + // d->actions[UndoAction]->setEnabled(!rr); + // d->actions[RedoAction]->setEnabled(!rr); + // d->actions[UndoAction]->setVisible(!rr); + // d->actions[RedoAction]->setVisible(!rr); + + + if (undoWasAvailable != isUndoAvailable()) + Q_EMIT undoAvailableChanged(!undoWasAvailable); + if (redoWasAvailable != isRedoAvailable()) + Q_EMIT redoAvailableChanged(!redoWasAvailable); + */ +} + +bool TextPagerEdit::lineBreaking() const +{ + return d->lineBreaking; +} + +void TextPagerEdit::setLineBreaking(bool lineBreaking) +{ + if (d->lineBreaking != lineBreaking) { + d->lineBreaking = lineBreaking; + d->layoutDirty = true; + viewport()->update(); + } +} + + +int TextPagerEdit::maximumSizeCopy() const +{ + return d->maximumSizeCopy; +} + +void TextPagerEdit::setMaximumSizeCopy(int max) +{ + d->maximumSizeCopy = qMax(0, max); + d->updateCopyAndCutEnabled(); +} + +QRect TextPagerEdit::cursorBlockRect(const TextPagerCursor &textCursor) const +{ + if (const QTextLayout *l = d->layoutForPosition(textCursor.position())) { + return l->boundingRect().toRect(); + } + return QRect(); +} + +QRect TextPagerEdit::cursorRect(const TextPagerCursor &textCursor) const +{ + int offset = -1; + if (d->layoutForPosition(textCursor.position(), &offset)) { + ASSUME(offset != -1); + QTextLine line = d->lineForPosition(textCursor.position()); + + //if line is empty line.cursorToX() crashes. A qt bug? + if(line.isValid()) { + qreal x = line.cursorToX(offset); + return QRect(x, line.y(), d->cursorWidth, line.height()); + } + } + return QRect(); +} + +int TextPagerEdit::lineNumber(int position) const +{ + return d->document->lineNumber(position); +} + +int TextPagerEdit::columnNumber(int position) const +{ + TextPagerCursor cursor(this, position); + return cursor.isNull() ? -1 : cursor.columnNumber(); +} + +int TextPagerEdit::lineNumber(const TextPagerCursor &cursor) const +{ + return cursor.document() == d->document + ? d->document->lineNumber(cursor.position()) : -1; +} + +int TextPagerEdit::columnNumber(const TextPagerCursor &cursor) const +{ + return cursor.document() == d->document + ? cursor.columnNumber() : -1; +} + +void TextPagerEdit::wheelEvent(QWheelEvent *e) +{ + if (e->orientation() == Qt::Vertical) { + d->scrollLines(3 * (e->delta() > 0 ? -1 : 1)); + } else { + QAbstractScrollArea::wheelEvent(e); + } + + if(e->modifiers() & Qt::ControlModifier) + { + const int delta = e->delta(); + if (delta < 0) + zoomOut(); + else if (delta > 0) + zoomIn(); + return; + } +} + +void TextPagerEdit::changeEvent(QEvent *e) +{ + if (e->type() == QEvent::FontChange) { + d->font = font(); + Q_FOREACH(QTextLayout *l, d->textLayouts) { + l->setFont(d->font); + } + Q_FOREACH(QTextLayout *l, d->unusedTextLayouts) { + l->setFont(d->font); + } + + d->adjustVerticalScrollBar(); + + if(lineNumArea_ && showLineNum_) + lineNumArea_->updateWidth(); + + d->layoutDirty = true; + viewport()->update(); + } +} + +void TextPagerEdit::keyPressEvent(QKeyEvent *e) +{ +#ifdef DEBUG_TEXTPAGER + if (doLog) { + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->key() << int(e->modifiers()) << e->text() << e->isAutoRepeat() << e->count(); + } +#endif + + Q_ASSERT(d->textCursor.textEdit == this); + if (d->readOnly) { + d->cursorMoveKeyEventReadOnly(e); + return; + } +} + +void TextPagerEdit::keyReleaseEvent(QKeyEvent *e) +{ +#ifdef DEBUG_TEXTPAGER + if (doLog) { // ### does it make any sense to replay these? Probably not + QFile file(logFileName); + file.open(QIODevice::Append); + QDataStream ds(&file); + ds << int(e->type()) << e->key() << int(e->modifiers()) << e->text() << e->isAutoRepeat() << e->count(); + } +#endif + QAbstractScrollArea::keyReleaseEvent(e); +} + +void TextPagerEdit::setCursorPosition(int pos, TextPagerCursor::MoveMode mode) +{ + d->textCursor.setPosition(pos, mode); +} + +bool TextPagerEdit::moveCursorPosition(TextPagerCursor::MoveOperation op, TextPagerCursor::MoveMode mode, int n) +{ + return d->textCursor.movePosition(op, mode, n); +} + +void TextPagerEdit::copy(QClipboard::Mode mode) +{ + if (d->textCursor.selectionSize() <= d->maximumSizeCopy) { + QApplication::clipboard()->setText(selectedText(), mode); + } +} + +bool TextPagerEdit::cursorVisible() const +{ + return d->cursorBlinkTimer.isActive(); +} + +void TextPagerEdit::setCursorVisible(bool cc) +{ + if (cc == d->cursorBlinkTimer.isActive()) + return; + + d->cursorVisible = cc; + if (cc) { + d->cursorBlinkTimer.start(QApplication::cursorFlashTime(), d); + } else { + d->cursorBlinkTimer.stop(); + } + const QRect r = cursorRect(d->textCursor) & viewport()->rect(); + if (!r.isNull()) { + viewport()->update(r); + } +} + +void TextPagerEdit::clearSelection() +{ + if (!d->textCursor.hasSelection()) + return; + + d->textCursor.clearSelection(); +} + +QString TextPagerEdit::selectedText() const +{ + return d->textCursor.selectedText(); +} + +bool TextPagerEdit::hasSelection() const +{ + return d->textCursor.hasSelection(); +} + +void TextPagerEdit::selectAll() +{ + TextPagerCursor cursor(d->document); + Q_ASSERT(cursor.position() == 0); + cursor.movePosition(TextPagerCursor::End, TextPagerCursor::KeepAnchor); + setTextCursor(cursor); + Q_EMIT selectionChanged(); +} + +QString TextPagerEdit::read(int pos, int size) const +{ + return d->document->read(pos, size); +} + +QChar TextPagerEdit::readCharacter(int index) const +{ + return d->document->readCharacter(index); +} + +void TextEditPrivate::onDocumentSizeChanged(int size) +{ + adjustVerticalScrollBar(); + + /*int s=findLastPageSize(); + + qDebug() << "lat page position" << s; + + if(s != -1) + textEdit->verticalScrollBar()->setRange(0, s); + else + textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); + + + //textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); +// qDebug() << findLastPageSize(); + maxViewportPosition = textEdit->verticalScrollBar()->maximum(); + updateScrollBarPageStepPending = true;*/ +} + + +void TextEditPrivate::adjustVerticalScrollBar() +{ + int s=findLastPageSize(); + + int size=(document != 0)?(document->documentSize()):0; + +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << "last page position" << s; +#endif + if(s == 0) { + textEdit->verticalScrollBar()->hide(); + textEdit->verticalScrollBar()->setEnabled(false); + } else { + textEdit->verticalScrollBar()->setEnabled(true); + textEdit->verticalScrollBar()->show(); + + if(s != -1) + textEdit->verticalScrollBar()->setRange(0, s); + else + textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); + } + + //textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); +// qDebug() << findLastPageSize(); + maxViewportPosition = textEdit->verticalScrollBar()->maximum(); + updateScrollBarPageStepPending = true; +} + +void TextEditPrivate::updateCopyAndCutEnabled() +{ + const bool wasEnabled = actions[TextPagerEdit::CopyAction]->isEnabled(); + const bool enable = qAbs(textCursor.position() - textCursor.anchor()) <= maximumSizeCopy; + actions[TextPagerEdit::CopyAction]->setEnabled(enable); + //actions[TextPagerEdit::CutAction]->setEnabled(enable); + if (wasEnabled != enable) { + Q_EMIT textEdit->copyAvailable(enable); + } +} + +void TextPagerEdit::takeSyntaxHighlighter(SyntaxHighlighter *highlighter) +{ + Q_ASSERT(highlighter); + const bool found = d->syntaxHighlighters.removeOne(highlighter); + Q_ASSERT(found); + Q_UNUSED(found); + highlighter->d->textEdit = 0; + highlighter->d->textLayout = 0; + disconnect(highlighter, 0, d, 0); +} + +void TextPagerEdit::removeSyntaxHighlighter(SyntaxHighlighter *highlighter) +{ + takeSyntaxHighlighter(highlighter); + delete highlighter; +} + +void TextPagerEdit::addSyntaxHighlighter(SyntaxHighlighter *highlighter) +{ + Q_ASSERT(highlighter); + if (highlighter->textEdit() != this) { + if (highlighter->textEdit()) { + qWarning("A SyntaxHighlighter can only be added to 1 TextPagerEdit. If this is a " + "use case you care about (having a syntaxHighlighter added to multiple " + "text edits) I could fix it. Anders"); + return; + } + d->syntaxHighlighters.append(highlighter); + connect(highlighter, SIGNAL(destroyed(QObject*)), d, SLOT(onSyntaxHighlighterDestroyed(QObject*))); + highlighter->d->textEdit = this; + highlighter->d->textLayout = d; + d->layoutDirty = true; + viewport()->update(); + } +} + +void TextPagerEdit::clearSyntaxHighlighters() +{ + Q_FOREACH(SyntaxHighlighter *highlighter, d->syntaxHighlighters) { + if (highlighter->parent() == this) { + removeSyntaxHighlighter(highlighter); + } else { + takeSyntaxHighlighter(highlighter); + } + } + Q_ASSERT(d->syntaxHighlighters.isEmpty()); +} + + +void TextEditPrivate::onSyntaxHighlighterDestroyed(QObject *o) +{ + const bool found = syntaxHighlighters.removeOne(static_cast(o)); + Q_ASSERT(found); + Q_UNUSED(found); + layoutDirty = true; + textEdit->viewport()->update(); +} + +QList TextPagerEdit::syntaxHighlighters() const +{ + return d->syntaxHighlighters; +} + +static inline bool compareExtraSelection(const TextPagerEdit::ExtraSelection &left, const TextPagerEdit::ExtraSelection &right) +{ + return left.cursor < right.cursor; +} + +void TextPagerEdit::setExtraSelections(const QList &selections) +{ + d->extraSelections = selections; + qSort(d->extraSelections.begin(), d->extraSelections.end(), compareExtraSelection); + d->layoutDirty = true; + viewport()->update(); +} + +QList TextPagerEdit::extraSelections() const +{ + return d->extraSelections; +} + +void TextEditPrivate::onTextSectionRemoved(TextPagerSection *section) +{ + Q_ASSERT(section); + if (!dirtyForSection(section)) + return; + + sectionsDirty = true; + if (section == sectionPressed) { + sectionPressed = 0; + } + if (section->hasCursor()) { + updateCursorPosition(lastHoverPos); + } +} + +void TextEditPrivate::onTextSectionAdded(TextPagerSection *section) +{ + dirtyForSection(section); + updateCursorPosition(lastHoverPos); + sectionsDirty = true; +} + +void TextEditPrivate::onScrollBarValueChanged(int value) +{ + if (blockScrollBarUpdate || value == requestedScrollBarPosition || value == viewportPosition) + return; + requestedScrollBarPosition = value; + layoutDirty = true; + textEdit->viewport()->update(); + + Q_EMIT scrollBarChanged(); +} + +void TextEditPrivate::onScrollBarActionTriggered(int action) +{ + // IR: see comment at the top of cursorMoveKeyEventReadOnly(). + // Also: added code to handle SliderPageStepAdd and SliderPageStepSub because + // these cases were not handled well by default. These cases occur when the user clicks inside + // the scrollbar area, but not on the bar itself; this should do something similar or + // identical to a page up/down operation. + + switch (action) { + case QAbstractSlider::SliderSingleStepAdd: + scrollLines(1); requestedScrollBarPosition = -1; break; + case QAbstractSlider::SliderSingleStepSub: + scrollLines(-1); requestedScrollBarPosition = -1; break; + case QAbstractSlider::SliderPageStepAdd: + scrollLines(qMax(1, visibleLines - 1)); requestedScrollBarPosition = -1; break; + case QAbstractSlider::SliderPageStepSub: + scrollLines(-qMax(1, visibleLines - 2)); requestedScrollBarPosition = -1; break; + default: break; + } + + Q_EMIT scrollBarChanged(); +} + +void TextEditPrivate::updateScrollBar() +{ + Q_ASSERT(!textEdit->verticalScrollBar()->isSliderDown()); + if (pendingScrollBarUpdate) { + const bool old = blockScrollBarUpdate; + blockScrollBarUpdate = true; + textEdit->verticalScrollBar()->setValue(viewportPosition); + blockScrollBarUpdate = old; + pendingScrollBarUpdate = false; + + Q_EMIT scrollBarChanged(); + } +} + +void TextEditPrivate::onCharactersAddedOrRemoved(int from, int count) +{ + Q_ASSERT(count >= 0); + Q_UNUSED(count); + + textCursor.clearSelection(); + + if(textCursor.position() > document->documentSize()) + textCursor.setPosition(0); + + UiLog().dbg() << + "TextEditPrivate::onCharactersAddedOrRemoved --> textCursor: " << textCursor.position(); + + /*if (from > qMin(bufferPosition + buffer.size(), layoutEnd)) { + return; + }*/ + buffer.clear(); // isn't it better to just add them here? + layoutDirty = true; + textEdit->viewport()->update(); +} + +void TextPagerEdit::ensureCursorVisible() +{ + if (d->textCursor.position() < d->viewportPosition) { + d->updateViewportPosition(qMax(0, d->textCursor.position() - 1), TextPagerLayout::Backward); + } else if (d->textCursor.position() > d->layoutEnd) { + d->updateViewportPosition(d->textCursor.position(), TextPagerLayout::Backward); + viewport()->update(); + } else { + const QRect r = viewport()->rect(); + QRect realCrect = cursorRect(d->textCursor); + QRect crect = realCrect; + crect.setLeft(r.left()); + crect.setRight(r.right()); + // ### what if the cursor is out of bounds horizontally? + + //qDebug() << "ensureCursorVisible" << r << crect << cursorRect(d->textCursor) << horizontalScrollBar()->value() << horizontalScrollBar()->maximum() << d->widest; + if (!r.contains(crect)) { + if (r.intersects(crect)) { + int scroll; + if (d->autoScrollLines != 0) { + scroll = d->autoScrollLines; + } else { + scroll = (crect.top() < r.top() ? -1 : 1); + } + d->scrollLines(scroll); + } else { + d->updateViewportPosition(d->textCursor.position(), TextPagerLayout::Backward); + } + viewport()->update(); + } + + //Adjust the horizontal position + if(realCrect.left() > horizontalScrollBar()->value()+r.width()) + { + int hval=realCrect.left()-r.width()/2; + horizontalScrollBar()->setValue((hval < horizontalScrollBar()->maximum())? + hval: + horizontalScrollBar()->maximum()); + } + else if(realCrect.right() < horizontalScrollBar()->value()) + { + int hval=realCrect.left()-r.width()/2; + horizontalScrollBar()->setValue((hval > horizontalScrollBar()->minimum())? + hval: + horizontalScrollBar()->minimum()); + } + } +} + +//textEdit->horizontalScrollBar()->setPageStep(s.width()); +//textEdit->horizontalScrollBar()->setMaximum(qMax(0, widest - s.width())); + + +TextPagerCursor &TextPagerEdit::textCursor() +{ + return d->textCursor; +} + +const TextPagerCursor &TextPagerEdit::textCursor() const +{ + return d->textCursor; +} + +void TextPagerEdit::setTextCursor(const TextPagerCursor &textCursor) +{ + const bool doEmit = (d->textCursor != textCursor); + d->textCursor = textCursor; + d->textCursor.textEdit = this; + if (doEmit) { + ensureCursorVisible(); + viewport()->update(); + Q_EMIT cursorPositionChanged(textCursor.position()); + } +} + +TextPagerCursor TextPagerEdit::cursorForPosition(const QPoint &pos) const +{ + const int idx = textPositionAt(pos); + if (idx == -1) + return TextPagerCursor(); + return TextPagerCursor(this, idx); +} + +TextPagerSection *TextPagerEdit::sectionAt(const QPoint &pos) const +{ + Q_ASSERT(d->document); + int textPos = textPositionAt(pos); + if (textPos == -1) + textPos = d->document->d->documentSize - 1; + return d->document->d->sectionAt(textPos, this); +} + +QList TextPagerEdit::sections(int from, int size, TextPagerSection::TextSectionOptions opt) const +{ + Q_ASSERT(d->document); + QList sections = d->document->d->getSections(from, size, opt, this); + return sections; +} + +TextPagerSection *TextPagerEdit::insertTextSection(int pos, int size, const QTextCharFormat &format, const QVariant &data) +{ + Q_ASSERT(d->document); + TextPagerSection *section = d->document->insertTextSection(pos, size, format, data); + if (section) { + section->d.textEdit = this; + section->d.priority = 100; + } + return section; +} + +void TextEditPrivate::updateHorizontalPosition() +{ + const QRect r = textEdit->viewport()->rect(); + const QPoint p(qBound(0, lastMouseMove.x(), r.right()), + qBound(0, lastMouseMove.y(), r.bottom())); + int pos = textPositionAt(p); + if (pos == -1) + pos = document->documentSize() - 1; + textEdit->setCursorPosition(pos, TextPagerCursor::KeepAnchor); +} + +void TextEditPrivate::updateScrollBarPosition() +{ + if (requestedScrollBarPosition == -1) { + return; + } else if (requestedScrollBarPosition == viewportPosition) { + requestedScrollBarPosition = -1; + return; + } + + const int req = requestedScrollBarPosition; + requestedScrollBarPosition = -1; + + Direction direction = Forward; + if (lastRequestedScrollBarPosition != -1 && lastRequestedScrollBarPosition != req) { + if (lastRequestedScrollBarPosition > req) { + direction = Backward; + } + } else if (req < viewportPosition) { + direction = Backward; + } + + lastRequestedScrollBarPosition = req; + + updateViewportPosition(req, direction); +} + +void TextEditPrivate::updateScrollBarPageStep() +{ + if (lines.isEmpty()) { + textEdit->verticalScrollBar()->setPageStep(1); + return; + } + const int visibleCharacters = lines.at(qMin(visibleLines, lines.size() - 1)).first - lines.at(0).first; + textEdit->verticalScrollBar()->setPageStep(visibleCharacters); +} + +void TextEditPrivate::onDocumentDestroyed() +{ + document = 0; + textEdit->setDocument(new TextPagerDocument(this)); // there should always be a document +} + +void TextEditPrivate::scrollLines(int lines) +{ + // IR: see comment at the top of cursorMoveKeyEventReadOnly(). + + int pos = viewportPosition; + const Direction d = (lines < 0 ? Backward : Forward); + const int add = lines < 0 ? -1 : 1; + + while (pos + add >= 0 && pos + add < document->documentSize()) { + if (d == Forward && + (lines - add == 0) && + bufferReadCharacter(pos) == '\n') { + // When iterating forwards, be sure not to skip over blank lines + // (ie. lines containing only '\n') by just ignoring them here + // - updateViewportPosition automatically takes us one index past + // this newline, thus displaying the next line) + + // IR: the above comment does not seem to be true, at least it is not always true. + // If performing a 'page down' operation, this 'break' causes one less line to + // be scrolled. This is because pos is already on the newline at the end of + // the previous line; this check re-reads that newline and then breaks without + // reading the last line at all. This is not done when paging backwards. So + // scrolling down 5 lines actually scrolls down 4 lines, and scrolling up 5 + // lines does indeed scroll up 5 lines. + break; + } else { + pos += add; + if (bufferReadCharacter(pos) == '\n') { + if ((lines -= add) == 0) { + break; + } + } + } + } + updateViewportPosition(pos, d); +} + +void TextEditPrivate::timerEvent(QTimerEvent *e) +{ + if (e->timerId() == tripleClickTimer.timerId()) { + tripleClickTimer.stop(); + } else if (e->timerId() == autoScrollTimer.timerId()) { + if (pendingTimeOut != -1) { + autoScrollTimer.start(pendingTimeOut, this); + pendingTimeOut = -1; + } + const QRect r = textEdit->viewport()->rect(); + Q_ASSERT(!r.contains(lastMouseMove)); + enum { LineCount = 1 }; + if (lastMouseMove.y() < r.top()) { + scrollLines(-LineCount); + if (atBeginning()) + autoScrollTimer.stop(); + } else { + Q_ASSERT(lastMouseMove.y() > r.bottom()); + scrollLines(LineCount); + if (atEnd()) + autoScrollTimer.stop(); + } + const QPoint p(qBound(0, lastMouseMove.x(), r.right()), + qBound(0, lastMouseMove.y(), r.bottom())); + int pos = textPositionAt(p); + if (pos == -1) + pos = document->documentSize() - 1; + textEdit->setCursorPosition(pos, TextPagerCursor::KeepAnchor); + } else if (e->timerId() == cursorBlinkTimer.timerId()) { + cursorVisible = !cursorVisible; + const QRect r = textEdit->cursorRect(textCursor) & textEdit->viewport()->rect(); + if (!r.isNull()) + textEdit->viewport()->update(r); + } else { + Q_ASSERT(0); + } +} + +static inline bool compareTextSectionByPriority(const TextPagerSection *left, const TextPagerSection *right) +{ + return left->priority() > right->priority(); +} + +void TextEditPrivate::updateCursorPosition(const QPoint &pos) +{ + lastHoverPos = pos; + const int textPos = textPositionAt(pos); + if (textPos != -1) { + QList hovered = textEdit->sections(textPos, 1, TextPagerSection::IncludePartial); + qSort(hovered.begin(), hovered.end(), compareTextSectionByPriority); + Q_FOREACH(TextPagerSection *section, hovered) { + if (section->hasCursor()) { + delete sectionCursor; + sectionCursor = new QCursor(section->cursor()); + textEdit->viewport()->setCursor(*sectionCursor); + return; + } + } + } + // no cursor + if (sectionCursor) { + textEdit->viewport()->setCursor(Qt::IBeamCursor); + delete sectionCursor; + sectionCursor = 0; + } + + //To notify the line num area widge! + Q_EMIT scrollBarChanged(); +} + +bool TextEditPrivate::isSectionOnScreen(const TextPagerSection *section) const +{ + Q_ASSERT(section->document() == document); + return (::matchSection(section, textEdit) + && section->position() <= layoutEnd + && section->position() + section->size() >= viewportPosition); +} + +void TextEditPrivate::cursorMoveKeyEventReadOnly(QKeyEvent *e) +{ + // IR: changed MoveToPreviousPage from visibleLines to (visibleLines-2) because: + // the scrollLines routine does not perform consistently between page up and page down; + // performing a page down followed by a page up does not get you back to where you started. + // See my comment in scrollLines, which might point to why this is. The quickest way + // to make the functions consistent was to make this change. The primary reason was to + // ensure that the user does not miss any lines of text when they page up and down through + // the document. The same change was made in onScrollBarActionTriggered. + + if (e == QKeySequence::MoveToNextLine) { + scrollLines(1); + } else if (e == QKeySequence::MoveToPreviousLine) { + scrollLines(-1); + } else if (e == QKeySequence::MoveToStartOfDocument) { + textEdit->verticalScrollBar()->setValue(0); + } else if (e == QKeySequence::MoveToEndOfDocument) { + textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum()); + } else if (e == QKeySequence::MoveToNextPage) { + scrollLines(qMax(1, visibleLines - 1)); + } else if (e == QKeySequence::MoveToPreviousPage) { + scrollLines(-qMax(1, visibleLines - 2)); + } else if (e == QKeySequence::MoveToStartOfLine) { + textCursor.movePosition(TextPagerCursor::StartOfLine); + e->accept(); + return; + } else if (e == QKeySequence::MoveToEndOfLine) { + textCursor.movePosition(TextPagerCursor::EndOfLine); + e->accept(); + return; + } + + else { + e->ignore(); + return; + } + e->accept(); + textCursor.setPosition(viewportPosition); +} + +void TextEditPrivate::relayout() +{ + const QSize s = textEdit->viewport()->size(); + + int widestOri=widest; + + widest=-1; + relayoutByGeometry(s.height()); + if(widest == -1) + widest=widestOri; + + textEdit->horizontalScrollBar()->setPageStep(s.width()); + textEdit->horizontalScrollBar()->setMaximum(qMax(0, widest - s.width())); +#ifdef _UI_TEXTPAGER_DEBUG + qDebug() << widest << s.width() << textEdit->horizontalScrollBar()->maximum(); +#endif +} + +bool TextEditPrivate::dirtyForSection(TextPagerSection *section) +{ + if (isSectionOnScreen(section)) { + layoutDirty = true; + textEdit->viewport()->update(); + return true; + } else { + return false; + } +} + +void TextEditPrivate::onTextSectionFormatChanged(TextPagerSection *section) +{ + dirtyForSection(section); +} + +void TextEditPrivate::onTextSectionCursorChanged(TextPagerSection *section) +{ + if (isSectionOnScreen(section)) { + updateCursorPosition(lastHoverPos); + } +} + +void TextEditPrivate::onSelectionChanged() +{ + if (inMouseEvent && textCursor.hasSelection() && QApplication::clipboard()->supportsSelection()) { + textEdit->copy(QClipboard::Selection); + } + + textEdit->viewport()->update(); + // ### could figure out old selection rect and | it with new one + // ### and update that but is it worth it? + updateCopyAndCutEnabled(); +} + +bool TextEditPrivate::canInsertFromMimeData(const QMimeData *data) const +{ + return data->hasText(); +} + + +//Find the viewport position for the end of the document supposing the the last line +//is exactly located at the bottom of the viewport. +int TextEditPrivate::findLastPageSize() const +{ + if (!document || document->documentSize() == 0) + return -1; + +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << "TextEditPrivate::findLastPageSize -->"; +#endif + TextEditPrivate p(textEdit); + p.font=textEdit->font(); + p.viewportPosition = 0; + p.document = document; + const int documentSize = document->documentSize(); + p.maxViewportPosition = documentSize; + + //The viewport height (including the height of the horizontal slider) + int vh=p.textEdit->viewport()->height(); //-p.textEdit->horizontalScrollBar()->height(); + + //Max 1.5*MinimumBufferSize can be kept in the buffer after the viewportPosition + int maxCharNum=p.MinimumBufferSize*1.5-200; + + //We use the lastpage cache to find a better start position + +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " lastPageCache height:" << lastPage.height << "position:" << lastPage.position << + "documentSize:" << lastPage.documentSize << "widest:" << lastPage.widest; +#endif + + if(lastPage.documentSize != documentSize) + { + lastPage.clear(); + lastPage.documentSize = documentSize; + } + + Direction sizeChangeDir=Forward; + if(lastPage.height != -1) { + if(lastPage.height > vh) { + maxCharNum=documentSize-lastPage.position+1; + sizeChangeDir=Backward; + } else if(lastPage.height< vh) { + maxCharNum=documentSize-lastPage.position+1000; + sizeChangeDir=Backward; + } else { + return lastPage.position; + } + } + + lastPage.height=vh; + + Q_ASSERT(maxCharNum>=0); + + int start = documentSize-maxCharNum; + if(start < 0) start=0; +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + QTime tm; + tm.start(); +#endif + + //Try to find the last page by iteration + int step=0; + const int maxStep=10; + bool reachedTop=(start == 0); + + while(step < maxStep) { +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " ITERATION STEP:" << step; + qDebug() << " start:" << start << "maxCharNum:" << maxCharNum; +#endif + //We get the viewportPosition closest to start + p.updateViewportPosition(start,sizeChangeDir,false); + +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " after updateviewport> start-viewportPos:" << start-p.viewportPosition; +#endif + //adjust the max number of characters + if(start > p.viewportPosition) + maxCharNum+=(start-p.viewportPosition)+1; + + //Relayout using the max number of characters + p.relayoutByPosition(maxCharNum); + +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " vh:" << vh << "fontSize:" << font.pointSize(); + qDebug() << " viewport size" << textEdit->viewport()->size(); + qDebug() << " layoutEnd:" << p.layoutEnd << "documentSize:" << documentSize << "viewportPosition:" << p.viewportPosition << p.contentRect; + qDebug() << " bottom:" << p.textLayouts.last()->boundingRect().bottom(); +#endif + + //The end of the layout should be the end of the document + if(p.layoutEnd == documentSize && p.textLayouts.count() >0) { + + int top=p.textLayouts.last()->boundingRect().bottom()+4-vh; + int pos=documentSize-p.textLayouts.last()->text().size(); + + for(int i=p.textLayouts.count()-2; i >=0; i--) { + + if(p.textLayouts.at(i)->boundingRect().top() <= top) { +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " find position:" << pos; // << "line:" << p.document->lineNumber(pos); +#endif + p.updateViewportPosition(pos, Backward,false); +#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE + qDebug() << " viewPortPosition:" << p.viewportPosition << "time:" << tm.elapsed(); // << "line:" << p.document->lineNumber(p.viewportPosition); +#endif + lastPage.position=p.viewportPosition; + lastPage.widest=p.widest; + return p.viewportPosition; + } + pos-=p.textLayouts.at(i)->text().size(); + } + } + //We could not find the last page. We try again from a larger number of characters from the + //end of the document + step++; + maxCharNum+=1000; + start = documentSize-maxCharNum; + + if(start < 0 && reachedTop) + break; + + if(start < 0) + { + reachedTop=true; + start=0; + } + } + + lastPage.clear(); + + //the whole text is in the viewport, no vertical scrollbar is needed + if(reachedTop) + return -1; + + //If we are here we the last page position will be the end of the document and there will be + //a white space block after the last row in the editor. + //TODO: improve it! + return documentSize; +} + +void TextPagerEdit::setSyntaxHighlighter(SyntaxHighlighter *h) +{ + if (h && h->textEdit() == this) { + if (d->syntaxHighlighters.size() == 1) + return; + takeSyntaxHighlighter(h); + } + clearSyntaxHighlighters(); + if (h) { + addSyntaxHighlighter(h); + } +} + + +void TextPagerEdit::setEnableSearchHighlighter(bool b) +{ + useSearchHighlight_=b; + if(useSearchHighlight_) { + setSyntaxHighlighter(searchHighlight_); + } else { + //searchHighlight_ can only be deleted when the editor is deleted. + //Because clearSyntaxHighlighters() deletes the highlighters we need to use + //takeSyntaxHighlighter() instead. + if(d->syntaxHighlighters.contains(searchHighlight_)) { + takeSyntaxHighlighter(searchHighlight_); + d->layoutDirty = true; + viewport()->update(); + //clearSyntaxHighlighters(); + } + } +} + +void TextPagerEdit::clearSearchHighlighter() +{ + searchHighlight_->clear(); +} + +void TextPagerEdit::setSearchHighlighter(QString txt,TextPagerDocument::FindMode mode) +{ + searchHighlight_->reset(txt,mode,useSearchHighlight_); +} + +void TextPagerEdit::setSearchHighlighter(QRegExp rx,TextPagerDocument::FindMode mode) +{ + searchHighlight_->reset(rx,mode,useSearchHighlight_); +} + +void TextPagerEdit::gotoLine(int lineNum) +{ + TextPagerCursor cursor=d->document->findLine(lineNum,textCursor()); + if(!cursor.isNull()) + setTextCursor(cursor); +} + +//--------------------------------------------- +// Fontsize management +//--------------------------------------------- + +void TextPagerEdit::setFontProperty(VProperty* p) +{ + fontProp_=p; + fontProp_->addObserver(this); + updateFont(); +} + +void TextPagerEdit::zoomIn() +{ + QFont f=font(); + int fps=f.pointSize(); + f.setPointSize(fps+1); + setFont(f); + + fontSizeChangedByZoom(); +} + +void TextPagerEdit::zoomOut() +{ + int oriSize=font().pointSize(); + + QFont f=font(); + int fps=f.pointSize(); + if(fps > 1) + { + f.setPointSize(fps-1); + setFont(f); + } + + if(font().pointSize() != oriSize) + fontSizeChangedByZoom(); +} + +void TextPagerEdit::fontSizeChangedByZoom() +{ + if(fontProp_) + fontProp_->setValue(font()); +} + +void TextPagerEdit::updateFont() +{ + if(fontProp_) + { + QFont f=fontProp_->value().value(); + if(font() != f) + setFont(f); + } +} + +void TextPagerEdit::notifyChange(VProperty* p) +{ + if(fontProp_ ==p) + { + setFont(p->value().value()); + } +} + +void TextPagerEdit::setShowLineNumbers(bool b) +{ + showLineNum_=b; + if(lineNumArea_) + { + lineNumArea_->setVisible(b); + //Initialise the width + lineNumArea_->updateWidth(); + } +} + +void TextPagerEdit::setLineNumberArea(TextPagerLineNumberArea *a) +{ + lineNumArea_=a; + connect(d,SIGNAL(scrollBarChanged()), + lineNumArea_,SLOT(update())); + + //Initialise the width + lineNumArea_->updateWidth(); +} + +void TextPagerEdit::lineNumberAreaPaintEvent(QPaintEvent *e) +{ + if(!lineNumArea_ || !showLineNum_) + return; + + QPainter painter(lineNumArea_); + const QRect er = e->rect(); + //painter.translate(-horizontalScrollBar()->value(), 0); + //painter.setFont(font()); + + QVector selections; + selections.reserve(d->extraSelections.size() + 1); + int textLayoutOffset = d->viewportPosition; + + QRect numRect=er; + + //Background and border + painter.fillRect(numRect, lineNumArea_->bgColour()); + painter.setPen(QPen(lineNumArea_->separatorColour())); + painter.drawLine(lineNumArea_->width()-1,er.y(),lineNumArea_->width()-1,er.bottom()); + + int numWidth=lineNumArea_->width()-lineNumArea_->rightMargin(); + + QFont fontNormal(font()); // the font to use for most line numbers + QFont fontBold(fontNormal); // the font to use for the current line number + fontBold.setBold(true); + painter.setPen(lineNumArea_->fontColour()); + painter.setFont(fontNormal); + + int cursorPos=d->textCursor.position(); + + //const QTextLayout *cursorLayout = d->cursorVisible ? d->layoutForPosition(d->textCursor.position()) : 0; + //int extraSelectionIndex = 0; + QTextLayout::FormatRange selectionRange; + selectionRange.start = -1; + int maxLineNum=-1; + Q_FOREACH(QTextLayout *l, d->textLayouts) { + const int textSize = l->text().size(); + const QRect r = l->boundingRect().toRect(); + if (r.intersects(er)) { + + const int lineNum=lineNumber(textLayoutOffset)+1; + maxLineNum=lineNum; + QRect lRect(0,r.y(),numWidth,r.height()); + + // is this the current line? + if(cursorPos >= textLayoutOffset && cursorPos <= textLayoutOffset+l->text().size()) + { + painter.setFont(fontBold); + painter.fillRect(lRect, lineNumArea_->currentColour()); // highlight the background + painter.drawText(lRect,QString::number(lineNum),Qt::AlignRight|Qt::AlignVCenter); + painter.setFont(fontNormal); + } + else + { + painter.drawText(lRect,QString::number(lineNum),Qt::AlignRight|Qt::AlignVCenter); + } + + } else if (r.top() > er.bottom()) { + break; + } + textLayoutOffset += textSize + 1; + } + + if(maxLineNum != -1) + lineNumArea_->updateWidth(maxLineNum); +} + +//========================================================================== +// +// TextPagerLineNumberArea +// +//========================================================================== + +TextPagerLineNumberArea::TextPagerLineNumberArea(TextPagerEdit *editor) : + QWidget(editor), textEditor_ (editor), digits_(6), rightMargin_(3), + bgCol_(232,231,230), fontCol_(220,220,200), separatorCol_(220,220,200), + currentCol_(212,212,255) +{ + Q_ASSERT(textEditor_); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaBackground")) + bgCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaFontColour")) + fontCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaSeparator")) + separatorCol_=p->value().value(); + + if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaCurrent")) + currentCol_=p->value().value(); + + editor->setLineNumberArea(this); +} + +void TextPagerLineNumberArea::updateWidth(int maxLineNum) +{ + if(maxLineNum == -1 || pow(10,digits_)-1 < maxLineNum) + { + setFixedWidth(computeWidth(maxLineNum)); + } +} + +int TextPagerLineNumberArea::computeWidth(int maxLineNum) const +{ + if(maxLineNum > 0) + { + int maxDigits=1; + int mx = maxLineNum; + while ( mx >= 10) + { + mx /= 10; + ++maxDigits; + } + + if(maxDigits > digits_) + digits_=maxDigits; + } + + return (textEditor_)?(3 + textEditor_->fontMetrics().width(QLatin1Char('9')) * digits_ + rightMargin_):3+rightMargin_; + +} + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,228 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGEREDIT_HPP__ +#define TEXTPAGEREDIT_HPP__ + +#include +#include + +#include "syntaxhighlighter.hpp" +#include "TextPagerDocument.hpp" +#include "TextPagerCursor.hpp" +#include "TextPagerSection.hpp" + +#include "VProperty.hpp" + +class TextPagerLineNumberArea; +class TextEditPrivate; +class TextPagerSearchHighlighter; + +class TextPagerEdit : public QAbstractScrollArea, public VPropertyObserver +{ + friend class TextPagerLineNumberArea; + + Q_OBJECT + Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth) + Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly) + Q_PROPERTY(bool cursorVisible READ cursorVisible WRITE setCursorVisible) + Q_PROPERTY(QString selectedText READ selectedText) + Q_PROPERTY(int maximumSizeCopy READ maximumSizeCopy WRITE setMaximumSizeCopy) + Q_PROPERTY(bool lineBreaking READ lineBreaking WRITE setLineBreaking) + +public: + TextPagerEdit(QWidget *parent = 0); + ~TextPagerEdit(); + + TextPagerDocument *document() const; + void setDocument(TextPagerDocument *doc); + + int cursorWidth() const; + void setCursorWidth(int cc); + + struct ExtraSelection + { + TextPagerCursor cursor; + QTextCharFormat format; + }; + + void setExtraSelections(const QList &selections); + QList extraSelections() const; + + void setSyntaxHighlighter(SyntaxHighlighter *h); + inline SyntaxHighlighter *syntaxHighlighter() const { return syntaxHighlighters().value(0); } + + QList syntaxHighlighters() const; + void addSyntaxHighlighter(SyntaxHighlighter *highlighter); + void takeSyntaxHighlighter(SyntaxHighlighter *highlighter); + void removeSyntaxHighlighter(SyntaxHighlighter *highlighter); + void clearSyntaxHighlighters(); + + //bool load(QIODevice *device, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse, QTextCodec *codec = 0); + + bool load(const QString &fileName, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse, QTextCodec *codec = 0); + + void paintEvent(QPaintEvent *e); + void scrollContentsBy(int dx, int dy); + + bool moveCursorPosition(TextPagerCursor::MoveOperation op, TextPagerCursor::MoveMode = TextPagerCursor::MoveAnchor, int n = 1); + void setCursorPosition(int pos, TextPagerCursor::MoveMode mode = TextPagerCursor::MoveAnchor); + + int viewportPosition() const; + int cursorPosition() const; + + int textPositionAt(const QPoint &pos) const; + + bool readOnly() const; + void setReadOnly(bool rr); + + bool lineBreaking() const; + void setLineBreaking(bool lb); + + int maximumSizeCopy() const; + void setMaximumSizeCopy(int max); + + QRect cursorBlockRect(const TextPagerCursor &cursor) const; + QRect cursorRect(const TextPagerCursor &cursor) const; + + int lineNumber(int position) const; + int columnNumber(int position) const; + int lineNumber(const TextPagerCursor &cursor) const; + int columnNumber(const TextPagerCursor &cursor) const; + + bool cursorVisible() const; + void setCursorVisible(bool cc); + + QString selectedText() const; + bool hasSelection() const; + + void setText(const QString &text); + QString read(int pos, int size) const; + QChar readCharacter(int index) const; + + void insert(int pos, const QString &text); + void remove(int from, int size); + + TextPagerCursor &textCursor(); + const TextPagerCursor &textCursor() const; + void setTextCursor(const TextPagerCursor &textCursor); + + TextPagerCursor cursorForPosition(const QPoint &pos) const; + + TextPagerSection *sectionAt(const QPoint &pos) const; + + QList sections(int from = 0, int size = -1, TextPagerSection::TextSectionOptions opt = 0) const; + inline TextPagerSection *sectionAt(int pos) const { return sections(pos, 1, TextPagerSection::IncludePartial).value(0); } + TextPagerSection *insertTextSection(int pos, int size, const QTextCharFormat &format = QTextCharFormat(), + const QVariant &data = QVariant()); + + void ensureCursorVisible(const TextPagerCursor &cursor, int linesMargin = 0); + + void setEnableSearchHighlighter(bool); + void clearSearchHighlighter(); + void setSearchHighlighter(QString txt,TextPagerDocument::FindMode mode); + void setSearchHighlighter(QRegExp rx,TextPagerDocument::FindMode mode); + + void gotoLine(int); + void setFontProperty(VProperty* p); + void notifyChange(VProperty* p); + void zoomIn(); + void zoomOut(); + + void setShowLineNumbers(bool b); + void setLineNumberArea(TextPagerLineNumberArea *a); + + enum ActionType { + CopyAction, + SelectAllAction + }; + QAction *action(ActionType type) const; + +public Q_SLOTS: + void ensureCursorVisible(); + void copy(QClipboard::Mode mode = QClipboard::Clipboard); + void selectAll(); + void clearSelection(); + +Q_SIGNALS: + void copyAvailable(bool on); + void textChanged(); + void selectionChanged(); + void cursorPositionChanged(int pos); + void sectionClicked(TextPagerSection *section, const QPoint &pos); + +protected: + //virtual void paste(int position, QClipboard::Mode mode); + virtual void changeEvent(QEvent *e); + virtual void keyPressEvent(QKeyEvent *e); + virtual void keyReleaseEvent(QKeyEvent *e); + virtual void wheelEvent(QWheelEvent *e); + virtual void mousePressEvent(QMouseEvent *e); + virtual void mouseDoubleClickEvent(QMouseEvent *); + virtual void mouseMoveEvent(QMouseEvent *e); + virtual void mouseReleaseEvent(QMouseEvent *e); + virtual void resizeEvent(QResizeEvent *e); + +private: + void updateFont(); + void fontSizeChangedByZoom(); + + void lineNumberAreaPaintEvent(QPaintEvent *e); + int lineNumberAreaWidth(); + void updateLineNumberArea(); + + TextEditPrivate *d; + friend class TextLayoutCacheManager; + friend class TextEditPrivate; + friend class TextPagerCursor; + + TextPagerSearchHighlighter* searchHighlight_; + bool useSearchHighlight_; + + bool showLineNum_; + TextPagerLineNumberArea* lineNumArea_; + VProperty* fontProp_; +}; + + +class TextPagerLineNumberArea : public QWidget +{ +public: + explicit TextPagerLineNumberArea(TextPagerEdit *editor); + QSize sizeHint() const {return QSize(computeWidth(), 0);} + int rightMargin() const {return rightMargin_;} + void updateWidth(int maxLineNum=-1); + QColor bgColour() const {return bgCol_;} + QColor fontColour() const {return fontCol_;} + QColor separatorColour() const {return separatorCol_;} + QColor currentColour() const {return currentCol_;} + +protected: + void paintEvent(QPaintEvent *event) { textEditor_->lineNumberAreaPaintEvent(event);} + +private: + int computeWidth(int maxLineNum=-1) const; + + TextPagerEdit *textEditor_; + mutable int digits_; + int rightMargin_; + QColor bgCol_; + QColor fontCol_; + QColor separatorCol_; + QColor currentCol_; +}; + + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit_p.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit_p.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerEdit_p.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerEdit_p.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,146 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGEREDIT_P_HPP__ +#define TEXTPAGEREDIT_P_HPP__ + +#include +#include +#include +#include +#include +#include "TextPagerLayout_p.hpp" +#include "TextPagerDocument_p.hpp" +#include "TextPagerCursor.hpp" +#include "TextPagerEdit.hpp" + +struct DocumentCommand; +struct CursorData { + int position, anchor; +}; + +struct LastPageCache { + LastPageCache() : position(-1),height(-1),widest(-1), documentSize(-1) {} + void clear() {position=-1; height=-1; widest=-1; documentSize=-1;} + int position; + int height; + int widest; + int documentSize; +}; + +class TextEditPrivate : public QObject, public TextPagerLayout +{ + Q_OBJECT +public: + TextEditPrivate(TextPagerEdit *qptr) + : requestedScrollBarPosition(-1), lastRequestedScrollBarPosition(-1), cursorWidth(2), + sectionCount(0), maximumSizeCopy(50000), pendingTimeOut(-1), autoScrollLines(0), + readOnly(true), cursorVisible(false), blockScrollBarUpdate(false), + updateScrollBarPageStepPending(true), inMouseEvent(false), sectionPressed(0), + pendingScrollBarUpdate(false), sectionCursor(0) + { + textEdit = qptr; + } + + bool canInsertFromMimeData(const QMimeData *data) const; + void updateHorizontalPosition(); + void updateScrollBarPosition(); + void updateScrollBarPageStep(); + void scrollLines(int lines); + void timerEvent(QTimerEvent *e); + void updateCursorPosition(const QPoint &pos); + int findLastPageSize() const; + bool atBeginning() const { return viewportPosition == 0; } + bool atEnd() const { return textEdit->verticalScrollBar()->value() == textEdit->verticalScrollBar()->maximum(); } + bool dirtyForSection(TextPagerSection *section); + void updateCopyAndCutEnabled(); + bool isSectionOnScreen(const TextPagerSection *section) const; + void cursorMoveKeyEventReadOnly(QKeyEvent *e); + virtual void relayout(); // from TextPagerLayout + void adjustVerticalScrollBar(); + + int requestedScrollBarPosition, lastRequestedScrollBarPosition, cursorWidth, sectionCount, + maximumSizeCopy, pendingTimeOut, autoScrollLines; + bool readOnly, cursorVisible, blockScrollBarUpdate, updateScrollBarPageStepPending, inMouseEvent; + QBasicTimer autoScrollTimer, cursorBlinkTimer; + QAction *actions[TextPagerEdit::SelectAllAction]; + TextPagerSection *sectionPressed; + TextPagerCursor textCursor, dragOverrideCursor; + QBasicTimer tripleClickTimer; + bool pendingScrollBarUpdate; + QCursor *sectionCursor; + QPoint lastHoverPos, lastMouseMove; + QHash > undoRedoCommands; + mutable LastPageCache lastPage; + +public Q_SLOTS: + void onSyntaxHighlighterDestroyed(QObject *o); + void onSelectionChanged(); + void onTextSectionAdded(TextPagerSection *section); + void onTextSectionRemoved(TextPagerSection *section); + void onTextSectionFormatChanged(TextPagerSection *section); + void onTextSectionCursorChanged(TextPagerSection *section); + void updateScrollBar(); + void onDocumentDestroyed(); + void onDocumentSizeChanged(int size); + +#if 0 + void onDocumentCommandInserted(DocumentCommand *cmd); + void onDocumentCommandFinished(DocumentCommand *cmd); + void onDocumentCommandRemoved(DocumentCommand *cmd); + void onDocumentCommandTriggered(DocumentCommand *cmd, bool undo); +#endif + + + void onScrollBarValueChanged(int value); + void onScrollBarActionTriggered(int action); + void onCharactersAddedOrRemoved(int index, int count); + +Q_SIGNALS: + void scrollBarChanged(); +}; + +/* +class DebugWindow : public QWidget +{ +public: + DebugWindow(TextEditPrivate *p) + : priv(p) + { + } + + void paintEvent(QPaintEvent *) + { + if (priv->lines.isEmpty()) + return; + + QPainter p(this); + p.fillRect(QRect(0, pixels(priv->viewportPosition), width(), + pixels(priv->lines.last().first + + priv->lines.last().second.textLength())), + Qt::black); + p.fillRect(QRect(0, pixels(priv->viewportPosition), width(), pixels(priv->layoutEnd)), Qt::red); + } + + int pixels(int pos) const + { + double fraction = double(pos) / double(priv->document->documentSize()); + return int(double(height()) * fraction); + } +private: + TextEditPrivate *priv; +}; +*/ + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,454 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "TextPagerLayout_p.hpp" +#include "TextPagerEdit_p.hpp" +#include "TextPagerDocument.hpp" +#include "TextPagerEdit.hpp" + +int TextPagerLayout::viewportWidth() const +{ + if (!lineBreaking) + return INT_MAX - 1024; + return textEdit ? textEdit->viewport()->width() : viewport; +} + +int TextPagerLayout::doLayout(int index, QList *sections) // index is in document coordinates +{ + QTextLayout *textLayout = 0; + if (!unusedTextLayouts.isEmpty()) { + textLayout = unusedTextLayouts.takeLast(); + textLayout->clearAdditionalFormats(); + } else { + textLayout = new QTextLayout; + textLayout->setCacheEnabled(true); + textLayout->setFont(font); + QTextOption option; + option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + textLayout->setTextOption(option); + } + textLayouts.append(textLayout); + if (index != 0 && bufferReadCharacter(index - 1) != '\n') { + qWarning() << index << viewportPosition << document->read(index - 1, 20) + << bufferReadCharacter(index - 1); + } + Q_ASSERT(index == 0 || bufferReadCharacter(index - 1) == '\n'); + const int max = bufferPosition + buffer.size(); + const int lineStart = index; + while (index < max && bufferReadCharacter(index) != '\n') + ++index; + + const QString string = buffer.mid(lineStart - bufferPosition, index - lineStart); + Q_ASSERT(string.size() == index - lineStart); + Q_ASSERT(!string.contains('\n')); + if (index < max) + ++index; // for the newline + textLayout->setText(string); + + QMultiMap formatMap; + if (sections) { + do { + Q_ASSERT(!sections->isEmpty()); + TextPagerSection *l = sections->first(); + Q_ASSERT(::matchSection(l, textEdit)); + Q_ASSERT(l->position() + l->size() >= lineStart); + if (l->position() >= index) { + break; + } + // section is in this QTextLayout + QTextLayout::FormatRange range; + range.start = qMax(0, l->position() - lineStart); // offset in QTextLayout + range.length = qMin(l->position() + l->size(), index) - lineStart - range.start; + range.format = l->format(); + formatMap.insertMulti(l->priority(), range); + if (l->position() + l->size() >= index) { // > ### ??? + // means section didn't end here. It continues in the next QTextLayout + break; + } + sections->removeFirst(); + } while (!sections->isEmpty()); + } + QList formats = formatMap.values(); + + int leftMargin = LeftMargin; + int rightMargin = 0; + int topMargin = 0; + int bottomMargin = 0; + Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { + syntaxHighlighter->d->currentBlockPosition = lineStart; + syntaxHighlighter->d->formatRanges.clear(); + syntaxHighlighter->d->currentBlock = string; + syntaxHighlighter->highlightBlock(string); + syntaxHighlighter->d->currentBlock.clear(); + if (syntaxHighlighter->d->blockFormat.isValid()) { + blockFormats[textLayout] = syntaxHighlighter->d->blockFormat; + if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockLeftMargin)) + leftMargin = syntaxHighlighter->d->blockFormat.leftMargin(); + if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockRightMargin)) + rightMargin = syntaxHighlighter->d->blockFormat.rightMargin(); + if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockTopMargin)) + topMargin = syntaxHighlighter->d->blockFormat.topMargin(); + if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockBottomMargin)) + bottomMargin = syntaxHighlighter->d->blockFormat.bottomMargin(); + + } + syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState; + if (!syntaxHighlighter->d->formatRanges.isEmpty()) + formats += syntaxHighlighter->d->formatRanges; + } + textLayout->setAdditionalFormats(formats); + textLayout->beginLayout(); + const int lineWidth = viewportWidth() - (leftMargin + rightMargin); + + int localWidest = -1; + Q_FOREVER { + QTextLine line = textLayout->createLine(); + if (!line.isValid()) { + break; + } + line.setLineWidth(lineWidth); + if (!lineBreaking) + localWidest = qMax(localWidest, line.naturalTextWidth() + (LeftMargin * 2)); + // ### support blockformat margins etc + int y = topMargin + lastBottomMargin; + if (!lines.isEmpty()) { + y += int(lines.last().second.rect().bottom()); + // QTextLine doesn't seem to get its rect() update until a + // new line has been created (or presumably in endLayout) + } + line.setPosition(QPoint(leftMargin, y)); + lines.append(qMakePair(lineStart + line.textStart(), line)); + } + + widest = qMax(widest, localWidest); + lastBottomMargin = bottomMargin; + + textLayout->endLayout(); +#ifndef QT_NO_DEBUG + for (int i=1; iboundingRect().toRect(); + // this will actually take the entire width set in setLineWidth + // and not what it actually uses. + r.setWidth(localWidest); + + contentRect |= r; + Q_ASSERT(!lineBreaking || contentRect.right() <= qint64(viewportWidth()) + LeftMargin + || viewportWidth() == -1); + + Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { + syntaxHighlighter->d->formatRanges.clear(); + syntaxHighlighter->d->blockFormat = QTextBlockFormat(); + syntaxHighlighter->d->currentBlockPosition = -1; + } + + return index; +} + +int TextPagerLayout::textPositionAt(const QPoint &p) const +{ + QPoint pos = p; + if (pos.x() >= 0 && pos.x() < LeftMargin) + pos.rx() = LeftMargin; // clicking in the margin area should count as the first characters + + int textLayoutOffset = viewportPosition; + Q_FOREACH(const QTextLayout *l, textLayouts) { + if(l->boundingRect().y() <= pos.y() && l->boundingRect().bottom() >=pos.y()) { + //if (l->boundingRect().toRect().contains(pos)) { + const int lineCount = l->lineCount(); + for (int i=0; ilineAt(i); + if (line.y() <= pos.y() && pos.y() <= line.height() + line.y()) { // ### < ??? + { + if(pos.x() > l->boundingRect().right()) + pos.setX(l->boundingRect().right()-1); + + return textLayoutOffset + line.xToCursor(qMax(LeftMargin, pos.x())); + } + } + } + } + textLayoutOffset += l->text().size() + 1; // + 1 for newlines which aren't in the QTextLayout + } + return -1; +} + +QList TextPagerLayout::relayoutCommon() +{ +// widest = -1; // ### should this be relative to current content or remember? What if you remove the line that was the widest? + Q_ASSERT(layoutDirty); + layoutDirty = false; + Q_ASSERT(document); + lines.clear(); + unusedTextLayouts = textLayouts; + textLayouts.clear(); + contentRect = QRect(); + visibleLines = lastVisibleCharacter = -1; + + Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { + syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState = -1; + } + + if (viewportPosition < bufferPosition + || (bufferPosition + buffer.size() < document->documentSize() + && buffer.size() - bufferOffset() < MinimumBufferSize)) { + bufferPosition = qMax(0, viewportPosition - MinimumBufferSize); + buffer = document->read(bufferPosition, int(MinimumBufferSize * 2.5)); + sections = document->d->getSections(bufferPosition, buffer.size(), TextPagerSection::IncludePartial, textEdit); + } else if (sectionsDirty) { + sections = document->d->getSections(bufferPosition, buffer.size(), TextPagerSection::IncludePartial, textEdit); + } + sectionsDirty = false; + QList l = sections; + while (!l.isEmpty() && l.first()->position() + l.first()->size() < viewportPosition) + l.takeFirst(); // could cache these as well + return l; +} + +void TextPagerLayout::relayoutByGeometry(int height) +{ + if (!layoutDirty) + return; + + QList l = relayoutCommon(); + + const int max = viewportPosition + buffer.size() - bufferOffset(); // in document coordinates + ASSUME(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n'); + + static const int extraLines = qMax(2, qgetenv("LAZYTEXTEDIT_EXTRA_LINES").toInt()); + int index = viewportPosition; + while (index < max) { + index = doLayout(index, l.isEmpty() ? 0 : &l); + Q_ASSERT(index == max || document->readCharacter(index - 1) == '\n'); + Q_ASSERT(!textLayouts.isEmpty()); + const int y = int(textLayouts.last()->boundingRect().bottom()); + if (y >= height) { + if (visibleLines == -1) { + visibleLines = lines.size(); + lastVisibleCharacter = index; + } else if (lines.size() >= visibleLines + extraLines) { + break; + } + } + } + if (visibleLines == -1) { + visibleLines = lines.size(); + lastVisibleCharacter = index; + } + + + layoutEnd = qMin(index, max); + qDeleteAll(unusedTextLayouts); + unusedTextLayouts.clear(); + Q_ASSERT(viewportPosition < layoutEnd || + (viewportPosition == layoutEnd && viewportPosition == document->documentSize())); +// qDebug() << "layoutEnd" << layoutEnd << "viewportPosition" << viewportPosition; +} + +void TextPagerLayout::relayoutByPosition(int size) +{ + if (!layoutDirty) + return; + + QList l = relayoutCommon(); + + const int max = viewportPosition + qMin(size, buffer.size() - bufferOffset()); + Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n'); + int index = viewportPosition; + while (index < max) { + index = doLayout(index, l.isEmpty() ? 0 : &l); + } + layoutEnd = index; + + qDeleteAll(unusedTextLayouts); + unusedTextLayouts.clear(); + Q_ASSERT(viewportPosition < layoutEnd || + (viewportPosition == layoutEnd && viewportPosition == document->documentSize())); +} + +void TextPagerLayout::relayout() +{ + //relayoutByPosition(2000); // ### totally arbitrary number + relayoutByPosition(1.5*MinimumBufferSize); +} + +QTextLayout *TextPagerLayout::layoutForPosition(int pos, int *offset, int *index) const +{ + if (offset) + *offset = -1; + if (index) + *index = -1; + + if (textLayouts.isEmpty() || pos < viewportPosition || pos > layoutEnd) { + return 0; + } + + int textLayoutOffset = viewportPosition; + int i = 0; + + Q_FOREACH(QTextLayout *l, textLayouts) { + if (pos >= textLayoutOffset && pos <= l->text().size() + textLayoutOffset) { + if (offset) + *offset = pos - textLayoutOffset; + if (index) + *index = i; + return l; + } + ++i; + textLayoutOffset += l->text().size() + 1; + } + return 0; +} + +QTextLine TextPagerLayout::lineForPosition(int pos, int *offsetInLine, int *lineIndex, bool *lastLine) const +{ + if (offsetInLine) + *offsetInLine = -1; + if (lineIndex) + *lineIndex = -1; + if (lastLine) + *lastLine = false; + + if (pos < viewportPosition || pos >= layoutEnd || textLayouts.isEmpty() || lines.isEmpty()) { + return QTextLine(); + } + int layoutIndex = 0; + QTextLayout *layout = textLayouts.value(layoutIndex); + Q_ASSERT(layout); + for (int i=0; i &line = lines.at(i); + int lineEnd = line.first + line.second.textLength(); + const bool last = line.second.lineNumber() + 1 == layout->lineCount(); + if (last) { + ++lineEnd; + // 1 is for newline characters + layout = textLayouts.value(++layoutIndex); + // could be 0 + } + if (pos < lineEnd) { + if (offsetInLine) { + *offsetInLine = pos - line.first; + Q_ASSERT(*offsetInLine >= 0); + Q_ASSERT(*offsetInLine < lineEnd + pos); + } + if (lineIndex) { + *lineIndex = i; + } + if (lastLine) + *lastLine = last; + return line.second; + } else if (!layout) { + break; + } + } + qWarning() << "Couldn't find a line for" << pos << "viewportPosition" << viewportPosition + << "layoutEnd" << layoutEnd; + Q_ASSERT(0); + return QTextLine(); +} + +// pos is not necessarily on a newline. Finds closest newline in the +// right direction and sets viewportPosition to that. Updates +// scrollbars if this is a TextEditPrivate + +void TextPagerLayout::updateViewportPosition(int pos, Direction direction,bool applyIt) +{ + pos = qMin(pos, maxViewportPosition); + if (document->documentSize() == 0) { + viewportPosition = 0; + } else { + Q_ASSERT(document->documentSize() > 0); + int index = document->find('\n', qMax(0, pos + (direction == Backward ? -1 : 0)), + TextPagerDocument::FindMode(direction)).anchor(); + if (index == -1) { + if (direction == Backward) { + index = 0; + } else { + index = qMax(0, document->find('\n', document->documentSize() - 1, TextPagerDocument::FindBackward).position()); + // position after last newline in document + // if there is no newline put it at 0 + } + } else { + ++index; + } + Q_ASSERT(index != -1); + viewportPosition = index; + + if (viewportPosition != 0 && document->read(viewportPosition - 1, 1) != QString("\n")) + qWarning() << "viewportPosition" << viewportPosition << document->read(viewportPosition - 1, 10) << this; + ASSUME(viewportPosition == 0 || document->read(viewportPosition - 1, 1) == QString("\n")); + } + if (viewportPosition > maxViewportPosition && direction == Forward) { + updateViewportPosition(viewportPosition, Backward); + return; + } + layoutDirty = true; + + if(applyIt) { + + if (textEdit && !suppressTextEditUpdates) { + textEdit->viewport()->update(); + TextEditPrivate *p = static_cast(this); + p->pendingScrollBarUpdate = true; + p->updateCursorPosition(p->lastHoverPos); + if (!textEdit->verticalScrollBar()->isSliderDown()) { + p->updateScrollBar(); + } // sliderReleased is connected to updateScrollBar() + } + + relayout(); + } +} + +#ifndef QT_NO_DEBUG_STREAM +QDebug &operator<<(QDebug &str, const QTextLine &line) +{ + if (!line.isValid()) { + str << "QTextLine() (invalid)"; + return str; + } + str.space() << "lineNumber" << line.lineNumber() + << "textStart" << line.textStart() + << "textLength" << line.textLength() + << "position" << line.position(); + return str; +} + +QString TextPagerLayout::dump() const +{ + QString out; + QTextStream ts(&out, QIODevice::WriteOnly); + ts << "viewportPosition " << viewportPosition + << " layoutEnd " << layoutEnd + << " viewportWidth " << viewportWidth() << '\n'; + for (int i=0; ilineCount(); ++j) { + QTextLine line = layout->lineAt(j); + ts << layout->text().mid(line.textStart(), line.textLength()); + if (j + 1 < layout->lineCount()) { + ts << "\n"; + } else { + ts << "\n"; + } + } + } + return out; +} + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerLayout_p.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,125 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERLAYOUT_P_HPP_ +#define TEXTPAGERLAYOUT_P_HPP_ + +#include +#include +#include +#include +#include +#include +#include +#ifndef QT_NO_DEBUG_STREAM +#include +#endif +#include "TextPagerDocument.hpp" +#include "TextPagerEdit.hpp" +#include "syntaxhighlighter.hpp" +#include "WeakPointer.hpp" + +#ifndef QT_NO_DEBUG_STREAM +QDebug &operator<<(QDebug &str, const QTextLine &line); +#endif + +class TextDocumentBuffer +{ +public: + TextDocumentBuffer(TextPagerDocument *doc) : document(doc), bufferPosition(0) {} + virtual ~TextDocumentBuffer() {} + + inline QString bufferRead(int from, int size) const + { + Q_ASSERT(document); + if (from < bufferPosition || from + size > bufferPosition + buffer.size()) { + return document->read(from, size); + } + return buffer.mid(from - bufferPosition, size); + } + inline QChar bufferReadCharacter(int index) const // document coordinates + { + Q_ASSERT(document); + if (index >= bufferPosition && index < bufferPosition + buffer.size()) { + return buffer.at(index - bufferPosition); + } else { + Q_ASSERT(index >= 0 && index < document->documentSize()); // what if index == documentSize? + return document->readCharacter(index); + } + } + + TextPagerDocument *document; + int bufferPosition; + QString buffer; +}; + +class TextPagerEdit; +class TextPagerLayout : public TextDocumentBuffer +{ +public: + enum { MinimumBufferSize = 90000, LeftMargin = 3 }; + TextPagerLayout(TextPagerDocument *doc = 0) + : TextDocumentBuffer(doc), textEdit(0), + viewportPosition(0), layoutEnd(-1), viewport(-1), + visibleLines(-1), lastVisibleCharacter(-1), lastBottomMargin(0), + widest(-1), maxViewportPosition(0), layoutDirty(true), sectionsDirty(true), + lineBreaking(false), suppressTextEditUpdates(false) + { + } + + virtual ~TextPagerLayout() + { + qDeleteAll(textLayouts); + qDeleteAll(unusedTextLayouts); + } + + TextPagerEdit *textEdit; + QList syntaxHighlighters; + int viewportPosition, layoutEnd, viewport, visibleLines, + lastVisibleCharacter, lastBottomMargin, widest, maxViewportPosition; + bool layoutDirty, sectionsDirty, lineBreaking, suppressTextEditUpdates; + QList textLayouts, unusedTextLayouts; + QHash blockFormats; + QList extraSelections; + QList > lines; // int is start position of line in document coordinates + QRect contentRect; // contentRect means the laid out area, not just the area currently visible + QList sections; // these are all the sections in the buffer. Some might be before the current viewport + QFont font; + + QList relayoutCommon(); // should maybe be smarter about MinimumScreenSize. Detect it based on font and viewport size + void relayoutByPosition(int size); + void relayoutByGeometry(int height); + virtual void relayout(); + + int viewportWidth() const; + + int doLayout(int index, QList *sections); + + QTextLine lineForPosition(int pos, int *offsetInLine = 0, + int *lineIndex = 0, bool *lastLine = 0) const; + QTextLayout *layoutForPosition(int pos, int *offset = 0, int *index = 0) const; + + int textPositionAt(const QPoint &pos) const; + inline int bufferOffset() const { return viewportPosition - bufferPosition; } + + QString dump() const; + + enum Direction { + Forward = 0, + Backward = TextPagerDocument::FindBackward + }; + void updateViewportPosition(int pos, Direction direction,bool applyIt=true); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,145 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TextPagerSearchHighlighter.hpp" + +#include "VConfig.hpp" +#include "VProperty.hpp" + +#include + +QColor TextPagerSearchHighlighter::bgColour_=QColor(200, 255, 200); + +TextPagerSearchHighlighter::TextPagerSearchHighlighter(QObject *parent) : + SyntaxHighlighter(parent), + mode_(NoMode), + caseSensitive_(false), + wholeWords_(false) + { + if(VProperty *p=VConfig::instance()->find("panel.search.highlightColour")) + { + bgColour_=p->value().value(); + } + + format_.setBackground(Qt::yellow); + + rx_=QRegExp("(server)"); + } + +//from QRegExp +bool TextPagerSearchHighlighter::isWordCharacter(const QChar& ch) const +{ + return ch.isLetterOrNumber() || ch.isMark() || ch == QLatin1Char('_'); +} + +void TextPagerSearchHighlighter::highlightBlock(const QString &string) +{ + if(string.simplified().isEmpty()) + return; + + if(mode_ == RegexpMode) + { + if(rx_.isEmpty()) return; + int index=0; + while((index = rx_.indexIn(string, index)) != -1) { + + if(rx_.matchedLength() == 0) + return; + setFormat(index, rx_.matchedLength(), format_); + index+=rx_.matchedLength(); + } + } + else if(mode_ == TextMode) + { + if(text_.isEmpty()) return; + int index=0; + while((index = string.indexOf(text_,index, + caseSensitive_?Qt::CaseSensitive:Qt::CaseInsensitive)) != -1) + { + bool found=true; + if(wholeWords_) + { + if(index>0) + found=!isWordCharacter(string.at(index-1)); + if(found && index + text_.size() < string.size()) + found=!isWordCharacter(string.at(index+text_.size())); + } + if(found) + setFormat(index, text_.size(), format_); + + index+=string.size(); + } + } +} + +void TextPagerSearchHighlighter::reset(QString txt,TextPagerDocument::FindMode mode,bool apply) +{ + bool changed=false; + if(mode_ != TextMode) + { + mode_=TextMode; + rx_=QRegExp(); + changed=true; + } + + if(txt != text_) + { + text_=txt; + changed=true; + } + + bool cs=mode & TextPagerDocument::FindCaseSensitively; + if(cs != caseSensitive_) + { + caseSensitive_=cs; + changed=true; + } + + bool ww=mode & TextPagerDocument::FindWholeWords; + if(ww != wholeWords_) + { + wholeWords_=ww; + changed=true; + } + + if(changed && apply) + rehighlight(); + +} + +void TextPagerSearchHighlighter::reset(QRegExp rx,TextPagerDocument::FindMode mode,bool apply) +{ + bool changed=false; + if(mode_ != RegexpMode) + { + mode_=RegexpMode; + text_.clear(); + changed=true; + } + + if(rx_.pattern() != rx.pattern() || rx_.caseSensitivity() != rx.caseSensitivity() || + rx_.patternSyntax() != rx.patternSyntax()) + { + rx_=rx; + changed=true; + } + + if(changed && apply) + rehighlight(); +} + +void TextPagerSearchHighlighter::clear() +{ + rx_=QRegExp(); + text_.clear(); + rehighlight(); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchHighlighter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,43 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ +#define VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ + +#include "syntaxhighlighter.hpp" + +#include "TextPagerDocument.hpp" + +#include +#include + +class TextPagerSearchHighlighter : public SyntaxHighlighter +{ +public: + TextPagerSearchHighlighter(QObject *parent=0); + virtual void highlightBlock(const QString &string); + void reset(QString txt,TextPagerDocument::FindMode mode,bool apply); + void reset(QRegExp rx,TextPagerDocument::FindMode mode, bool apply); + void clear(); + enum Mode {NoMode,TextMode,RegexpMode}; + +protected: + bool isWordCharacter(const QChar& ch) const; + + Mode mode_; + QRegExp rx_; + QString text_; + QTextCharFormat format_; + bool caseSensitive_; + bool wholeWords_; + static QColor bgColour_; +}; + +#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,252 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#include + +#include "TextPager/TextPagerSearchInterface.hpp" + +#include "TextPagerEdit.hpp" +#include "UserMessage.hpp" + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +TextPagerCursor::MoveOperation TextPagerSearchInterface::translateCursorMoveOp(QTextCursor::MoveOperation move) +{ + switch (move) + { + case QTextCursor::NoMove: return TextPagerCursor::NoMove; + case QTextCursor::Start: return TextPagerCursor::Start; + case QTextCursor::StartOfLine: return TextPagerCursor::StartOfLine; + case QTextCursor::StartOfWord: return TextPagerCursor::StartOfWord; + case QTextCursor::PreviousWord: return TextPagerCursor::PreviousWord; + case QTextCursor::End: return TextPagerCursor::End; + default: + { + assert(0); + return TextPagerCursor::NoMove; + } + } +} + + +bool TextPagerSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, + QTextCursor::MoveOperation move, int iteration, StringMatchMode::Mode matchMode) +{ + if(!editor_) + return false; + + if(editor_->document()->documentSize() == 0) + return false; + + bool doSearch=true; + if(str.simplified().isEmpty()) + { + doSearch=false; + } + + TextPagerCursor cursor(editor_->textCursor()); + + cursor.movePosition(translateCursorMoveOp(move)); // move the cursor? + + TextPagerDocument::FindMode mode=TextPagerDocument::FindWrap; + + if(flags & QTextDocument::FindCaseSensitively) + mode |= TextPagerDocument::FindCaseSensitively; + + if(flags & QTextDocument::FindBackward) + mode |= TextPagerDocument::FindBackward; + + if(flags & QTextDocument::FindWholeWords) + mode |= TextPagerDocument::FindWholeWords; + + bool found = false; + + Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; + + if(doSearch) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + } + + switch (matchMode) + { + case StringMatchMode::ContainsMatch: + { + editor_->setSearchHighlighter(str,mode); + + if(doSearch) + { + cursor = editor_->document()->find(str, cursor, mode); // perform the search + found = (!cursor.isNull()); + } + break; + } + case StringMatchMode::WildcardMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + regexp.setPatternSyntax(QRegExp::Wildcard); + + editor_->setSearchHighlighter(regexp,mode); + + if(doSearch) + { + cursor = editor_->document()->find(regexp, cursor, mode); // perform the search } + found = (!cursor.isNull()); + } + break; + } + case StringMatchMode::RegexpMatch: + { + QRegExp regexp(str); + regexp.setCaseSensitivity(cs); + + editor_->setSearchHighlighter(regexp,mode); + + if(doSearch) + { + cursor = editor_->document()->find(regexp, cursor, mode); // perform the search + found = (!cursor.isNull()); + } + break; + } + + default: + break; + } + + if(found) + { + editor_->setTextCursor(cursor); // mark the selection of the match + editor_->ensureCursorVisible(); + cursor.movePosition(TextPagerCursor::StartOfLine); + } + + if(doSearch) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + } + + return found; +} + +void TextPagerSearchInterface::automaticSearchForKeywords(bool userClickedReload) +{ + if(editor_->document()->documentSize() ==0) + return; + + bool performSearch = vpPerformAutomaticSearch_->value().toBool(); + + if (performSearch) + { + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + + // search direction + QTextDocument::FindFlags findFlags; + TextPagerCursor cursor(editor_->textCursor()); + std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); + QTextCursor::MoveOperation move; + + if (searchFrom == "bottom") + { + findFlags = QTextDocument::FindBackward; + move = QTextCursor::End; + } + else + { + move = QTextCursor::Start; + } + + // case sensitivity + bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); + if (caseSensitive) + findFlags = findFlags | QTextDocument::FindCaseSensitively; + + // string match mode + std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); + StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); + + // the term to be searched for + std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); + QString searchTerm = QString::fromStdString(searchTerm_s); + + // perform the search + bool found = findString (searchTerm, false, findFlags, move, 1, mode); + + if(!found) + { + if(userClickedReload) + { + // move the cursor to the start of the last line + gotoLastLine(); + } + } + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + + } + else + { + // move the cursor to the start of the last line + gotoLastLine(); + } +} + +void TextPagerSearchInterface::refreshSearch() +{ + if(!editor_) + return; + + TextPagerCursor cursor(editor_->textCursor()); + if (cursor.hasSelection()) + { + cursor.movePosition(TextPagerCursor::StartOfLine, TextPagerCursor::MoveAnchor); + editor_->setTextCursor(cursor); + } +} + + +void TextPagerSearchInterface::clearHighlights() +{ + if(editor_) + editor_->clearSearchHighlighter(); +} + +void TextPagerSearchInterface::enableHighlights() +{ + if(editor_) + editor_->setEnableSearchHighlighter(true); +} + +void TextPagerSearchInterface::disableHighlights() +{ + if(editor_) + editor_->setEnableSearchHighlighter(false); +} + +void TextPagerSearchInterface::gotoLastLine() +{ + // move the cursor to the start of the last line + TextPagerCursor cursor = editor_->textCursor(); + cursor.movePosition(TextPagerCursor::End); + cursor.movePosition(TextPagerCursor::StartOfLine); + editor_->setTextCursor(cursor); + editor_->ensureCursorVisible(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSearchInterface.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ +#define VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ + +#include "AbstractTextEditSearchInterface.hpp" +#include "TextPagerCursor.hpp" + +class TextPagerEdit; + +class TextPagerSearchInterface : public AbstractTextEditSearchInterface +{ +public: + TextPagerSearchInterface() : editor_(NULL) {} + void setEditor(TextPagerEdit* e) {editor_=e;} + + bool findString (QString str, bool highlightAll, QTextDocument::FindFlags findFlags, + QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); + + void automaticSearchForKeywords(bool); + void refreshSearch(); + void clearHighlights(); + void disableHighlights(); + void enableHighlights(); + bool highlightsNeedSearch() {return false;} + void gotoLastLine(); + +protected: + TextPagerCursor::MoveOperation translateCursorMoveOp(QTextCursor::MoveOperation move); + TextPagerEdit *editor_; + +}; + +#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "TextPagerSection.hpp" +#include "TextPagerDocument.hpp" +#include "TextPagerDocument_p.hpp" + +TextPagerSection::~TextPagerSection() +{ + if (d.document) + d.document->takeTextSection(this); +} + +QString TextPagerSection::text() const +{ + Q_ASSERT(d.document); + return d.document->read(d.position, d.size); +} + +void TextPagerSection::setFormat(const QTextCharFormat &format) +{ + Q_ASSERT(d.document); + d.format = format; + Q_EMIT d.document->d->sectionFormatChanged(this); +} + +QCursor TextPagerSection::cursor() const +{ + return d.cursor; +} + +void TextPagerSection::setCursor(const QCursor &cursor) +{ + d.cursor = cursor; + d.hasCursor = true; + Q_EMIT d.document->d->sectionCursorChanged(this); +} + +void TextPagerSection::resetCursor() +{ + d.hasCursor = false; + d.cursor = QCursor(); + Q_EMIT d.document->d->sectionCursorChanged(this); +} + +bool TextPagerSection::hasCursor() const +{ + return d.hasCursor; +} + +void TextPagerSection::setPriority(int priority) +{ + d.priority = priority; + Q_EMIT d.document->d->sectionFormatChanged(this); // ### it hasn't really but I to need it dirtied +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,75 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERSECTION_HPP__ +#define TEXTPAGERSECTION_HPP__ + +#include +#include +#include +#include + +class TextPagerDocument; +class TextPagerEdit; +class TextPagerSection +{ +public: + enum TextSectionOption { + IncludePartial = 0x01 + }; + Q_DECLARE_FLAGS(TextSectionOptions, TextSectionOption); + + ~TextPagerSection(); + QString text() const; + int position() const { return d.position; } + int size() const { return d.size; } + QTextCharFormat format() const { return d.format; } + void setFormat(const QTextCharFormat &format); + QVariant data() const { return d.data; } + void setData(const QVariant &data) { d.data = data; } + TextPagerDocument *document() const { return d.document; } + TextPagerEdit *textEdit() const { return d.textEdit; } + QCursor cursor() const; + void setCursor(const QCursor &cursor); + void resetCursor(); + bool hasCursor() const; + int priority() const { return d.priority; } + void setPriority(int priority); +private: + struct Data { + Data(int p, int s, TextPagerDocument *doc, const QTextCharFormat &f, const QVariant &d) + : position(p), size(s), priority(0), document(doc), textEdit(0), format(f), data(d), hasCursor(false) + {} + int position, size, priority; + TextPagerDocument *document; + TextPagerEdit *textEdit; + QTextCharFormat format; + QVariant data; + QCursor cursor; + bool hasCursor; + } d; + + TextPagerSection(int pos, int size, TextPagerDocument *doc, const QTextCharFormat &format, const QVariant &data) + : d(pos, size, doc, format, data) + {} + + friend class TextPagerDocument; + friend class TextDocumentPrivate; + friend class TextPagerEdit; +}; + +Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerSection::TextSectionOptions); + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection_p.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection_p.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerSection_p.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerSection_p.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,34 @@ +// Copyright 2010 Anders Bakken +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef TEXTPAGERSECTION_P_HPP_ +#define TEXTPAGERSECTION_P_HPP_ + +#include +#include +class TextPagerSection; +class TextSectionManager : public QObject +{ + Q_OBJECT +public: + static TextSectionManager *instance() { static TextSectionManager *inst = new TextSectionManager; return inst; } +Q_SIGNALS: + void sectionFormatChanged(TextPagerSection *section); + void sectionCursorChanged(TextPagerSection *section); +private: + TextSectionManager() : QObject(QCoreApplication::instance()) {} + friend class TextPagerSection; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,113 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TextPagerWidget.hpp" + +#include "GotoLineDialog.hpp" + +bool add = false; + +TextPagerWidget::TextPagerWidget(QWidget *parent) : + //TextPagerEdit(parent), + doLineNumbers(true), + gotoLineDialog_(NULL) +{ + QHBoxLayout* hb=new QHBoxLayout(this); + hb->setContentsMargins(0,0,0,0); + hb->setSpacing(0); + + textEditor_=new TextPagerEdit(this); + lineNumArea_ = new TextPagerLineNumberArea(textEditor_); + + hb->addWidget(lineNumArea_); + hb->addWidget(textEditor_,1); + + setAttribute(Qt::WA_MouseTracking); +} + +void TextPagerWidget::clear() +{ + textEditor_->document()->clear(); +} + +bool TextPagerWidget::load(const QString &fileName, TextPagerDocument::DeviceMode mode) +{ + return textEditor_->load(fileName, mode, NULL); +} + +void TextPagerWidget::setText(const QString &txt) +{ + textEditor_->setText(txt); +} + +void TextPagerWidget::setFontProperty(VProperty* p) +{ + textEditor_->setFontProperty(p); +} + +void TextPagerWidget::zoomIn() +{ + textEditor_->zoomIn(); +} + +void TextPagerWidget::zoomOut() +{ + textEditor_->zoomOut(); +} +// --------------------------------------------------------------------------- +// TextEdit::gotoLine +// triggered when the user asks to bring up the 'go to line' dialog +// --------------------------------------------------------------------------- + +void TextPagerWidget::gotoLine() +{ + // create the dialog if it does not already exist + + if (!gotoLineDialog_) + { + gotoLineDialog_ = new GotoLineDialog(this); + + connect(gotoLineDialog_, SIGNAL(gotoLine(int)), this, SLOT(gotoLine(int))); + } + + // if created, set it up and display it + + if (gotoLineDialog_) + { + gotoLineDialog_->show(); + gotoLineDialog_->raise(); + gotoLineDialog_->activateWindow(); + gotoLineDialog_->setupUIBeforeShow(); + } +} + +void TextPagerWidget::gotoLine(int line) +{ + textEditor_->gotoLine(line); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/TextPagerWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/TextPagerWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,53 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_TEXTPAGERWIDGET_HPP_ +#define VIEWER_SRC_TEXTPAGERWIDGET_HPP_ + +#include "TextPagerEdit.hpp" + +class GotoLineDialog; + +class TextPagerWidget : public QWidget //TextPagerEdit +{ + Q_OBJECT +public: + TextPagerWidget(QWidget *parent = 0); + + TextPagerEdit* textEditor() const {return textEditor_;} + void clear(); + bool load(const QString &fileName, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse); + void setText(const QString& txt); + + void setFontProperty(VProperty* p); + void zoomIn(); + void zoomOut(); + void gotoLine(); + + //void mouseMoveEvent(QMouseEvent *e); + //void timerEvent(QTimerEvent *e); +protected Q_SLOTS: + void gotoLine(int); + +Q_SIGNALS: + void cursorCharacter(const QChar &ch); + +private: + + bool doLineNumbers; + QBasicTimer appendTimer, changeSelectionTimer; + TextPagerEdit *textEditor_; + + TextPagerLineNumberArea *lineNumArea_; + GotoLineDialog *gotoLineDialog_; +}; + + +#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/WeakPointer.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/WeakPointer.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TextPager/WeakPointer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TextPager/WeakPointer.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,16 @@ +#ifndef WEAKPOINTER_H +#define WEAKPOINTER_H + +#include + +#if (QT_VERSION < QT_VERSION_CHECK(4, 6, 0)) +// while QWeakPointer existed in Qt 4.5 it lacked certain APIs (like +// data so I'll stick with QPointer until 4.6) +#include +#define WeakPointer QPointer +#else +#include +#define WeakPointer QWeakPointer +#endif + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "LineEdit.hpp" + +#include "TimeItemWidget.hpp" + +#include "Node.hpp" + +//======================================================== +// +// TimeItemWidget +// +//======================================================== + +TimeItemWidget::TimeItemWidget(QWidget *parent) : QWidget(parent) +{ + setupUi(this); + +} + +QWidget* TimeItemWidget::realWidget() +{ + return this; +} + +void TimeItemWidget::reload(VInfo_ptr nodeInfo) +{ + active_=true; +} + +void TimeItemWidget::clearContents() +{ + InfoPanelItem::clear(); +} + + +static InfoPanelItemMaker maker1("time"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,45 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TIMEITEMWIDGET_HPP_ +#define TIMEITEMWIDGET_HPP_ +#include "ui_TimeItemWidget.h" + +#include + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" + +class LineEdit; + +class TimeItemWidget + : public QWidget + , public InfoPanelItem + , protected Ui::TimeItemWidget + +{ +public: + explicit TimeItemWidget(QWidget *parent=0); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + void nodeChanged(const VNode*, const std::vector&) {} + void defsChanged(const std::vector&) {} + + QGraphicsView* view() { return graphicsView; } + +protected: + void updateState(const ChangeFlags&) {} +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TimeItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TimeItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,206 @@ + + + TimeItemWidget + + + + 0 + 0 + 859 + 621 + + + + Form + + + + + + 0 + + + + + %ECF_LOG% + + + + + + + All + + + filterGroup + + + + + + + Tasks + + + true + + + filterGroup + + + + + + + TimeSort + + + true + + + sortGroup + + + + + + + NameSort + + + sortGroup + + + + + + + + 2015 + 1 + 1 + + + + + + + + + 2100 + 1 + 1 + + + + + + + + + 16 + 16 + + + + + + + + + + M + + + + + + + + 16 + 16 + + + + + + + + + + L + + + + + + + + 16 + 16 + + + + + + + + + + U + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + View options + + + Detached + + + + :/viewer/images/configure.svg:/viewer/images/configure.svg + + + true + + + true + + + + + + + + + + + + + + + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1419 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TreeNodeModel.hpp" + +#include + +#include "ConnectState.hpp" +#include "VFilter.hpp" +#include "ServerFilter.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "VFilter.hpp" +#include "VNState.hpp" +#include "VSState.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" +#include "VIcon.hpp" +#include "VFileInfo.hpp" +#include "VModelData.hpp" +#include "VTree.hpp" + +//#define _UI_TREENODEMODEL_DEBUG + +//======================================= +// +// TreeNodeModel +// +//======================================= + +TreeNodeModel::TreeNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef, + AttributeFilter *atts,IconFilter* icons,QObject *parent) : + AbstractNodeModel(parent), + data_(0), + atts_(atts), + icons_(icons), + serverToolTip_(true), + nodeToolTip_(true), + attributeToolTip_(true) +{ + //Create the data handler for the tree model. + data_=new VTreeModelData(filterDef,atts_,this); + + //Reset the data handler + data_->reset(serverFilter); + + //Icon filter changes + connect(icons_,SIGNAL(changed()), + this,SIGNAL(rerender())); + +} + +VModelData* TreeNodeModel::data() const +{ + return data_; +} + + +int TreeNodeModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return 1; +} + +int TreeNodeModel::rowCount( const QModelIndex& parent) const +{ + //There is no data at all + if(!hasData()) + { + return 0; + } + //We use only column 0 + else if(parent.column() > 0) + { + return 0; + } + //"parent" is the root + else if(!parent.isValid()) + { + return data_->count(); + } + //"parent" is a server + else if(isServer(parent)) + { + if(VTreeServer *server=indexToServer(parent)) + { + if(server->inScan()) + return 0; + else + return server->tree()->attrNum(atts_)+server->tree()->numOfChildren(); + } + } + //"parent" is a node + else if(VTreeNode* parentNode=indexToNode(parent)) + { + return parentNode->attrNum(atts_)+parentNode->numOfChildren(); + } + + return 0; +} + +Qt::ItemFlags TreeNodeModel::flags ( const QModelIndex & index) const +{ + Qt::ItemFlags defaultFlags; + + defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + return defaultFlags; +} + +//This function can be called billions of times, so it has to be extremely fast. +QVariant TreeNodeModel::data(const QModelIndex& index, int role ) const +{ + //Data lookup can be costly so we only enter the function for the valid cases + if(index.isValid() && (role > Qt::UserRole || role == Qt::DisplayRole || + role == Qt::ToolTipRole || role == Qt::BackgroundRole || + role == Qt::ForegroundRole)) + { + //At this point we know that the index is valid so we do not need to + //check it anymore. + + //This role is called millions of times when expanding or + //relayouting huge trees. So has to be HIGHLY optimised!!!!! + if(role == AttributeLineRole) + { + if(VTreeNode* node=indexToAttrParentNode(index)) + { + VNode *vnode=node->vnode(); + Q_ASSERT(vnode); + if(VAttribute* a=vnode->attribute(index.row(),atts_)) + { + return a->lineNum(); + } + } + return 0; + } + + //Identifies server + else if(role == ServerRole) + { + if(isServerForValid(index)) + return 0; + else + return -1; + } + + else if(role == AttributeRole) + { + return isAttribute(index); + } + + //Server + else if(isServerForValid(index)) + { + return serverData(index,role); + } + + //If we are here we can be sure that the index in not a server! + + //We only continue for the relevant roles for nodes and attributes + if(role == InfoRole || role == LoadRole) + { + return QVariant(); + } + + bool itIsANode=false; + if(VTreeNode* node=indexToAttrParentOrNode(index,itIsANode)) + { + //Attribute + if(itIsANode ==false) + { + return attributesData(index,role,node); + } + + //Node + if(itIsANode) + { + return nodeData(index,role,node); + } + } + } + + return QVariant(); +} + +QVariant TreeNodeModel::serverData(const QModelIndex& index,int role) const +{ + if(role == FilterRole) + return true; + + if(role == Qt::ToolTipRole && !serverToolTip_) + return QVariant(); + + ServerHandler *server=indexToServerHandler(index); + if(!server) + return QVariant(); + + if(index.column() == 0) + { + if(role == ServerPointerRole) + return qVariantFromValue((void *) server); + + //The colour of the server node + if(role == ConnectionRole) + return (server->connectState()->state() == ConnectState::Lost)?0:1; + + //The colour of the server node + else if(role == Qt::BackgroundRole) + return server->vRoot()->stateColour(); + + //The font colour of the server node + else if(role == Qt::ForegroundRole) + return server->vRoot()->stateFontColour(); + + //The text + else if(role == Qt::DisplayRole) + return QString::fromStdString(server->name()); + + //The number of nodes the server has + else if(role == NodeNumRole) + { + if(server->activity() != ServerHandler::LoadActivity) + { + return server->vRoot()->totalNum(); + } + return QVariant(); + } + + //Extra information about the server activity + else if(role == InfoRole) + { + switch(server->activity()) + { + case ServerHandler::LoadActivity: + return "Loading ..."; + default: + return ""; + } + } + + else if(role == LoadRole) + return (server->activity() == ServerHandler::LoadActivity); + + else if(role == IconRole) + { + if(icons_->isEmpty()) + return QVariant(); + else + return VIcon::pixmapList(server->vRoot(),icons_); + } + + //Tooltip + else if(role == Qt::ToolTipRole) + { + QString txt=server->vRoot()->toolTip(); + txt+=VIcon::toolTip(server->vRoot(),icons_); + return txt; + } + } + + return QVariant(); +} + +QVariant TreeNodeModel::nodeData(const QModelIndex& index, int role,VTreeNode* tnode) const +{ + if(role == Qt::ToolTipRole && !nodeToolTip_) + return QVariant(); + + if(!tnode) + return QVariant(); + + VNode* vnode=tnode->vnode(); + if(!vnode || !vnode->node()) + return QVariant(); + + if(role == NodePointerRole) + return qVariantFromValue((void *) vnode); + + else if(role == ConnectionRole) + { + return (vnode->server()->connectState()->state() == ConnectState::Lost)?0:1; + } + + else if(role == Qt::DisplayRole) + return vnode->name(); + + else if(role == Qt::BackgroundRole) + { + if(vnode->isSuspended()) + { + QVariantList lst; + lst << vnode->stateColour() << vnode->realStateColour(); + return lst; + } + else + return vnode->stateColour() ; + } + + else if(role == Qt::ForegroundRole) + return vnode->stateFontColour(); + + else if(role == NodeTypeRole) + { + if(vnode->isTask()) return 2; + else if(vnode->isSuite()) return 0; + else if(vnode->isFamily()) return 1; + else if(vnode->isAlias()) return 3; + return 0; + } + else if(role == NodeTypeForegroundRole) + { + return vnode->typeFontColour(); + } + else if(role == IconRole) + { + if(icons_->isEmpty()) + return QVariant(); + else + return VIcon::pixmapList(vnode,icons_); + } + else if(role == Qt::ToolTipRole) + { + QString txt=vnode->toolTip(); + txt+=VIcon::toolTip(vnode,icons_); + return txt; + } + + //The number of nodes a suite has + else if(role == NodeNumRole) + { + if(vnode->isTopLevel()) + { + if(data_->isFilterComplete()) + return vnode->server()->vRoot()->totalNumOfTopLevel(vnode); + else + return QString::number(tnode->root()->totalNumOfTopLevel(tnode)) + "/" + + QString::number(vnode->server()->vRoot()->totalNumOfTopLevel(vnode)); + } + return QVariant(); + } + + //The number of nodes a suite has + else if(role == AbortedReasonRole && vnode->isAborted()) + { + return QString::fromStdString(vnode->abortedReason()); + } + + return QVariant(); +} + +//======================================================================= +// +// Attributes data +// +//======================================================================= + +QVariant TreeNodeModel::attributesData(const QModelIndex& index, int role,VTreeNode* tnode) const +{ + if(role == Qt::ToolTipRole && !attributeToolTip_) + return QVariant(); + + if(role == Qt::BackgroundRole) + return QColor(220,220,220); + + VNode *vnode=tnode->vnode(); + Q_ASSERT(vnode); + + if(role == ConnectionRole) + { + return (vnode->server()->connectState()->state() == ConnectState::Lost)?0:1; + } + else if(role == Qt::DisplayRole) + { + if(VAttribute* a=vnode->attribute(index.row(),atts_)) + return a->data(); + else + return QStringList(); + } + else if(role == Qt::ToolTipRole) + { + if(VAttribute* a=vnode->attribute(index.row(),atts_)) + return a->toolTip(); + else + return QString(); + } + + return QVariant(); +} + +QVariant TreeNodeModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + //No header!!! + return QVariant(); +} + +QModelIndex TreeNodeModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When "parent" is the root this index refers to a server + if(!parent.isValid()) + { + //For the server the internal pointer is NULL + if(row < data_->count()) + { + return createIndex(row,column,(void*)NULL); + } + } + + //Here we must be under one of the servers + else + { + //If "parent" is a server this index refers to a topLevel node (suite). + //We set the server as an internal pointer + if(VTreeServer* server=indexToServer(parent)) + { + return createIndex(row,column,server); + } + + //If "parent" is not a server it must be a tree node. The internal pointer is the parent tree node. + else if(VTreeNode* parentNode=indexToNode(parent)) + { + return createIndex(row,column,parentNode); + } + } + return QModelIndex(); +} + +QModelIndex TreeNodeModel::parent(const QModelIndex &child) const +{ + //If "child" is a server the parent is the root + if(isServer(child)) + return QModelIndex(); + + int row=-1; + + //If the "child"'s internal pointer is a server it can be a server attribute or a topLevel node (suite) + //and the parent is this server. + if((row=data_->indexOfServer(child.internalPointer())) != -1) + { + return createIndex(row,0,(void*)NULL); + } + + //The "child" cannot be a server attribute or a topLevel node so it must be a node or an attribute. + //Its internal pointer must point to the parent node. + else if(VTreeNode *parentNode=static_cast(child.internalPointer())) + { + //The parent is a topLevel node (suite): its internal pointer is the server + if(parentNode->isTopLevel()) + { + VTreeNode* root=parentNode->parent(); + Q_ASSERT(root); + row=root->indexOfChild(parentNode); + Q_ASSERT(row >=0); + VTreeServer *ts=root->server(); + + int serverAttrNum=root->attrNum(atts_); + return createIndex(serverAttrNum+row,0,ts); + } + //The parent is a non topLevel node (non-suite): its internal pointer + //is its parent (i.e.. the grandparent) + else if(VTreeNode *grandParentNode=parentNode->parent()) + { + int num=grandParentNode->attrNum(atts_)+grandParentNode->indexOfChild(parentNode); + return createIndex(num,0,grandParentNode); + } + } + + return QModelIndex(); +} + +//---------------------------------------------- +// +// Server to index mapping and lookup +// +//---------------------------------------------- + +bool TreeNodeModel::isServer(const QModelIndex & index) const +{ + //For the servers the internal pointer is NULL + return (index.isValid() && index.internalPointer() == 0); +} + +//This has to be extrememly fast! +//When we know that the index is valid. +bool TreeNodeModel::isServerForValid(const QModelIndex & index) const +{ + //For the servers the internal pointer is NULL + return index.internalPointer() == 0; +} + +bool TreeNodeModel::isNode(const QModelIndex & index) const +{ + return (indexToNode(index) != NULL); +} + +// A node is a flat node when: +// -there are no child nodes (only attributes) +// or +// -any child node does not have children or attributes +bool TreeNodeModel::isFlatNode(const QModelIndex& index) const +{ + if(VTreeNode *node=indexToNode(index)) + { + if(node->numOfChildren() == 0) + return true; + else + { + for(int i=0; i < node->numOfChildren(); i++) + { + if(node->childAt(i)->numOfChildren() == 0) + { + if(node->childAt(i)->attrNum(atts_) > 0) + return false; + } + else + { + return false; + } + } + return true; + } + } + return false; +} + +bool TreeNodeModel::isAttribute(const QModelIndex & index) const +{ + return (index.isValid() && !isServer(index) && !isNode(index)); +} + +ServerHandler* TreeNodeModel::indexToServerHandler(const QModelIndex & index) const +{ + //For servers the internal id is a null pointer + if(index.isValid()) + { + if(index.internalPointer() == NULL) + return data_->serverHandler(index.row()); + } + + return NULL; +} + +VTreeServer* TreeNodeModel::indexToServer(const QModelIndex & index) const +{ + //For servers the internal id is a null pointer + if(index.isValid()) + { + if(index.internalPointer() == NULL) + return data_->server(index.row())->treeServer(); + } + + return NULL; +} + +VTreeServer* TreeNodeModel::nameToServer(const std::string& name) const +{ + VModelServer* ms=data_->server(name); + return (ms)?ms->treeServer():NULL; +} + +QModelIndex TreeNodeModel::serverToIndex(ServerHandler* server) const +{ + //For servers the internal id is set to their position in servers_ + 1 + int i; + if((i=data_->indexOfServer(server))!= -1) + return createIndex(i,0,(void*)NULL); + + return QModelIndex(); +} + +QModelIndex TreeNodeModel::serverToIndex(VModelServer* server) const +{ + //For servers the internal id is set to their position in servers_ + 1 + int i; + if((i=data_->indexOfServer(server))!= -1) + return createIndex(i,0,(void*)NULL); + + return QModelIndex(); +} + +//---------------------------------------------- +// +// Node to index mapping and lookup +// +//---------------------------------------------- + +//We can only call it when the index is valid! +VTreeNode* TreeNodeModel::indexToAttrParentNode(const QModelIndex & index) const +{ + void *ip; + if((ip=index.internalPointer()) == NULL) + return 0; + + //If it is not a sever ... + + //If the internal pointer is a server it is either a server attribute or a + //top level node (suite) + + if(VModelServer *mserver=data_->server(ip)) + { + VTreeServer* server=mserver->treeServer(); + //It is an attribute + if(index.row() < server->tree()->attrNum(atts_)) + { + return server->tree(); + } + } + + //Otherwise the internal pointer points to the parent node. + else if(VTreeNode *parentNode=static_cast(ip)) + { + //It is an attribute + if(index.row() < parentNode->attrNum(atts_)) + return parentNode; + } + + return 0; +} + +//We can only call it when the index is valid! +VTreeNode* TreeNodeModel::indexToAttrParentOrNode(const QModelIndex & index,bool &itIsANode) const +{ + void *ip; + if((ip=index.internalPointer()) == NULL) + return 0; + + //If it is not a sever ... + + itIsANode=false; + //If the internal pointer is a server it is either a server attribute or a + //top level node (suite) + if(VModelServer *mserver=data_->server(ip)) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + + //It is an attribute + int serverAttNum=server->tree()->attrNum(atts_); + + if(index.row() < serverAttNum) + { + return server->tree(); + } + //It is a top level node + else + { + itIsANode=true; + return server->tree()->childAt(index.row()-serverAttNum); + } + } + + //Otherwise the internal pointer points to the parent node. + else if(VTreeNode *parentNode=static_cast(ip)) + { + int attNum=parentNode->attrNum(atts_); + + //It is an attribute + if(index.row() < attNum) + { + return parentNode; + } + else + { + itIsANode=true; + return parentNode->childAt(index.row()-attNum); + } + } + + return 0; +} + +VTreeNode* TreeNodeModel::indexToServerOrNode( const QModelIndex & index) const +{ + VTreeNode* node=indexToNode(index); + if(!node) + { + if(VTreeServer* ts=indexToServer(index)) + node=ts->tree(); + } + + return node; +} + +VTreeNode* TreeNodeModel::indexToNode( const QModelIndex & index) const +{ + //If it is not a sever ... + if(index.isValid() && !isServer(index)) + { + //If the internal pointer is a server it is either a server attribute or a + //top level node (suite) + + if(VModelServer *mserver=data_->server(index.internalPointer())) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + + int serverAttNum=server->tree()->attrNum(atts_); + + //It is an attribute + if(index.row() < serverAttNum) + { + return NULL; + } + //It is a top level node + else + { + return server->tree()->childAt(index.row()-serverAttNum); + } + } + + //Otherwise the internal pointer points to the parent node. + else if(VTreeNode *parentNode=static_cast(index.internalPointer())) + { + int attNum=parentNode->attrNum(atts_); + if(index.row() >= attNum) + { + return parentNode->childAt(index.row()-attNum); + } + } + } + + return NULL; +} + +//Find the index for the node! The VNode can be a server as well!!! +QModelIndex TreeNodeModel::nodeToIndex(const VNode* node, int column) const +{ + if(!node) + return QModelIndex(); + + //This is a server!!! + if(node->isServer()) + { + return serverToIndex(node->server()); + } + //If the node is toplevel node (suite). + else if(node->isTopLevel()) + { + if(VModelServer *mserver=data_->server(node->server())) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + + //the node is displayed in the tree + if(VTreeNode* tn=server->tree()->find(node)) + { + int row=tn->indexInParent(); + Q_ASSERT(tn->parent() == server->tree()); + Q_ASSERT(row >=0); + row+=server->tree()->attrNum(atts_); + return createIndex(row,column,server); + } + } + } + + //Other nodes + else if(VNode *parentNode=node->parent()) + { + if(VModelServer *mserver=data_->server(node->server())) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + + if(VTreeNode* tn=server->tree()->find(node)) + { + VTreeNode* tnParent=tn->parent(); + Q_ASSERT(tnParent); + Q_ASSERT(tnParent->vnode() == parentNode); + int row=tnParent->indexOfChild(tn); + row+=tnParent->attrNum(atts_); + return createIndex(row,column,tnParent); + } + } + } + + return QModelIndex(); +} + +//Find the index for the node +QModelIndex TreeNodeModel::nodeToIndex(const VTreeNode* node, int column) const +{ + if(!node) + return QModelIndex(); + + //It is a server + if(node->parent() == 0) + { + VTreeServer *server=node->server(); + Q_ASSERT(server); + return serverToIndex(server); + } + else if(node->isTopLevel()) + { + VTree *vt=node->root(); + Q_ASSERT(vt); + Q_ASSERT(vt == node->parent()); + int row=vt->indexOfChild(node); + if(row != -1) + { + row+=vt->attrNum(atts_); + return createIndex(row,column,vt->server()); + } + + } + if(VTreeNode *parentNode=node->parent()) + { + int row=parentNode->indexOfChild(node); + if(row != -1) + { + row+=parentNode->attrNum(atts_); + return createIndex(row,column,parentNode); + } + } + + return QModelIndex(); + +} + +//Find the index for the node when we know what the server is! +QModelIndex TreeNodeModel::nodeToIndex(VTreeServer* server,const VTreeNode* node, int column) const +{ + if(!node) + return QModelIndex(); + + //If the node is toplevel node (suite). + if(node->isTopLevel()) + { + Q_ASSERT(node->parent() == server->tree()); + int row=server->tree()->indexOfChild(node); + Q_ASSERT(row >=0); + + row+=server->tree()->attrNum(atts_); + return createIndex(row,column,server); + } + //Other nodes + else if(VTreeNode *parentNode=node->parent()) + { + int row=parentNode->indexOfChild(node); + if(row != -1) + { + row+=parentNode->attrNum(atts_); + return createIndex(row,column,parentNode); + } + } + + return QModelIndex(); + +} + +//Find the index for the node! The VNode can be a server as well!!! +QModelIndex TreeNodeModel::attributeToIndex(const VAttribute* a, int column) const +{ + if(!a) + return QModelIndex(); + + VNode* node=a->parent(); + if(!node) + return QModelIndex(); + + VModelServer *mserver=data_->server(node->server()); + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + + int row=node->indexOfAttribute(a,atts_); + if(row != -1) + { + //This is a server!!! + if(node->isServer()) + { + return createIndex(row,column,server); + } + else + { + if(VTreeNode* tn=server->tree()->find(node)) + { + return createIndex(row,column,tn); + } + } + } + + return QModelIndex(); +} + +#if 0 +QModelIndex TreeNodeModel::forceShowNode(const VNode* node) const +{ + //There is nothing to do when there is no status filter set + if(data_->isFilterNull()) + return QModelIndex(); + + Q_ASSERT(node); + Q_ASSERT(!node->isServer()); + Q_ASSERT(node->server()); + + if(VModelServer *mserver=data_->server(node->server())) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + server->setForceShowNode(node); + return nodeToIndex(node); + } + + return QModelIndex(); +} + +QModelIndex TreeNodeModel::forceShowAttribute(const VAttribute* a) const +{ + VNode* node=a->parent(); + Q_ASSERT(node); + //Q_ASSERT(!node->isServer()); + Q_ASSERT(node->server()); + + if(VModelServer *mserver=data_->server(node->server())) + { + VTreeServer* server=mserver->treeServer(); + Q_ASSERT(server); + server->setForceShowAttribute(a); + return attributeToIndex(a); + } + + return QModelIndex(); +} + +#endif + +//Sets the object strored in info as the current forceShow object in the +//data. A forceShow object must always be part of the tree whatever state or +//atribute filter is set. There can be only one forceShow object at a time in the +//whole model/tree. +void TreeNodeModel::setForceShow(VInfo_ptr info) +{ + UI_FUNCTION_LOG + + if(info) + UiLog().dbg() << " info=" << info->path(); + + //Clear the forceShow object in all the server data objects. Only one of + //these is actually need to clear it. + for(int i=0; i < data_->count(); i++) + { + VTreeServer *ts=data_->server(i)->treeServer(); + Q_ASSERT(ts); + ts->clearForceShow(info->item()); + } + + //Sets the forceShow objects in all the server data objects. Only one of these will + //actually set the forceShow. + for(int i=0; i < data_->count(); i++) + { + VTreeServer *ts=data_->server(i)->treeServer(); + Q_ASSERT(ts); + ts->setForceShow(info->item()); + } +} + +//The selection changed in the view +void TreeNodeModel::selectionChanged(QModelIndexList lst) +{ + UI_FUNCTION_LOG + + //if(data_->isFilterNull()) + // return; + + if(lst.count() > 0) + { + QModelIndex idx=lst.back(); + + VInfo_ptr info=nodeInfo(idx); + + setForceShow(info); + } + + + #if 0 + Q_FOREACH(QModelIndex idx,lst) + { + VInfo_ptr info=nodeInfo(idx); + + for(int i=0; i < data_->count(); i++) + { + VTreeServer *ts=data_->server(i)->treeServer(); + Q_ASSERT(ts); + ts->clearForceShow(info->item()); + } + + for(int i=0; i < data_->count(); i++) + { + VTreeServer *ts=data_->server(i)->treeServer(); + Q_ASSERT(ts); + ts->setForceShow(info->item()); + } + } + #endif +} + + +//------------------------------------------------------------------ +// Create info object to index. It is used to identify nodes in +// the tree all over in the programme outside the view. +//------------------------------------------------------------------ + +//WARNING: if we are in the middle of an attribute filter change it will not give +//correct results, because atts_ contains the new filter state, but the whole VTree still +//base on the previous atts_ state!! However we can assume that the index vas visible in +//the tree so attrNum() is cached on the tree nodes so we get correct results for nodes. +//Attributes however cannot be identified correctly. + +VInfo_ptr TreeNodeModel::nodeInfo(const QModelIndex& index) +{ + //For invalid index no info is created. + if(!index.isValid()) + { + VInfo_ptr res; + return res; + } + + //Check if the node is a server + if(ServerHandler *s=indexToServerHandler(index)) + { + return VInfoServer::create(s); + } + + //If the internal pointer is a server it is either a server attribute or a + //top level node (suite) + if(VModelServer *mserver=data_->server(index.internalPointer())) + { + VTreeServer *server=mserver->treeServer(); + Q_ASSERT(server); + + //If the attrNum is cached it is correct! + int serverAttNum=server->tree()->attrNum(atts_); + + //It is a top level node + if(index.row() >= serverAttNum) + { + VNode *n=server->tree()->childAt(index.row()-serverAttNum)->vnode(); + return VInfoNode::create(n); + } + else + { + //TODO: serverattribute + } + } + + //Otherwise the internal pointer points to the parent node + else if(VTreeNode *parentNode=static_cast(index.internalPointer())) + { + //If the attrNum is cached it is correct! + int attNum=parentNode->attrNum(atts_); + + //It is a node + if(index.row() >= attNum) + { + VNode *n=parentNode->childAt(index.row()-attNum)->vnode(); + return VInfoNode::create(n); + } + //It is an attribute + else + { + //This wil not work properly if we are in the middle of an attribute + //filter change! atts_ is the new filter state, but index.row() is based on + //the previous filter state!! + if(VAttribute* a=parentNode->vnode()->attribute(index.row(),atts_)) + { + VInfo_ptr p=VInfoAttribute::create(a); + return p; + + } + } + } + + VInfo_ptr res; + return res; +} + +//---------------------------------------- +// Slots +//---------------------------------------- + +//Server is about to be added +void TreeNodeModel::slotServerAddBegin(int row) +{ + beginInsertRows(QModelIndex(),row,row); +} + +//Addition of the new server has finished +void TreeNodeModel::slotServerAddEnd() +{ + endInsertRows(); +} + +//Server is about to be removed +void TreeNodeModel::slotServerRemoveBegin(VModelServer* server,int /*nodeNum*/) +{ +#ifdef _UI_TREENODEMODEL_DEBUG + UiLog().dbg() << "TreeNodeModel::slotServerRemoveBegin -->"; +#endif + + int row=data_->indexOfServer(server); + Q_ASSERT(row >= 0); + +#ifdef _UI_TREENODEMODEL_DEBUG + UiLog().dbg() << " row: " << row; +#endif + beginRemoveRows(QModelIndex(),row,row); +} + +//Removal of the server has finished +void TreeNodeModel::slotServerRemoveEnd(int /*nodeNum*/) +{ +#ifdef _UI_TREENODEMODEL_DEBUG + UiLog().dbg() << "TreeNodeModel::slotServerRemoveEnd -->"; +#endif + + endRemoveRows(); +} + +void TreeNodeModel::slotDataChanged(VModelServer* server) +{ + QModelIndex idx=serverToIndex(server); + Q_EMIT dataChanged(idx,idx); +} + +//The node changed (it status etc) +void TreeNodeModel::slotNodeChanged(VTreeServer* server,const VTreeNode* node) +{ + if(!node) + return; + + QModelIndex index=nodeToIndex(server,node,0); + + if(!index.isValid()) + return; + + Q_EMIT dataChanged(index,index); +} + +//One of the attributes of the node changed. The total number of the +//attributes is the same. +void TreeNodeModel::slotAttributesChanged(VTreeServer* server,const VTreeNode* node) +{ + if(!node) + return; + + //Find the index of the node. + QModelIndex parent=nodeToIndex(server,node,0); + if(!parent.isValid()) + return; + + //Find out the indexes for all the attributes. For an attribute + //the internal pointer of the index points to the parent VNode. + QModelIndex idx1=index(0,0,parent); + QModelIndex idx2=index(node->attrNum(atts_)-1,0,parent); + + Q_EMIT dataChanged(idx1,idx2); +} + +//Attributes were added or removed +void TreeNodeModel::slotBeginAddRemoveAttributes(VTreeServer* server,const VTreeNode* node,int currentNum,int cachedNum) +{ + int diff=currentNum-cachedNum; + if(diff==0) + return; + + //Find the index of the node. This call does not use the + //number of attributes of the node so it is safe. + QModelIndex parent=nodeToIndex(server,node,0); + if(!parent.isValid()) + return; + + //At this point the model state is based on cachedNum!!!! + //So VNode::attr() must return cachedNum and we need to pretend we have + //cachedNum number of attributes + + //Insertion + if(diff > 0) + { + //We add extra rows to the end of the attributes + beginInsertRows(parent,cachedNum,cachedNum+diff-1); + } + //Deletion + else if(diff <0) + { + //We remove rows from the end + beginRemoveRows(parent,cachedNum+diff,cachedNum-1); + } + + //At this point the model state is based on currentNum!!!! +} + +//Attributes were added or removed +void TreeNodeModel::slotEndAddRemoveAttributes(VTreeServer* server,const VTreeNode* node,int currentNum,int cachedNum) +{ + int diff=currentNum-cachedNum; + if(diff==0) + return; + + //Find the index of the node. This call does not use the + //number of attributes of the node so it is safe. + QModelIndex parent=nodeToIndex(server,node,0); + if(!parent.isValid()) + return; + + //At this point the model state is based on currentNum!!!! + + //Insertion + if(diff > 0) + { + endInsertRows(); + } + //Deletion + else if(diff <0) + { + endRemoveRows(); + } + + //We need to update all the attributes to reflect the change! + //(Since we do not have information about what attribute was actually added + //we always add or remove attributes at the end of the attribute list!!! Then the + //slot below will update all the attributes of the node in the tree). + slotAttributesChanged(server,node); +} + +//The server scan has started (to populate the tree). At this point the tree is empty only containing the +//root node. Num tells us the number of children nodes (aka suites) the root node will contain. +void TreeNodeModel::slotBeginServerScan(VModelServer* server,int num) +{ + assert(active_ == true); + + QModelIndex idx=serverToIndex(server); + + //At this point the server node does not have any rows in the model!!! + if(idx.isValid() && num >0) + { + beginInsertRows(idx,0,num-1); + } +} + +//The server scan has finished. The tree is fully populated. +void TreeNodeModel::slotEndServerScan(VModelServer* server,int num) +{ + assert(active_ == true); + + QModelIndex idx=serverToIndex(server); + if(idx.isValid() && num >0) + { + endInsertRows(); + } + + VTreeServer *ts=server->treeServer(); + Q_ASSERT(ts); + Q_EMIT scanEnded(ts->tree()); + + if(ts->isFirstScan()) + Q_EMIT firstScanEnded(ts); +} + +//The server clear has started. It will remove all the nodes except the root node. +//So we need to remove all the rows belonging to the rootnode. +void TreeNodeModel::slotBeginServerClear(VModelServer* server,int) +{ + assert(active_ == true); + + QModelIndex idx=serverToIndex(server); + + if(idx.isValid()) + { + VTreeServer *ts=server->treeServer(); + Q_ASSERT(ts); + Q_EMIT clearBegun(ts->tree()); + + //We removes the attributes as well!!! + int num=rowCount(idx); + beginRemoveRows(idx,0,num-1); + } +} +//The server clear has finished. The tree is empty only containing the rootnode +void TreeNodeModel::slotEndServerClear(VModelServer* server,int) +{ + assert(active_ == true); + endRemoveRows(); +} + +void TreeNodeModel::slotBeginFilterUpdateRemove(VTreeServer* server,const VTreeNode* topChange,int totalRows) +{ + Q_ASSERT(topChange); + Q_ASSERT(!topChange->isRoot()); + + QModelIndex idx=nodeToIndex(server,topChange); + int attrNum=topChange->attrNum(atts_); + int chNum=topChange->numOfChildren(); + int totalNum=attrNum+chNum; + + Q_ASSERT(totalNum == totalRows); + Q_ASSERT(attrNum >=0); + Q_ASSERT(chNum >=0); + + if(totalRows >0) + { + Q_EMIT filterUpdateRemoveBegun(topChange); + beginRemoveRows(idx,attrNum,attrNum+chNum-1); + } +} + +void TreeNodeModel::slotEndFilterUpdateRemove(VTreeServer* /*server*/,const VTreeNode* topChange,int totalRows) +{ + Q_ASSERT(!topChange->isRoot()); + + if(totalRows >0) + { + endRemoveRows(); + } +} + +void TreeNodeModel::slotBeginFilterUpdateAdd(VTreeServer* server,const VTreeNode* topChange,int chNum) +{ + Q_ASSERT(topChange); + Q_ASSERT(!topChange->isRoot()); + + QModelIndex idx=nodeToIndex(server,topChange); + int attrNum=topChange->attrNum(atts_); + //int totalNum=attrNum+chNum; + + Q_ASSERT(attrNum >=0); + Q_ASSERT(chNum >=0); + + if(chNum >0) + { + beginInsertRows(idx,attrNum,attrNum+chNum-1); + } +} + +void TreeNodeModel::slotEndFilterUpdateAdd(VTreeServer* /*server*/,const VTreeNode* topChange,int chNum) +{ + Q_ASSERT(topChange); + Q_ASSERT(!topChange->isRoot()); + + if(chNum >0) + { + endInsertRows(); + } + + Q_EMIT filterUpdateAddEnded(topChange); +} + +void TreeNodeModel::slotBeginFilterUpdateRemoveTop(VTreeServer* server,int row) +{ + Q_ASSERT(server); + QModelIndex idx=serverToIndex(server); + int attrNum=server->tree()->attrNum(atts_); + int chNum=server->tree()->numOfChildren(); + + Q_ASSERT(chNum > row); + Q_ASSERT(attrNum >=0); + Q_ASSERT(chNum >=0); + + beginRemoveRows(idx,attrNum+row,attrNum+row); +} + +void TreeNodeModel::slotEndFilterUpdateRemoveTop(VTreeServer* server,int) +{ + Q_ASSERT(server); + endRemoveRows(); +} + +void TreeNodeModel::slotBeginFilterUpdateInsertTop(VTreeServer* server,int row) +{ + Q_ASSERT(server); + QModelIndex idx=serverToIndex(server); + int attrNum=server->tree()->attrNum(atts_); + int chNum=server->tree()->numOfChildren(); + + Q_ASSERT(chNum >= row); + Q_ASSERT(attrNum >=0); + Q_ASSERT(chNum >=0); + + beginInsertRows(idx,attrNum+row,attrNum+row); +} + +void TreeNodeModel::slotEndFilterUpdateInsertTop(VTreeServer* server,int row) +{ + Q_ASSERT(server); + endInsertRows(); + + int attrNum=server->tree()->attrNum(atts_); + int chNum=server->tree()->numOfChildren(); + + Q_ASSERT(chNum >= row); + Q_ASSERT(attrNum >=0); + Q_ASSERT(chNum >=0); + + if(row >=0) + { + const VTreeNode* topChange=server->tree()->childAt(row); + //when a suite becomes visible we must notify the view about it so that + //the expand state could be correctly set!!! + Q_EMIT filterUpdateAddEnded(topChange); + } +} + + +int TreeNodeModel::iconNum(VNode* n) const +{ + if(icons_->isEmpty()) + return 0; + else + return VIcon::pixmapNum(n,icons_); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,140 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TREENODEMODEL_H +#define TREENODEMODEL_H + +#include + +#include "AbstractNodeModel.hpp" +#include "Node.hpp" +#include "VAttributeType.hpp" +#include "Viewer.hpp" +#include "VInfo.hpp" + +class AttributeFilter; +class NodeFilterDef; +class ServerFilter; +class ServerHandler; +class VModelServer; +class VTreeModelData; +class VTreeNode; +class VTreeServer; + +class TreeNodeModel : public AbstractNodeModel +{ +Q_OBJECT + +public: + TreeNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef, + AttributeFilter *atts,IconFilter* icons,QObject *parent=0); + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + QModelIndex serverToIndex(ServerHandler*) const; + + QModelIndex nodeToIndex(const VTreeNode*,int column=0) const; + QModelIndex nodeToIndex(const VNode*,int column=0) const; + VTreeServer* indexToServer(const QModelIndex & index) const; + VTreeServer* nameToServer(const std::string&) const; + VTreeNode* indexToNode( const QModelIndex & index) const; + VTreeNode* indexToServerOrNode( const QModelIndex & index) const; + + QModelIndex attributeToIndex(const VAttribute* a, int column=0) const; + + bool isFlatNode(const QModelIndex& index) const; + VInfo_ptr nodeInfo(const QModelIndex& index); + void setForceShow(VInfo_ptr); + void selectionChanged(QModelIndexList lst); + + void setEnableServerToolTip(bool st) {serverToolTip_=st;} + void setEnableNodeToolTip(bool st) {nodeToolTip_=st;} + void setEnableAttributeToolTip(bool st) {attributeToolTip_=st;} + + VModelData* data() const; + +public Q_SLOTS: + void slotServerAddBegin(int row); + void slotServerAddEnd(); + void slotServerRemoveBegin(VModelServer*,int); + void slotServerRemoveEnd(int); + + void slotNodeChanged(VTreeServer*,const VTreeNode*); + void slotAttributesChanged(VTreeServer*,const VTreeNode*); + void slotBeginAddRemoveAttributes(VTreeServer*,const VTreeNode*,int,int); + void slotEndAddRemoveAttributes(VTreeServer*,const VTreeNode*,int,int); + void slotBeginFilterUpdateRemove(VTreeServer*,const VTreeNode*,int); + void slotEndFilterUpdateRemove(VTreeServer*,const VTreeNode*,int); + void slotBeginFilterUpdateAdd(VTreeServer*,const VTreeNode*,int); + void slotEndFilterUpdateAdd(VTreeServer*,const VTreeNode*,int); + void slotBeginFilterUpdateRemoveTop(VTreeServer*,int); + void slotEndFilterUpdateRemoveTop(VTreeServer*,int); + void slotBeginFilterUpdateInsertTop(VTreeServer*,int); + void slotEndFilterUpdateInsertTop(VTreeServer*,int); + + //void slotResetBranch(VModelServer*,const VNode*); + void slotDataChanged(VModelServer*); + void slotBeginServerScan(VModelServer* server,int); + void slotEndServerScan(VModelServer* server,int); + void slotBeginServerClear(VModelServer* server,int); + void slotEndServerClear(VModelServer* server,int); + + int iconNum(VNode*) const; + bool isNode(const QModelIndex & index) const; + bool isAttribute(const QModelIndex & index) const; + +Q_SIGNALS: + void clearBegun(const VTreeNode*); + void scanEnded(const VTreeNode*); + void filterUpdateRemoveBegun(const VTreeNode*); + void filterUpdateAddEnded(const VTreeNode*); + void filterChangeBegun(); + void filterChangeEnded(); + void firstScanEnded(const VTreeServer*); + +#if 0 +protected: + QModelIndex forceShowNode(const VNode* node) const; + QModelIndex forceShowAttribute(const VAttribute* a) const; +#endif + +private: + bool isServer(const QModelIndex& index) const; + bool isServerForValid(const QModelIndex& index) const; + + ServerHandler* indexToServerHandler(const QModelIndex & index) const; + QModelIndex serverToIndex(VModelServer* server) const; + + QModelIndex nodeToIndex(VTreeServer*,const VTreeNode*,int column=0) const; + VTreeNode* indexToAttrParentNode(const QModelIndex & index) const; + VTreeNode* indexToAttrParentOrNode(const QModelIndex & index,bool &itIsANode) const; + QVariant serverData(const QModelIndex& index,int role) const; + QVariant nodeData(const QModelIndex& index,int role,VTreeNode*) const; + QVariant attributesData(const QModelIndex& index,int role,VTreeNode*) const; + + //Attribute filter + VTreeModelData* data_; + AttributeFilter* atts_; + IconFilter* icons_; + + bool serverToolTip_; + bool nodeToolTip_; + bool attributeToolTip_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,761 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TreeNodeView.hpp" + +#include +#include +#include +#include +#include +#include + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +#include "AbstractNodeView.hpp" +#include "ActionHandler.hpp" +#include "Animation.hpp" +#include "AttributeEditor.hpp" +#include "ExpandState.hpp" +#include "TableNodeSortModel.hpp" +#include "PropertyMapper.hpp" +#include "StandardView.hpp" +#include "ServerHandler.hpp" +#include "TreeNodeModel.hpp" +#include "TreeNodeViewDelegate.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "VItemPathParser.hpp" +#include "VNode.hpp" +#include "VModelData.hpp" +#include "VTree.hpp" + +#define _UI_TREENODEVIEW_DEBUG + +TreeNodeView::TreeNodeView(AbstractNodeView* view,TreeNodeModel* model,NodeFilterDef* filterDef,QWidget* parent) : + QObject(parent),NodeViewBase(filterDef), + view_(view), + model_(model), + needItemsLayout_(false), + prop_(NULL), + setCurrentIsRunning_(false), + setCurrentFromExpandIsRunning_(false), + canRegainCurrentFromExpand_(true), + inStartUp_(true) +{ + Q_ASSERT(view_); + + setObjectName("view"); + setProperty("style","nodeView"); + setProperty("view","tree"); + + //Context menu + connect(view_, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + + //Selection + connect(view_,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClickItem(const QModelIndex))); + + //Selection + connect(view_,SIGNAL(selectionChangedInView(const QItemSelection&, const QItemSelection &)), + this,SLOT(selectionChanged(const QItemSelection&, const QItemSelection &))); + + //expandState_=new ExpandState(this,model_); + actionHandler_=new ActionHandler(this,view_); + + connect(view_->delegate(),SIGNAL(sizeHintChangedGlobal()), + this,SLOT(slotSizeHintChangedGlobal())); + + //Properties + std::vector propVec; + propVec.push_back("view.tree.indentation"); + propVec.push_back("view.tree.background"); + propVec.push_back("view.tree.autoExpandLeafNode"); + propVec.push_back("view.tree.drawBranchLine"); + propVec.push_back("view.tree.branchLineColour"); + propVec.push_back("view.tree.serverToolTip"); + propVec.push_back("view.tree.nodeToolTip"); + propVec.push_back("view.tree.attributeToolTip"); + + prop_=new PropertyMapper(propVec,this); + + VProperty *prop=0; + std::string propName; + + //Initialise indentation + prop=prop_->find("view.tree.indentation",true); + adjustIndentation(prop->value().toInt()); + + //Init bg colour + prop=prop_->find("view.tree.background",true); + adjustBackground(prop->value().value()); + + //Init branch line status (on/off) + prop=prop_->find("view.tree.autoExpandLeafNode",true); + adjustAutoExpandLeafNode(prop->value().toBool()); + + //Init branch line status (on/off) + prop=prop_->find("view.tree.drawBranchLine",true); + adjustDrawBranchLine(prop->value().toBool()); + + //Init branch line/connector colour + prop=prop_->find("view.tree.branchLineColour",true); + adjustBranchLineColour(prop->value().value()); + + //Adjust tooltip + prop=prop_->find("view.tree.serverToolTip",true); + adjustServerToolTip(prop->value().toBool()); + + prop=prop_->find("view.tree.nodeToolTip",true); + adjustNodeToolTip(prop->value().toBool()); + + prop=prop_->find("view.tree.attributeToolTip",true); + adjustAttributeToolTip(prop->value().toBool()); + + inStartUp_=false; +} + +TreeNodeView::~TreeNodeView() +{ + delete actionHandler_; + delete prop_; +} + +QWidget* TreeNodeView::realWidget() +{ + return view_; +} + +QObject* TreeNodeView::realObject() +{ + return this; +} + +//Collect the selected list of indexes +QModelIndexList TreeNodeView::selectedList() +{ + QModelIndexList lst; + Q_FOREACH(QModelIndex idx,view_->selectedIndexes()) + if(idx.column() == 0) + lst << idx; + return lst; +} + +// This slot is called when the selection changed in the view +void TreeNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ +#ifdef _UI_TREENODEVIEW_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndexList lst=view_->selectedIndexes(); + + //When the selection was triggered from restoring the expand state of the tree + //we do not want to broadcast it + if(lst.count() > 0 && !setCurrentFromExpandIsRunning_) + { + VInfo_ptr info=model_->nodeInfo(lst.back()); + if(info && !info->isEmpty()) + { +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " emit=" << info->path(); +#endif + Q_EMIT selectionChanged(info); + } + //Remembers the current selection + lastSelection_=info; + } + + + //The model has to know about the selection in order to manage the + //nodes that are forced to be shown. We only do it when we are not in the the middle of + // "setCurrentSelection()" call because that handles the forceShow independently. + if(!setCurrentIsRunning_) + model_->selectionChanged(lst); +} + +//Returns the current selection (the last one!) +VInfo_ptr TreeNodeView::currentSelection() +{ + QModelIndexList lst=view_->selectedIndexes(); + if(lst.count() > 0) + { + return model_->nodeInfo(lst.back()); + } + return VInfo_ptr(); +} + +//Sets the current selection to the given VInfo item. +// called: +// -from outside of the view when the selection is broadcast from another view +// -from within the view when the tree expand state was restored +void TreeNodeView::setCurrentSelection(VInfo_ptr info) +{ +#ifdef _UI_TREENODEVIEW_DEBUG + UI_FUNCTION_LOG +#endif + //We cannot call it recursively + Q_ASSERT(setCurrentIsRunning_ == false); + + //While the current item is being selected we do not allow + //another setCurrent call to go through + if(!info || setCurrentIsRunning_) + return; + + //Indicate that setCurrent started + setCurrentIsRunning_=true; + +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " info=" << info->path(); +#endif + + //Forcing an object to be shown can result in altering and relayouting the tree. We + //have to block the regaining of the selection at the end of the layout + //process when the tree expand state is restored. + canRegainCurrentFromExpand_=false; + + //Force the object to be shown in the tree + model_->setForceShow(info); + + //Lookup the object in the model + QModelIndex idx=model_->infoToIndex(info); + + //Get the index again if it is needed + //if(!idx.isValid()) + //{ + // idx=model_->infoToIndex(info); + //} + + //The re-layouting finished. We do not need to block the regaining of selection when + //the tree expand state is restored. + canRegainCurrentFromExpand_=true; + + //If the item is in the model we set it as current + if(idx.isValid()) + { + view_->setCurrentIndex(idx); //this will call selectionChanged + view_->scrollTo(idx); + } + + //Indicate that the set current process finished + setCurrentIsRunning_=false; +} + +//Sets the current selection to the given VInfo item +//when the tree expand state is restored +void TreeNodeView::setCurrentSelectionFromExpand(VInfo_ptr info) +{ +#ifdef _UI_TREENODEVIEW_DEBUG + UI_FUNCTION_LOG +#endif + if(!info || setCurrentFromExpandIsRunning_) + return; + +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " info=" << info->path(); +#endif + + setCurrentFromExpandIsRunning_=true; + setCurrentSelection(info); + setCurrentFromExpandIsRunning_=false; +} + +//Selects the first server in the view +void TreeNodeView::selectFirstServer() +{ + QModelIndex idx=model_->index(0,0); + if(idx.isValid()) + { + view_->setCurrentIndex(idx); + VInfo_ptr info=model_->nodeInfo(idx); + Q_EMIT selectionChanged(info); + } +} + +void TreeNodeView::slotContextMenu(const QPoint &position) +{ +#ifdef _UI_TREENODEVIEW_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndex indexClicked=view_->indexAt(position); + QModelIndexList indexSel=selectedList(); + if(!indexSel.contains(indexClicked)) + { + indexSel.clear(); + indexSel << indexClicked; + } + + QPoint scrollOffset(view_->horizontalScrollBar()->value(),view_->verticalScrollBar()->value()); + handleContextMenu(indexClicked,indexSel,view_->mapToGlobal(position),position+scrollOffset,view_); +} + +void TreeNodeView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) +{ + //Node actions + if(indexClicked.isValid() && indexClicked.column() == 0) //indexLst[0].isValid() && indexLst[0].column() == 0) + { + std::vector nodeLst; + for(int i=0; i < indexLst.count(); i++) + { + VInfo_ptr info=model_->nodeInfo(indexLst[i]); + if(info && !info->isEmpty()) + nodeLst.push_back(info); + } + + actionHandler_->contextMenu(nodeLst,globalPos); + } + + //Desktop actions + else + { + } +} + +void TreeNodeView::slotDoubleClickItem(const QModelIndex& idx) +{ + VInfo_ptr info=model_->nodeInfo(idx); + if(info && info->isAttribute()) + { + slotViewCommand(info,"edit"); + } +} + +void TreeNodeView::slotViewCommand(VInfo_ptr info,QString cmd) +{ + //Expand all the children of the given node + if(cmd == "expand") + { + QModelIndex idx=model_->infoToIndex(info); + if(idx.isValid()) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif +#ifdef _UI_TREENODEVIEW_DEBUG + QTime t; + t.start(); +#endif + //apply expand in the view + view_->expandAll(idx); +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << "expandAll time=" << t.elapsed()/1000. << "s"; +#endif +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + //save/update the expand state object + saveExpandAll(idx); + } + } + else if(cmd == "collapse") + { + QModelIndex idx=model_->infoToIndex(info); + if(idx.isValid()) + { + //apply expand in the view + view_->collapseAll(idx); + + //save/update the expand state object + saveCollapseAll(idx); + } + } + + else if(cmd == "edit") + { + if(info && info->isAttribute()) + { + AttributeEditor::edit(info,view_); + } + } + + //Only filter the given suite (of the node stored in info). + //We achive this by setting the suitefilter to filter only this + //suite. + else if(cmd == "filterOne") + { + if(info) + { + if(ServerHandler* server=info->server()) + { + VNode* n=info->node(); + if(n && info->isNode()) + server->setSuiteFilterWithOne(n); + } + } + } + + /*if(cmd == "set_as_root") + { + model_->setRootNode(nodeLst.at(0)->node()); + expandAll(); + }*/ +} + +void TreeNodeView::reload() +{ + //model_->reload(); + //expandAll(); +} + +void TreeNodeView::rerender() +{ + if(needItemsLayout_) + { + view_->doItemsLayout(); + needItemsLayout_=false; + } + else + { + view_->viewport()->update(); + } +} + +void TreeNodeView::slotRerender() +{ + rerender(); +} + +void TreeNodeView::slotRepaint(Animation* an) +{ + if(!an) + return; + + Q_FOREACH(VNode* n,an->targets()) + { + view_->update(model_->nodeToIndex(n)); + } +} + +void TreeNodeView::slotSizeHintChangedGlobal() +{ + needItemsLayout_=true; +} + +//==================================================== +// Expand state management +//==================================================== + +void TreeNodeView::expandTo(const QModelIndex& idxTo) +{ + QModelIndex idx=model_->parent(idxTo); + QModelIndexList lst; + + while(idx.isValid()) + { + lst.push_front(idx); + idx=idx.parent(); + } + + Q_FOREACH(QModelIndex d,lst) + { + view_->expand(d); + } +} + +#if 0 +//Save all +void TreeNodeView::slotSaveExpand() +{ + //For each server we save the expand state + for(int i=0; i < model_->rowCount(); i++) + { + QModelIndex serverIdx=model_->index(i, 0); + VTreeServer* ts=model_->indexToServer(serverIdx); + Q_ASSERT(ts); + + //The expand state is stored on the VTreeServer and must survive updates and refreshes! + ExpandState* es=ts->expandState(); + if(!es) + { + es=new ExpandState(view_,model_); + ts->setExpandState(es); //the treeserver takes ownership of the expandstate + } + + //Save the current state + Q_ASSERT(ts->tree()); + VNode* vnode=ts->tree()->vnode(); + Q_ASSERT(vnode); + es->save(vnode); + } +} + +void TreeNodeView::slotRestoreExpand() +{ + //For each server we restore the expand state + for(int i=0; i < model_->rowCount(); i++) + { + QModelIndex serverIdx=model_->index(i, 0); + VTreeServer* ts=model_->indexToServer(serverIdx); + Q_ASSERT(ts); + + //The expand state is stored on the VTreeServer + ExpandState* es=ts->expandState(); + if(es) + { + Q_ASSERT(ts->tree()); + VNode* vnode=ts->tree()->vnode(); + Q_ASSERT(vnode); + + bool expanded=view_->isExpanded(serverIdx); + view_->collapse(serverIdx); + es->collectExpanded(vnode,view_->expandedIndexes); + if(expanded) + view_->expand(serverIdx); + } + } + regainSelectionFromExpand(); + +} +#endif + +//Save the expand state for the given node (it can be a server as well) +void TreeNodeView::slotSaveExpand(const VTreeNode* node) +{ + UI_FUNCTION_LOG + Q_ASSERT(node); + VTreeServer* ts=node->server(); + Q_ASSERT(ts); + +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " node=" << node->vnode()->fullPath(); +#endif + + ExpandState* es=ts->expandState(); + if(!es) + { + es=new ExpandState(view_,model_); + ts->setExpandState(es); //the treeserver takes ownership of the expandstate + } + + Q_ASSERT(es); + + //Save the current state + es->save(node->vnode()); + + //es->print(); +} + +//Restore the expand state for the given node (it can be a server as well) +void TreeNodeView::slotRestoreExpand(const VTreeNode* node) +{ + UI_FUNCTION_LOG + Q_ASSERT(node); + VTreeServer* ts=node->server(); + Q_ASSERT(ts); + +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " node=" << node->vnode()->fullPath(); +#endif + + if(ExpandState* es=ts->expandState()) + { + QModelIndex idx=model_->nodeToIndex(node); + if(idx.isValid()) + { + bool expandedOri=view_->isExpanded(idx); + view_->collapse(idx); + es->collectExpanded(node->vnode(),view_->expandedIndexes); +#ifdef _UI_TREENODEVIEW_DEBUG + UiLog().dbg() << " expanded=" << view_->isExpanded(idx); +#endif + if(expandedOri || view_->isExpanded(idx)) + view_->expand(idx); + } + + //es->print(); + } + + if(canRegainCurrentFromExpand_) + regainSelectionFromExpand(); +} + +void TreeNodeView::saveExpandAll(const QModelIndex& idx) +{ + if(!idx.isValid()) + return; + + VTreeNode* tnode=model_->indexToServerOrNode(idx); + Q_ASSERT(tnode); + VTreeServer* ts=tnode->server(); + Q_ASSERT(ts); + Q_ASSERT(ts->tree()); + VNode* vnode=ts->tree()->vnode(); + Q_ASSERT(vnode); + + //The expand state is stored on the VTreeServer + ExpandState* es=ts->expandState(); + if(!es) + { + es=new ExpandState(view_,model_); + ts->setExpandState(es); //the treeserver takes ownership of the expandstate + } + if(es->isEmpty()) + { + es->save(vnode); + } + es->saveExpandAll(vnode); +} + +void TreeNodeView::saveCollapseAll(const QModelIndex& idx) +{ + if(!idx.isValid()) + return; + + VTreeNode* tnode=model_->indexToServerOrNode(idx); + Q_ASSERT(tnode); + VTreeServer* ts=tnode->server(); + Q_ASSERT(ts); + Q_ASSERT(ts->tree()); + VNode* vnode=ts->tree()->vnode(); + Q_ASSERT(vnode); + + //The expand state is stored on the VTreeServer + ExpandState* es=ts->expandState(); + if(!es) + { + es=new ExpandState(view_,model_); + ts->setExpandState(es); //the treeserver takes ownership of the expandstate + } + if(es->isEmpty()) + { + es->save(vnode); + } + es->saveCollapseAll(vnode); +} + + +void TreeNodeView::regainSelectionFromExpand() +{ + Q_ASSERT(canRegainCurrentFromExpand_ == true); + + VInfo_ptr s=currentSelection(); + if(!s) + { + if(lastSelection_) + { + std::string lastPath=lastSelection_->storedPath(); + lastSelection_->regainData(); + if(!lastSelection_->hasData()) + { + lastSelection_.reset(); + + //We might have lost the selection because the + //selected node was removed. We try to find the parent + //and select it when exists. + VItemPathParser p(lastPath); + VInfo_ptr ptr=VInfo::createFromPath(p.parent()); + if(ptr) + { + setCurrentSelectionFromExpand(ptr); + } + } + else + { + setCurrentSelectionFromExpand(lastSelection_); + } + } + } +} + +//============================================== +// Property handling +//============================================== + +void TreeNodeView::adjustIndentation(int indent) +{ + if(indent >=0) + { + view_->setIndentation(indent); + needItemsLayout_=true; + } +} + +void TreeNodeView::adjustBackground(QColor col) +{ + if(col.isValid()) + { + QPalette p=view_->viewport()->palette(); + p.setColor(QPalette::Window,col); + view_->viewport()->setPalette(p); + + //When we set the palette on startup something resets the palette + //before the first paint event happens. So we set the expected bg colour + //so that the view should know what bg colour it should use. + if(inStartUp_) + view_->setExpectedBg(col); + } +} + +void TreeNodeView::adjustAutoExpandLeafNode(bool b) +{ + view_->setAutoExpandLeafNode(b); +} + +void TreeNodeView::adjustDrawBranchLine(bool b) +{ + view_->setDrawConnector(b); +} + +void TreeNodeView::adjustBranchLineColour(QColor col) +{ + view_->setConnectorColour(col); +} + +void TreeNodeView::adjustServerToolTip(bool st) +{ + model_->setEnableServerToolTip(st); +} + +void TreeNodeView::adjustNodeToolTip(bool st) +{ + model_->setEnableNodeToolTip(st); +} + +void TreeNodeView::adjustAttributeToolTip(bool st) +{ + model_->setEnableAttributeToolTip(st); +} + +void TreeNodeView::notifyChange(VProperty* p) +{ + if(p->path() == "view.tree.background") + { + adjustBackground(p->value().value()); + } + else if(p->path() == "view.tree.indentation") + { + adjustIndentation(p->value().toInt()); + } + else if(p->path() == "view.tree.autoExpandLeafNode") + { + adjustAutoExpandLeafNode(p->value().toBool()); + } + else if(p->path() == "view.tree.drawBranchLine") + { + adjustDrawBranchLine(p->value().toBool()); + } + else if(p->path() == "view.tree.branchLineColour") + { + adjustBranchLineColour(p->value().value()); + } + else if(p->path() == "view.tree.serverToolTip") + { + adjustServerToolTip(p->value().toBool()); + } + else if(p->path() == "view.tree.nodeToolTip") + { + adjustNodeToolTip(p->value().toBool()); + } + else if(p->path() == "view.tree.attributeToolTip") + { + adjustAttributeToolTip(p->value().toBool()); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeViewDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeViewDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1398 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TreeNodeViewDelegate.hpp" + +#include +#include +#include +#include +#include + +#include "AbstractNodeModel.hpp" +#include "Animation.hpp" +#include "FontMetrics.hpp" +#include "IconProvider.hpp" +#include "Palette.hpp" +#include "PropertyMapper.hpp" +#include "ServerHandler.hpp" +#include "TreeNodeModel.hpp" +#include "UiLog.hpp" + +static std::vector propVec; + +static QColor typeFgColourClassic=QColor(Qt::white); +static QColor typeBgColourClassic=QColor(150,150,150); +static QColor childCountColour=QColor(90,91,92); + + +struct NodeShape +{ + QColor col_; + QPolygon shape_; +}; + +struct NodeText +{ + QColor fgCol_; + QColor bgCol_; + QRect br_; + QString text_; +}; + +struct ServerUpdateData +{ + QRect br_; + int prevTime_; + int nextTime_; + QString prevText_; + QString nextText_; + float prog_; +}; + + +class TreeNodeDelegateBox : public NodeDelegateBox +{ +public: + + TreeNodeDelegateBox() : textTopCorrection(0), textBottomCorrection(0) + { + topMargin=2; + bottomMargin=2; + leftMargin=1; + rightMargin=2; + topPadding=0; + bottomPadding=0; + leftPadding=2; + rightPadding=2; + } + + int realFontHeight; + int textTopCorrection; + int textBottomCorrection; + + void adjust(const QFont& f) + { + FontMetrics fm(f); + realFontHeight=fm.realHeight(); + textTopCorrection=fm.topPaddingForCentre(); + textBottomCorrection=fm.bottomPaddingForCentre(); + fontHeight=fm.height(); + + if(textTopCorrection > 1) + { + textTopCorrection-=2; + realFontHeight+=2; + } + + height=realFontHeight+topPadding+bottomPadding; + fullHeight=height+topMargin+bottomMargin; + sizeHintCache=QSize(100,fullHeight); + spacing=fm.width('A')*3/4; + + int h=static_cast(static_cast(fm.height())*0.7); + iconSize=h; + if(iconSize % 2 == 1) + iconSize+=1; + + iconGap=1; + if(iconSize > 16) + iconGap=2; + + iconPreGap=fm.width('A')/2; + } + + QRect adjustTextRect(const QRect& rIn) const + { + //Q_ASSERT(rIn.height() == fontHeight); + QRect r=rIn; + r.setY(r.y()-textTopCorrection); + r.setHeight(fontHeight); + return r; + } + + QRect adjustSelectionRect(const QRect& optRect) const { + QRect r=optRect; + return r; + } +}; + +class TreeAttrDelegateBox : public AttrDelegateBox +{ +public: + TreeAttrDelegateBox() : textTopCorrection(0), textBottomCorrection(0) + { + topMargin=2; + bottomMargin=2; + leftMargin=1; + rightMargin=2; + topPadding=0; + bottomPadding=0; + leftPadding=1; + rightPadding=2; + } + + int realFontHeight; + int textTopCorrection; + int textBottomCorrection; + + void adjust(const QFont& f) + { + FontMetrics fm(f); + realFontHeight=fm.realHeight(); + textTopCorrection=fm.topPaddingForCentre(); + textBottomCorrection=fm.bottomPaddingForCentre(); + fontHeight=fm.height(); + + height=realFontHeight+topPadding+bottomPadding; + fullHeight=height+topMargin+bottomMargin; + sizeHintCache=QSize(100,fullHeight); + spacing=fm.width('A')*3/4; + } + + QRect adjustTextRect(const QRect& rIn) const + { + QRect r=rIn; + r.setY(r.y()-textTopCorrection+1); + r.setHeight(fontHeight); + return r; + } + + QRect adjustTextBgRect(const QRect& rIn) const + { + QRect r=rIn; + return r.adjusted(0,-1,0,1); + } + + QRect adjustSelectionRect(const QRect& optRect) const { + QRect r=optRect; + return r.adjusted(0,-selectRm.topOffset(),0,-selectRm.bottomOffset()); + } + + QRect adjustSelectionRectNonOpt(const QRect& optRect) const { + return adjustSelectionRect(optRect); + } +}; + + + +TreeNodeViewDelegate::TreeNodeViewDelegate(TreeNodeModel* model,QWidget *parent) : + nodeRectRad_(0), + drawChildCount_(true), + nodeStyle_(ClassicNodeStyle), + drawNodeType_(true), + bgCol_(Qt::white), + model_(model) +{ + + drawAttrSelectionRect_=true; + + attrFont_=font_; + attrFont_.setPointSize(8); + serverInfoFont_=font_; + suiteNumFont_=font_; + abortedReasonFont_=font_; + abortedReasonFont_.setBold(true); + typeFont_=font_; + typeFont_.setBold(true); + typeFont_.setPointSize(font_.pointSize()-1); + + //Property + if(propVec.empty()) + { + propVec.push_back("view.tree.nodeFont"); + propVec.push_back("view.tree.attributeFont"); + propVec.push_back("view.tree.display_child_count"); + propVec.push_back("view.tree.displayNodeType"); + propVec.push_back("view.tree.background"); + propVec.push_back("view.common.node_style"); + + //Base settings + addBaseSettings(propVec); + } + + prop_=new PropertyMapper(propVec,this); + + //WARNING: updateSettings() cannot be called from here because + //nodeBox_ and attrBox_ are only created in the derived classes. So it muse be + //called from those derived classes. + + //The parent must be the view!!! + animation_=new AnimationHandler(parent); + + nodeBox_=new TreeNodeDelegateBox; + attrBox_=new TreeAttrDelegateBox; + + nodeBox_->adjust(font_); + attrBox_->adjust(attrFont_); + + typeFont_=font_; + typeFont_.setBold(true); + typeFont_.setPointSize(font_.pointSize()-1); + + updateSettings(); +} + +TreeNodeViewDelegate::~TreeNodeViewDelegate() +{ + delete animation_; +} + +void TreeNodeViewDelegate::updateSettings() +{ + Q_ASSERT(nodeBox_); + Q_ASSERT(attrBox_); + + if(VProperty* p=prop_->find("view.common.node_style")) + { + if(p->value().toString() == "classic") + nodeStyle_=ClassicNodeStyle; + else + nodeStyle_=BoxAndTextNodeStyle; + } + if(VProperty* p=prop_->find("view.tree.nodeRectRadius")) + { + nodeRectRad_=p->value().toInt(); + } + if(VProperty* p=prop_->find("view.tree.nodeFont")) + { + QFont newFont=p->value().value(); + + if(font_ != newFont ) + { + font_=newFont; + nodeBox_->adjust(font_); + serverInfoFont_=font_; + serverNumFont_.setFamily(font_.family()); + serverNumFont_.setPointSize(font_.pointSize()-1); + suiteNumFont_.setFamily(font_.family()); + suiteNumFont_.setPointSize(font_.pointSize()-1); + abortedReasonFont_.setFamily(font_.family()); + abortedReasonFont_.setPointSize(font_.pointSize()); + typeFont_.setFamily(font_.family()); + typeFont_.setPointSize(font_.pointSize()-1); + + Q_EMIT sizeHintChangedGlobal(); + } + } + if(VProperty* p=prop_->find("view.tree.attributeFont")) + { + QFont newFont=p->value().value(); + + if(attrFont_ != newFont) + { + attrFont_=newFont; + attrBox_->adjust(attrFont_); + Q_EMIT sizeHintChangedGlobal(); + } + } + + if(VProperty* p=prop_->find("view.tree.display_child_count")) + { + drawChildCount_=p->value().toBool(); + } + + if(VProperty* p=prop_->find("view.tree.displayNodeType")) + { + drawNodeType_=p->value().toBool(); + } + + if(VProperty* p=prop_->find("view.tree.background")) + { + bgCol_=p->value().value(); + } + + //Update the settings handled by the base class + updateBaseSettings(); +} + +bool TreeNodeViewDelegate::isSingleHeight(int h) const +{ + return (h==nodeBox_->fullHeight || h == attrBox_->fullHeight); +} + +QSize TreeNodeViewDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex & index ) const +{ + return nodeBox_->sizeHintCache; +} + +//This has to be extremely fast +void TreeNodeViewDelegate::sizeHint(const QModelIndex& index,int& w,int& h) const +{ + QVariant tVar=index.data(Qt::DisplayRole); + + h=nodeBox_->fullHeight; + + //For nodes we compute the exact size of visual rect + if(tVar.type() == QVariant::String) + { + QString text=index.data(Qt::DisplayRole).toString(); + if(index.data(AbstractNodeModel::ServerRole).toInt() ==0) + { + widthHintServer(index,w,text); + } + else + { + w=nodeWidth(index,text); + } + } + //For attributes we do not need the exact width since they do not have children so + //there is nothing on their right in the view. We compute their proper size when + //they are first rendered. However the exact height must be known at this stage! + else if(tVar.type() == QVariant::StringList) + { + //Each attribute has this height except the multiline labels + h=attrBox_->fullHeight; + + //It is a big enough hint for the width. + w=300; + + //For multiline labels we need to compute the height + int attLineNum=0; + if((attLineNum=index.data(AbstractNodeModel::AttributeLineRole).toInt()) > 1) + { + h=labelHeight(attLineNum); + } + } +} + +void TreeNodeViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index,QSize& size) const +{ + size=QSize(0,0); + + //Background + QStyleOptionViewItem vopt(option); + initStyleOption(&vopt, index); + + //Save painter state + painter->save(); + + if(index.data(AbstractNodeModel::ConnectionRole).toInt() == 0) + { + QRect fullRect=QRect(0,vopt.rect.y(),painter->device()->width(),vopt.rect.height()); + painter->fillRect(fullRect,lostConnectBgBrush_); + QRect bandRect=QRect(0,vopt.rect.y(),5,vopt.rect.height()); + painter->fillRect(bandRect,lostConnectBandBrush_); + } + + QVariant tVar=index.data(Qt::DisplayRole); + painter->setFont(font_); + + if(tVar.type() == QVariant::String) + { + int width=0; + QString text=index.data(Qt::DisplayRole).toString(); + if(index.data(AbstractNodeModel::ServerRole).toInt() ==0) + { + width=renderServer(painter,index,vopt,text); + } + else + { + width=renderNode(painter,index,vopt,text); + } + + size=QSize(width,nodeBox_->fullHeight); + } + //Render attributes + else if(tVar.type() == QVariant::StringList) + { + QStringList lst=tVar.toStringList(); + if(lst.count() > 0) + { + QMap::const_iterator it=attrRenderers_.find(lst.at(0)); + if(it != attrRenderers_.end()) + { + AttributeRendererProc a=it.value(); + (this->*a)(painter,lst,vopt,size); + } + //if(width==0) + // width=300; + } + + } + + painter->restore(); + + //else + // QStyledItemDelegate::paint(painter,option,index); +} + + + +int TreeNodeViewDelegate::renderServer(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const +{ + ServerHandler* server=static_cast(index.data(AbstractNodeModel::ServerPointerRole).value()); + Q_ASSERT(server); + + int totalWidth=0; + bool selected=option.state & QStyle::State_Selected; + //int offset=nodeBox_->leftMargin; + QFontMetrics fm(font_); + + //The initial filled rect (we will adjust its width) + QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); + +#if 0 + painter->setPen(QColor(190,190,190)); + painter->drawLine(0,option.rect.y()+1,painter->device()->width(),option.rect.y()+1); + painter->drawLine(0,option.rect.bottom()-1,painter->device()->width(),option.rect.bottom()-1); +#endif +#if 0 + + QRect progRect(0,itemRect.y()-deltaH,painter->device()->width(),itemRect.height()+2*deltaH); + progRect.setWidth(painter->device()->width()); + painter->setBrush(Qt::NoBrush); + painter->setPen(QColor(190,190,190)); + painter->drawRect(progRect); + painter->setBrush(QColor(230,230,230)); + painter->drawRect(progRect.adjusted(0,0,-progRect.width()*0.4,0)); +#endif + + int currentRight=itemRect.x(); + + NodeShape stateShape; + stateShape.col_=index.data(Qt::BackgroundRole).value(); + + NodeText nodeText; + nodeText.text_=text; + int textWidth=fm.width(text); + + if(nodeStyle_ == BoxAndTextNodeStyle) + { + stateShape.shape_=QPolygon(QRect(itemRect.x(),itemRect.y(),itemRect.height(),itemRect.height())); + QRect shBr=stateShape.shape_.boundingRect(); + currentRight=shBr.x()+shBr.width()+2; + nodeText.br_=QRect(currentRight,itemRect.y(),textWidth+nodeBox_->leftPadding, itemRect.height()); + nodeText.fgCol_=QColor(Qt::black); + currentRight=nodeText.br_.x()+nodeText.br_.width(); + } + else + { + stateShape.shape_=QPolygon(QRect(itemRect.x(),itemRect.y(),textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding,itemRect.height())); + nodeText.br_=QRect(itemRect.x()+nodeBox_->leftPadding,itemRect.y(),textWidth, itemRect.height()); + nodeText.fgCol_=index.data(Qt::ForegroundRole).value(); + QRect shBr=stateShape.shape_.boundingRect(); + currentRight=shBr.x()+shBr.width();; + } + + //Refresh timer + /* bool hasTimer=true; + QRect timeRect(currentRight+offset,itemRect.y()-1,itemRect.height()+2,itemRect.height()+2); + currentRight=timeRect.right();*/ + + //Icons area + QList pixLst; + QList pixRectLst; + + QVariant va=index.data(AbstractNodeModel::IconRole); + if(va.type() == QVariant::List) + { + QVariantList lst=va.toList(); + if(lst.count() >0) + { + int xp=currentRight+nodeBox_->iconPreGap; + int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; + for(int i=0; i < lst.count(); i++) + { + int id=lst[i].toInt(); + if(id != -1) + { + pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); + pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); + xp+=nodeBox_->iconSize+nodeBox_->iconGap; + } + } + + if(!pixLst.isEmpty()) + { + currentRight=xp-nodeBox_->iconGap; + } + } + } + + //The pixmap (optional) + bool hasPix=false; + QRect pixRect; + + //The info rectangle (optional) + QRect infoRect; + QString infoTxt=index.data(AbstractNodeModel::InfoRole).toString(); + bool hasInfo=(infoTxt.isEmpty() == false); + + if(hasInfo) + { + //infoFont.setBold(true); + fm=QFontMetrics(serverInfoFont_); + + int infoWidth=fm.width(infoTxt); + infoRect = nodeText.br_; + infoRect.setLeft(currentRight+fm.width('A')); + infoRect.setWidth(infoWidth); + currentRight=infoRect.x()+infoRect.width(); + } + + //The load icon (optional) + QRect loadRect; + bool hasLoad=index.data(AbstractNodeModel::LoadRole).toBool(); + Animation* an=0; + + //Update load animation + if(hasLoad) + { + an=animation_->find(Animation::ServerLoadType,true); + + loadRect = QRect(currentRight+fm.width('A'), + itemRect.top()+(itemRect.height()-an->scaledSize().height())/2, + an->scaledSize().width(), + an->scaledSize().height()); + + currentRight=loadRect.x()+loadRect.width(); + + //Add this index to the animations + //There is no guarantee that this index will be valid in the future!!! + an->addTarget(server->vRoot()); + } + //Stops load animation + else + { + if((an=animation_->find(Animation::ServerLoadType,false)) != NULL) + an->removeTarget(server->vRoot()); + } + + //The node number (optional) + QRect numRect; + QString numTxt; + bool hasNum=false; + + if(drawChildCount_) + { + QVariant va=index.data(AbstractNodeModel::NodeNumRole); + hasNum=(va.isNull() == false); + if(hasNum) + { + numTxt="(" + QString::number(va.toInt()) + ")"; + + QFontMetrics fmNum(serverNumFont_); + + int numWidth=fmNum.width(numTxt); + numRect = nodeText.br_; + numRect.setLeft(currentRight+fmNum.width('A')/2); + numRect.setWidth(numWidth); + currentRight=numRect.x()+numRect.width(); + } + } + + //Update +#if 0 + bool hasUpdate=false; + ServerUpdateData updateData; + if(server) + { + hasUpdate=true; + updateData.br_=QRect(currentRight+3*offset,itemRect.y()-1,0,itemRect.height()+2); + updateData.prevTime_=server->secsSinceLastRefresh(); + updateData.nextTime_=server->secsTillNextRefresh(); + if(updateData.prevTime_ >=0) + { + if(updateData.nextTime_ >=0) + { + updateData.prevText_="-" + formatTime(updateData.prevTime_); + updateData.nextText_="+" +formatTime(updateData.nextTime_); + updateData.prog_=(static_cast(updateData.prevTime_)/static_cast(updateData.prevTime_+updateData.nextTime_)); + updateData.br_.setWidth(fm.width("ABCDE")+fm.width(updateData.prevText_)+fm.width(updateData.nextText_)+2*offset); + } + else + { + updateData.prevText_="last update: " + formatTime(updateData.prevTime_); + updateData.prog_=0; + updateData.br_.setWidth(fm.width(updateData.prevText_)); + } + currentRight=updateData.br_.right(); + } + else + { + hasUpdate=false; + } + } +#endif + + //Define clipping + int rightPos=currentRight+1; + totalWidth=rightPos-option.rect.left(); + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } + + //Draw state/node rect + renderServerCell(painter,stateShape,nodeText,selected); + + //Draw timer + /*int remaining=6*60*1000; // server->remainingTimeToRefresh(); + int total=10*60*1000; //server->refreshPeriod() ; + renderTimer(painter,timeRect,remaining,total);*/ + + //Draw icons + for(int i=0; i < pixLst.count(); i++) + { + painter->drawPixmap(pixRectLst[i],pixLst[i]); + } + + //Draw pixmap if needed + if(hasPix) + { + painter->drawPixmap(pixRect,errPix_); + } + + //Draw info + if(hasInfo) + { + painter->setPen(Qt::black); + painter->setFont(serverInfoFont_); + painter->drawText(infoRect,Qt::AlignLeft | Qt::AlignVCenter,infoTxt); + } + + //Draw number + if(hasNum) + { + painter->setPen(childCountColour); + painter->setFont(serverNumFont_); + painter->drawText(numRect,Qt::AlignLeft | Qt::AlignVCenter,numTxt); + } + + //Draw load animation + if(hasLoad) + { + Animation* an=animation_->find(Animation::ServerLoadType,false); + if(an) + { + painter->drawPixmap(loadRect,an->currentPixmap()); + } + } +#if 0 + if(hasUpdate) + { + renderServerUpdate(painter,updateData); + } +#endif + if(setClipRect) + { + painter->restore(); + } + + return totalWidth; +} + +int TreeNodeViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const +{ + int totalWidth=0; + bool selected=option.state & QStyle::State_Selected; + QFontMetrics fm(font_); + + //The initial filled rect (we will adjust its width) + QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); + + NodeShape stateShape; + NodeShape realShape; + NodeText nodeText; + NodeText typeText; + + //get the colours + QVariant bgVa=index.data(Qt::BackgroundRole); + bool hasRealBg=false; + if(bgVa.type() == QVariant::List) + { + QVariantList lst=bgVa.toList(); + if(lst.count() == 2) + { + hasRealBg=true; + stateShape.col_=lst[0].value(); + realShape.col_=lst[1].value(); + } + } + else + { + stateShape.col_=bgVa.value(); + } + + int currentRight=itemRect.x(); + + //Node type + QFontMetrics fmType(typeFont_); + int typeWidth=fmType.width(" S"); + + if(drawNodeType_) + { + int nodeType=index.data(AbstractNodeModel::NodeTypeRole).toInt(); + switch(nodeType) + { + case 0: typeText.text_="S"; break; + case 1: typeText.text_="F"; break; + case 2: typeText.text_="T"; break; + case 3: typeText.text_="A"; break; + default: break; + } + } + + //The text rectangle + nodeText.text_=text; + int textWidth=fm.width(text); + + if(nodeStyle_ == BoxAndTextNodeStyle) + { + int realW=itemRect.height()/4; + + QRect r1(currentRight,itemRect.y(),itemRect.height(),itemRect.height()); + if(hasRealBg) + r1.adjust(0,0,-realW,0); + + stateShape.shape_=QPolygon(r1); + + if(hasRealBg) + { + QRect r2(r1.x()+r1.width(),r1.top(),realW+1,r1.height()); + realShape.shape_=QPolygon(r2); + currentRight=r2.x()+r2.width()+2; + } + else + currentRight=r1.x()+r1.width()+2; + + if(drawNodeType_) + { + typeText.br_=r1; + typeText.fgCol_=index.data(AbstractNodeModel::NodeTypeForegroundRole).value(); + } + + nodeText.br_=QRect(currentRight,r1.top(),textWidth+nodeBox_->leftPadding,r1.height()); + nodeText.fgCol_=QColor(Qt::black); + currentRight=nodeText.br_.x() + nodeText.br_.width(); + +#if 0 + if(nodeStyle_ == BoxAndTextNodeStyle) + { + int realW=itemRect.height()/4; + + //state box + currentRight+=itemRect.height(); + if(hasRealBg) + { + currentRight+=1; + } + currentRight+=2; + + //node name + currentRight+=textWidth+nodeBox_->leftPadding; + } +#endif + + } + //Classic style + else + { + if(drawNodeType_) + { + typeText.br_=QRect(currentRight,itemRect.y(),typeWidth,itemRect.height()); + typeText.fgCol_=typeFgColourClassic; + typeText.bgCol_=typeBgColourClassic; + currentRight=typeText.br_.x()+typeText.br_.width()+2; + } + + QRect r1(currentRight,itemRect.y(),textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding,itemRect.height()); + stateShape.shape_=QPolygon(r1); + currentRight=r1.x()+r1.width(); + + nodeText.br_=QRect(r1.left()+nodeBox_->leftPadding,r1.top(),textWidth,r1.height()); + nodeText.fgCol_=index.data(Qt::ForegroundRole).value(); + + if(hasRealBg) + { + int realW=6; + QRect r2(r1.x()+r1.width(),r1.top(),realW+1,r1.height()); + realShape.shape_=QPolygon(r2); + currentRight=r2.x()+r2.width(); + } + } + + //Icons area + QList pixLst; + QList pixRectLst; + + QVariant va=index.data(AbstractNodeModel::IconRole); + if(va.type() == QVariant::List) + { + QVariantList lst=va.toList(); + if(lst.count() >0) + { + int xp=currentRight+nodeBox_->iconPreGap; + int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; + for(int i=0; i < lst.count(); i++) + { + int id=lst[i].toInt(); + if(id != -1) + { + pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); + pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); + xp+=nodeBox_->iconSize+nodeBox_->iconGap; + } + } + + if(!pixLst.isEmpty()) + { + currentRight=xp-nodeBox_->iconGap; + } + } + } + + //The node number (optional) + QRect numRect; + QString numTxt; + bool hasNum=false; + + if(drawChildCount_) + { + va=index.data(AbstractNodeModel::NodeNumRole); + hasNum=(va.isNull() == false); + + if(hasNum) + { + numTxt="(" + va.toString() + ")"; + QFontMetrics fmNum(suiteNumFont_); + + int numWidth=fmNum.width(numTxt); + numRect = nodeText.br_; + numRect.setLeft(currentRight+fmNum.width('A')/2); + numRect.setWidth(numWidth); + currentRight=numRect.x()+numRect.width(); + } + } + + //The aborted reason + QRect reasonRect; + QString reasonTxt=index.data(AbstractNodeModel::AbortedReasonRole).toString(); + if(reasonTxt.contains('\n')) + reasonTxt=reasonTxt.split("\n").first(); + + bool hasReason=(!reasonTxt.isEmpty()); + if(hasReason) + { + QFontMetrics fmReason(abortedReasonFont_); + reasonRect = nodeText.br_; + reasonRect.setLeft(currentRight+fmReason.width('A')/2); + reasonTxt=fmReason.elidedText(reasonTxt,Qt::ElideRight,220); + reasonRect.setWidth(fmReason.width(reasonTxt)); + currentRight=reasonRect.x()+reasonRect.width(); + } + + //Define clipping + int rightPos=currentRight+1; + totalWidth=rightPos-option.rect.left(); + +#if 0 + const bool setClipRect = rightPos > option.rect.right(); + if(setClipRect) + { + painter->save(); + painter->setClipRect(option.rect); + } +#endif + + + renderNodeCell(painter,stateShape,realShape,nodeText,typeText,selected); + + //Draw icons + for(int i=0; i < pixLst.count(); i++) + { + //painter->fillRect(pixRectLst[i],Qt::black); + painter->drawPixmap(pixRectLst[i],pixLst[i]); + } + + //Draw number + if(hasNum) + { + painter->setPen(childCountColour); + painter->setFont(suiteNumFont_); + painter->drawText(numRect,Qt::AlignLeft | Qt::AlignVCenter,numTxt); + } + + //Draw aborted reason + if(hasReason) + { + painter->setPen(stateShape.col_.darker(120)); + painter->setFont(abortedReasonFont_); + painter->drawText(reasonRect,Qt::AlignLeft | Qt::AlignVCenter,reasonTxt); + } +#if 0 + if(setClipRect) + { + painter->restore(); + } +#endif + return totalWidth; +} + +void TreeNodeViewDelegate::renderServerCell(QPainter *painter,const NodeShape& stateShape, + const NodeText& text,bool selected) const +{ + renderNodeShape(painter,stateShape); + + //Draw text + painter->setFont(font_); + painter->setPen(QPen(text.fgCol_,0)); + painter->drawText(text.br_,Qt::AlignHCenter | Qt::AlignVCenter,text.text_); + + //selection + painter->setPen(nodeSelectPen_); + painter->setBrush(Qt::NoBrush); + QPolygon sel=stateShape.shape_; + + if(selected) + { + if(nodeStyle_ == BoxAndTextNodeStyle) + { + sel=sel.united(QPolygon(text.br_.adjusted(0,0,2,0))); + } + painter->drawRect(sel.boundingRect()); + } +} + +void TreeNodeViewDelegate::renderNodeCell(QPainter *painter,const NodeShape& stateShape,const NodeShape &realShape, + const NodeText& nodeText,const NodeText& typeText,bool selected) const +{ + renderNodeShape(painter,stateShape); + renderNodeShape(painter,realShape); + + //Draw type + if(drawNodeType_) + { + if(typeText.bgCol_.isValid()) + { + //painter->fillRect(typeText.br_,typeText.bgCol_); + painter->setBrush(typeText.bgCol_); + painter->setPen(typeText.bgCol_); + painter->drawRect(typeText.br_); + } + painter->setFont(typeFont_); + painter->setPen(typeText.fgCol_); + painter->setBrush(Qt::NoBrush); + painter->drawText(typeText.br_,Qt::AlignCenter,typeText.text_); + } + + //Draw text + painter->setFont(font_); + painter->setPen(QPen(nodeText.fgCol_,0)); + painter->drawText(nodeBox_->adjustTextRect(nodeText.br_),Qt::AlignHCenter | Qt::AlignVCenter,nodeText.text_); + + //selection + painter->setPen(nodeSelectPen_); + painter->setBrush(Qt::NoBrush); + QPolygon sel=stateShape.shape_; + + if(selected) + { + if(nodeStyle_ == BoxAndTextNodeStyle) + { + if(!realShape.shape_.isEmpty()) + sel=sel.united(realShape.shape_); + + sel=sel.united(QPolygon(nodeText.br_.adjusted(0,0,0,0))); + } + else + { + if(!realShape.shape_.isEmpty()) + sel=sel.united(realShape.shape_); + } + painter->drawRect(sel.boundingRect()); + } + + /* + + if(nodeStyle_ == BoxAndTextNodeStyle) + { + painter->setFont(typeFont_); + painter->setPen(); + painter->drawText(typeText.br_,Qt::AlignCenter,typeText.text_); + } + else + { + painter->setPen(Qt::NoPen); + + if(typeTxt == "S") + painter->setBrush(QColor(150,150,150)); //bgBrush); + else if(typeTxt == "F") + painter->setBrush(QColor(150,150,150)); //bgBrush); + else + painter->setBrush(QColor(190,190,190)); //bgBrush); + + painter->drawRect(typeRect); + + painter->setPen(Qt::white); + painter->setFont(typeFont_); + painter->drawText(typeRect,Qt::AlignCenter,typeTxt); + } + } + + //Draw text + painter->setFont(font_); + painter->setPen(QPen(fg,0)); + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + //selection + painter->setPen(nodeSelectPen_); + painter->setBrush(Qt::NoBrush); + QPolygon sel=stateShape.shape_; + + if(selected) + { + if(nodeStyle_ == BoxAndTextNodeStyle) + { + if(!realShape.shape_.isEmpty()) + sel=sel.united(realShape.shape_); + + sel=sel.united(QPolygon(textRect.adjusted(0,0,2,0))); + } + else + { + if(!realShape.shape_.isEmpty()) + sel=sel.united(realShape.shape_); + } + painter->drawRect(sel.boundingRect()); + //painter->drawPolygon(sel); + } +*/ + //painter->setRenderHints(QPainter::Antialiasing,false); +} + + +void TreeNodeViewDelegate::renderNodeShape(QPainter* painter,const NodeShape& shape) const +{ + if(shape.shape_.isEmpty()) + return; + + QColor bg=shape.col_; + QColor bgLight, borderCol; + Palette::statusColours(bg,bgLight,borderCol); + + QBrush bgBrush; + if(useStateGrad_) + { + grad_.setColorAt(0,bgLight); + grad_.setColorAt(1,bg); + bgBrush=QBrush(grad_); + } + else + bgBrush=QBrush(bg); + + //Fill shape + painter->setPen(borderCol); + painter->setBrush(bgBrush); + painter->drawPolygon(shape.shape_); +} + +void TreeNodeViewDelegate::renderTimer(QPainter *painter,QRect target,int remaining, int total) const +{ + QImage img(target.width(),target.height(),QImage::Format_ARGB32_Premultiplied); + QRect r=img.rect().adjusted(2,2,-2,-2); + img.fill(Qt::transparent); + QPainter p(&img); + p.setRenderHint(QPainter::Antialiasing,true); + + int angle=static_cast(360.*static_cast(total-remaining)/static_cast(total)); + /*angle-=90.; + if(angle >=0 && angle <= 90) angle=90-angle; + else + angle=450-angle;*/ + + QColor c(43,97,158); + + QBrush b(c); + p.setBrush(b); + p.setPen(c); + p.drawPie(r,90*16,-angle*16); + p.setBrush(Qt::NoBrush); + p.drawEllipse(r); + + painter->drawImage(target,img,img.rect()); +} + + +void TreeNodeViewDelegate::renderServerUpdate(QPainter* painter,const ServerUpdateData& data) const +{ + QFont font(font_); + font.setPointSize(font_.pointSize()-1); + QFontMetrics fm(font); + painter->setFont(font); + painter->setPen(Qt::black); + + QColor minCol=QColor(198,215,253); + QColor maxCol=QColor(43,97,158); + + QRect r1=data.br_; + r1.setWidth(fm.width(data.prevText_)); + painter->setPen(minCol); + //painter->setPen(Qt::red); + painter->drawText(r1,Qt::AlignLeft | Qt::AlignVCenter,data.prevText_); + + if(!data.prevText_.isEmpty()) + { + QRect r2=data.br_; + r2.setX(data.br_.right()-fm.width(data.nextText_)); + //painter->setPen(QColor(1,128,73)); + painter->setPen(maxCol); + painter->drawText(r2,Qt::AlignRight | Qt::AlignVCenter,data.nextText_); + + int dh=(data.br_.height()-fm.height()+1)/2; + QRect r=data.br_.adjusted(r1.width()+4,2*dh,-r2.width()-4,-2*dh); + + int pos=static_cast(data.prog_* r.width()); + QRect rPrev=r.adjusted(0,0,-(r.width()-pos),0); + + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(1,0); + QColor posCol=interpolate(minCol,maxCol,data.prog_); + + grad.setColorAt(0,minCol); + grad.setColorAt(1,posCol); + painter->setPen(Qt::NoPen); + painter->setBrush(grad); + painter->drawRect(rPrev); + + painter->setBrush(Qt::NoBrush); + painter->setPen(QColor(190,190,190)); + painter->drawRect(r); + } +} + +void TreeNodeViewDelegate::widthHintServer(const QModelIndex& index,int& itemWidth, QString text) const +{ + ServerHandler* server=static_cast(index.data(AbstractNodeModel::ServerPointerRole).value()); + Q_ASSERT(server); + + QFontMetrics fm(font_); + + //The initial filled rect. We only care of the width + QRect itemRect(nodeBox_->leftMargin,0,10,10); + int currentRight=itemRect.left(); + + NodeShape stateShape; + + NodeText nodeText; + nodeText.text_=text; + int textWidth=fm.width(text); + + if(nodeStyle_ == BoxAndTextNodeStyle) + { + currentRight+=2+textWidth+nodeBox_->leftPadding; + } + else + { + currentRight+=textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding; + } + + //Icons area + Q_ASSERT(model_); + int pixNum=model_->iconNum(server->vRoot()); + if(pixNum > 0) + { + currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; + } + + //The info rectangle (optional) + QString infoTxt=index.data(AbstractNodeModel::InfoRole).toString(); + bool hasInfo=(infoTxt.isEmpty() == false); + + if(hasInfo) + { + fm=QFontMetrics(serverInfoFont_); + int infoWidth=fm.width(infoTxt); + currentRight+=fm.width('A')+infoWidth; + } + + //The load icon (optional) + bool hasLoad=index.data(AbstractNodeModel::LoadRole).toBool(); + Animation* an=0; + + //Update load animation + if(hasLoad) + { + an=animation_->find(Animation::ServerLoadType,true); + currentRight+=fm.width('A')+an->scaledSize().width(); + } + //Stops load animation + else + { + //if((an=animation_->find(Animation::ServerLoadType,false)) != NULL) + // an->removeTarget(server->vRoot()); + } + + //The node number (optional) + QString numTxt; + bool hasNum=false; + + if(drawChildCount_) + { + QVariant va=index.data(AbstractNodeModel::NodeNumRole); + hasNum=(va.isNull() == false); + if(hasNum) + { + numTxt="(" + QString::number(va.toInt()) + ")"; + QFontMetrics fmNum(serverNumFont_); + int numWidth=fmNum.width(numTxt); + currentRight+=fmNum.width('A')/2+numWidth; + } + } + + itemWidth=currentRight+1; +} + +int TreeNodeViewDelegate::nodeWidth(const QModelIndex& index,QString text) const +{ + VNode* node=static_cast(index.data(AbstractNodeModel::NodePointerRole).value()); + Q_ASSERT(node); + + int offset=nodeBox_->leftMargin; + QFontMetrics fm(font_); + + //The initial filled rect. We only care about the width! + QRect itemRect(offset,0,10,10); + int currentRight=itemRect.left(); + + bool hasRealBg=node->isSuspended(); + + //Node type + QFontMetrics fmType(typeFont_); + int typeWidth=fmType.width(" S"); + + //The text rectangle + int textWidth=fm.width(text); + + if(nodeStyle_ == BoxAndTextNodeStyle) + { + //state box + currentRight+=itemRect.height(); + if(hasRealBg) + { + currentRight+=1; + } + currentRight+=2; + + //node name + currentRight+=textWidth+nodeBox_->leftPadding; + } + //Classic style + else + { + //node type box + if(drawNodeType_) + { + currentRight+=typeWidth+2; + } + + //node name + state + currentRight+=textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding; + + if(hasRealBg) + { + int realW=6; + currentRight+=realW+1; + } + } + + //Icons area + int pixNum=0; + + //in some subclasses we might not have a model_ + if(model_) + { + pixNum=model_->iconNum(node); + if(pixNum > 0) + { + currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; + } + } + else + { + QVariant va=index.data(AbstractNodeModel::IconRole); + if(va.type() == QVariant::List) + { + QVariantList lst=va.toList(); + pixNum=lst.count(); + } + } + + if(pixNum > 0) + { + currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; + } + + + //The node number (optional) + if(drawChildCount_) + { + if(node->isTopLevel()) + { + QVariant va=index.data(AbstractNodeModel::NodeNumRole); + if(va.isNull() == false) + { + QString numTxt="(" + va.toString() + ")"; + QFontMetrics fmNum(suiteNumFont_); + int numWidth=fmNum.width(numTxt); + currentRight+=fmNum.width('A')/2+numWidth; + } + } + } + + //The aborted reason + if(node->isAborted()) + { + QString reasonTxt=QString::fromStdString(node->abortedReason()); + if(reasonTxt.contains('\n')) + reasonTxt=reasonTxt.split("\n").first(); + + bool hasReason=(!reasonTxt.isEmpty()); + if(hasReason) + { + QFontMetrics fmReason(abortedReasonFont_); + reasonTxt=fmReason.elidedText(reasonTxt,Qt::ElideRight,220); + currentRight+=fmReason.width('A')/2+fmReason.width(reasonTxt); + } + } + + return currentRight+1; +} + + +QString TreeNodeViewDelegate::formatTime(int timeInSec) const +{ + int h=timeInSec/3600; + int r=timeInSec%3600; + int m=r/60; + int s=r%60; + + QTime t(h,m,s); + if(h > 0) + return t.toString("h:mm:ss"); + else + return t.toString("m:ss"); + + return QString(); +} + +QColor TreeNodeViewDelegate::interpolate(QColor c1,QColor c2,float r) const +{ + return QColor::fromRgbF(c1.redF()+r*(c2.redF()-c1.redF()), + c1.greenF()+r*(c2.greenF()-c1.greenF()), + c1.blueF()+r*(c2.blueF()-c1.blueF())); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeViewDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeViewDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,104 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TREENODEVIEWDELEGATEBASE_HPP +#define TREENODEVIEWDELEGATEBASE_HPP + +#include +#include +#include +#include + +#include "NodeViewDelegate.hpp" +#include "VProperty.hpp" + +#include + +class AnimationHandler; +class PropertyMapper; +class NodeShape; +class NodeText; +class ServerUpdateData; +class TreeNodeModel; + +class TreeNodeViewDelegate : public NodeViewDelegate +{ +Q_OBJECT + +public: + explicit TreeNodeViewDelegate(TreeNodeModel* model,QWidget *parent=0); + ~TreeNodeViewDelegate(); + + bool isSingleHeight(int h) const; + + //from baseclass + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const {} + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex& index ) const; + + //custom implementations + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index,QSize&) const; + void sizeHint(const QModelIndex& index,int& w,int& h) const; + + +Q_SIGNALS: + void sizeHintChangedGlobal(); + +protected: + void updateSettings(); + + int renderServer(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const; + + int renderNode(QPainter *painter,const QModelIndex& index, + const QStyleOptionViewItem& option,QString text) const; + + void renderServerCell(QPainter *painter,const NodeShape& stateShape, + const NodeText& text,bool selected) const; + + void renderNodeCell(QPainter *painter,const NodeShape& stateShape,const NodeShape &realShape, + const NodeText& nodeText,const NodeText& typeText,bool selected) const; + + void renderNodeShape(QPainter* painter,const NodeShape& shape) const; + void renderTimer(QPainter *painter,QRect target, int remaining, int total) const; + void renderServerUpdate(QPainter* painter,const ServerUpdateData&) const; + + void widthHintServer(const QModelIndex& index,int& itemWidth,QString text) const; + int nodeWidth(const QModelIndex& index,QString text) const; + + QString formatTime(int timeInSec) const; + QColor interpolate(QColor c1,QColor c2,float r) const; + + enum NodeStyle {ClassicNodeStyle,BoxAndTextNodeStyle}; + + AnimationHandler* animation_; + + int nodeRectRad_; + bool drawChildCount_; + NodeStyle nodeStyle_; + + bool drawNodeType_; + QColor typeBgCol_; + + QFont serverNumFont_; + QFont suiteNumFont_; + QFont serverInfoFont_; + QFont abortedReasonFont_; + QFont typeFont_; + QColor bgCol_; + + TreeNodeModel* model_; +}; + +#endif // TREENODEVIEWDELEGATEBASE_HPP + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,112 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TreeNodeView_HPP_ +#define TreeNodeView_HPP_ + +#include + +#include "NodeViewBase.hpp" + +#include "VInfo.hpp" +#include "VProperty.hpp" + +class AbstractNodeView; +class ActionHandler; +class Animation; +class ExpandNode; +class TableNodeSortModel; +class PropertyMapper; +class TreeNodeModel; +class StandardNodeViewDelegategate; +class VTreeNode; + +class TreeNodeView : public QObject, public NodeViewBase, public VPropertyObserver +{ +Q_OBJECT + +public: + TreeNodeView(AbstractNodeView* view,TreeNodeModel* model,NodeFilterDef* filterDef,QWidget *parent=0); + ~TreeNodeView(); + + void reload(); + void rerender(); + QWidget* realWidget(); + QObject* realObject(); + VInfo_ptr currentSelection(); + void setCurrentSelection(VInfo_ptr n); + void selectFirstServer(); + + void notifyChange(VProperty* p); + + void readSettings(VSettings* vs) {} + void writeSettings(VSettings* vs) {} + +public Q_SLOTS: + void slotContextMenu(const QPoint &position); + void slotViewCommand(VInfo_ptr,QString); +#if 0 + void slotSaveExpand(); + void slotRestoreExpand(); +#endif + void slotSaveExpand(const VTreeNode* node); + void slotRestoreExpand(const VTreeNode* node); + void slotRepaint(Animation*); + void slotRerender(); + void slotSizeHintChangedGlobal(); + +protected Q_SLOTS: + void slotDoubleClickItem(const QModelIndex&); + void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); + +Q_SIGNALS: + void selectionChanged(VInfo_ptr); + void infoPanelCommand(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + +protected: + QModelIndexList selectedList(); + void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); + + void saveExpand(ExpandNode *parentExpand,const QModelIndex& idx); + void restoreExpand(ExpandNode *expand,const VNode* node); + void expandTo(const QModelIndex& idxTo); + void saveExpandAll(const QModelIndex& idx); + void saveCollapseAll(const QModelIndex& idx); + + void setCurrentSelectionFromExpand(VInfo_ptr info); + void regainSelectionFromExpand(); + + void adjustBackground(QColor col); + void adjustIndentation(int); + void adjustAutoExpandLeafNode(bool b); + void adjustDrawBranchLine(bool b); + void adjustBranchLineColour(QColor col); + void adjustServerToolTip(bool); + void adjustNodeToolTip(bool); + void adjustAttributeToolTip(bool); + + AbstractNodeView *view_; + TreeNodeModel* model_; + ActionHandler* actionHandler_; + bool needItemsLayout_; + PropertyMapper* prop_; + QMap styleSheet_; + bool setCurrentIsRunning_; + bool setCurrentFromExpandIsRunning_; + bool canRegainCurrentFromExpand_; + VInfo_ptr lastSelection_; + bool inStartUp_; +}; + +#endif + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,354 @@ + +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#include "TreeNodeWidget.hpp" + +#include +#include + +#include "AbstractNodeModel.hpp" +#include "DashboardDock.hpp" +#include "CompactView.hpp" +#include "StandardView.hpp" +#include "NodePathWidget.hpp" +#include "NodeViewBase.hpp" +#include "TreeNodeModel.hpp" +#include "TreeNodeView.hpp" +#include "UiLog.hpp" +#include "VFilter.hpp" +#include "VConfig.hpp" +#include "VModelData.hpp" +#include "VSettings.hpp" +#include "WidgetNameProvider.hpp" + +#include "FilterWidget.hpp" + +AttributeFilter* TreeNodeWidget::lastAtts_=NULL; + +TreeNodeWidget::TreeNodeWidget(ServerFilter* serverFilter,QWidget* parent) : + NodeWidget("tree",serverFilter,parent), + viewLayoutMode_(StandardLayoutMode), + layoutProp_(0) +{ + //Init qt-creator form + setupUi(this); + + if(!lastAtts_) + { + lastAtts_=new AttributeFilter(); + } + + initAtts(); + + //Create the breadcrumbs (it will be reparented) + bcWidget_=new NodePathWidget(this); + + //This defines how to filter the nodes in the tree. We only want to filter according to node status. + filterDef_=new NodeFilterDef(serverFilter_,NodeFilterDef::NodeStateScope); + + //Create the tree model. It uses the datahandler to access the data. + model_=new TreeNodeModel(serverFilter_,filterDef_,atts_,icons_,this); + + //Create the view + QHBoxLayout *hb=new QHBoxLayout(viewHolder_); + hb->setContentsMargins(0,0,0,0); + hb->setSpacing(0); + + layoutProp_=VConfig::instance()->find("view.tree.layoutStyle"); + Q_ASSERT(layoutProp_); + layoutProp_->addObserver(this); + + if(layoutProp_->value() == "compact") + { + viewLayoutMode_=CompactLayoutMode; + } + + Q_ASSERT(view_==0); + setViewLayoutMode(viewLayoutMode_); + + connect(model_,SIGNAL(firstScanEnded(const VTreeServer*)), + this,SLOT(firstScanEnded(const VTreeServer*))); + + connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), + this,SLOT(slotSelectionChangedInBc(VInfo_ptr))); + + connect(atts_,SIGNAL(changed()), + this,SLOT(slotAttsChanged())); + + //This will not emit the trigered signal of the action!! + //Synchronise the action and the breadcrumbs state + actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); + + //The node status filter is exposed via a menu. So we need a reference to it. + states_=filterDef_->nodeState(); + + viewHolder_->setObjectName("h"); + WidgetNameProvider::nameChildren(this); +} + +TreeNodeWidget::~TreeNodeWidget() +{ +} + +void TreeNodeWidget::setViewLayoutMode(TreeNodeWidget::ViewLayoutMode mode) +{ + if(view_ && viewLayoutMode_ == mode) + return; + + viewLayoutMode_ = mode; + + if(view_) + { + QWidget *realW=view_->realWidget(); + viewHolder_->layout()->removeWidget(realW); + delete view_; + view_=0; + delete realW; + model_->data()->deleteExpandState(); + } + + if(viewLayoutMode_ == CompactLayoutMode) + { + TreeNodeModel* realModel=static_cast(model_); + + TreeNodeView* gv=new TreeNodeView(new CompactView(realModel,this), + realModel,filterDef_,this); + viewHolder_->layout()->addWidget(gv->realWidget()); + //Store the pointer to the (non-QObject) base class of the view!!! + view_=gv; + } + else + { + TreeNodeModel* realModel=static_cast(model_); + + TreeNodeView *tv=new TreeNodeView(new StandardView(realModel,this), + realModel,filterDef_,this); + viewHolder_->layout()->addWidget(tv->realWidget()); + //Store the pointer to the (non-QObject) base class of the view!!! + view_=tv; + } + + //Signals-slots + connect(view_->realObject(),SIGNAL(selectionChanged(VInfo_ptr)), + this,SLOT(slotSelectionChangedInView(VInfo_ptr))); + + connect(view_->realObject(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(popInfoPanel(VInfo_ptr,QString))); + + connect(view_->realObject(),SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + connect(model_,SIGNAL(clearBegun(const VTreeNode*)), + view_->realObject(),SLOT(slotSaveExpand(const VTreeNode*))); + + connect(model_,SIGNAL(scanEnded(const VTreeNode*)), + view_->realObject(),SLOT(slotRestoreExpand(const VTreeNode*))); + + connect(model_,SIGNAL(rerender()), + view_->realObject(),SLOT(slotRerender())); + + connect(model_,SIGNAL(filterUpdateRemoveBegun(const VTreeNode*)), + view_->realObject(),SLOT(slotSaveExpand(const VTreeNode*))); + + connect(model_,SIGNAL(filterUpdateAddEnded(const VTreeNode*)), + view_->realObject(),SLOT(slotRestoreExpand(const VTreeNode*))); + +} + +void TreeNodeWidget::initAtts() +{ + if(VProperty *prop=VConfig::instance()->find("view.tree.attributesPolicy")) + { + if(prop->valueAsStdString() == "last") + { + atts_->setCurrent(lastAtts_->current()); + } + else if(VProperty *propDef=VConfig::instance()->find("view.tree.defaultAttributes")) + { + atts_->setCurrent(propDef->value().toString().split("/")); + } + } +} + +void TreeNodeWidget::populateDockTitleBar(DashboardDockTitleWidget* tw) +{ + //Builds the menu for the settings tool button + QMenu *menu=new QMenu(this); + menu->setTearOffEnabled(true); + + menu->addAction(actionBreadcrumbs); + QMenu *menuState=new QMenu(this); + QMenu *menuType=new QMenu(this); + QMenu *menuIcon=menu->addMenu(tr("Icon")); + + menuState->setTearOffEnabled(true); + menuType->setTearOffEnabled(true); + menuIcon->setTearOffEnabled(true); + + //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); + attrFilterMenu_=new VParamFilterMenu(menuType,atts_,"Show attributes",VParamFilterMenu::ShowMode); + iconFilterMenu_=new VParamFilterMenu(menuIcon,icons_,"Show icons",VParamFilterMenu::ShowMode, + VParamFilterMenu::PixmapDecor); + stateFilterMenu_=new VParamFilterMenu(menuState,states_,"Show statuses", + VParamFilterMenu::ShowMode,VParamFilterMenu::ColourDecor); + + //Sets the menu on the toolbutton + tw->optionsTb()->setMenu(menu); + + //Add the bc to the titlebar (bc will have a new parent) + tw->setBcWidget(bcWidget_); + + QList acLst; + + QAction* acState=new QAction(this); + acState->setIcon(QPixmap(":viewer/status.svg")); + acState->setToolTip("Filter by status"); + acState->setMenu(menuState); + acLst << acState; + + QAction* acAttr=new QAction(this); + acAttr->setIcon(QPixmap(":viewer/attribute.svg")); + acAttr->setToolTip("Show attributes"); + acAttr->setMenu(menuType); + acLst << acAttr; + + tw->addActions(acLst); +} + +void TreeNodeWidget::slotSelectionChangedInView(VInfo_ptr info) +{ + updateActionState(info); + bcWidget_->setPath(info); + if(broadcastSelection()) + Q_EMIT selectionChanged(info); +} + + +void TreeNodeWidget::on_actionBreadcrumbs_triggered(bool b) +{ + if(b) + { + bcWidget_->setMode(NodePathWidget::GuiMode); + } + else + { + bcWidget_->setMode(NodePathWidget::TextMode); + } +} + +void TreeNodeWidget::rerender() +{ + bcWidget_->rerender(); + view_->rerender(); +} + + +bool TreeNodeWidget::initialSelectionInView() +{ + //Seeting the initail selection is probably unsuccessful because at + //this point the servers are probably not fully loaded + VInfo_ptr selInfo=VInfo::createFromPath(firstSelectionPath_); + if(selInfo) + view_->setCurrentSelection(selInfo); + else + view_->selectFirstServer(); + + return true; +} + +//When the first successful scan ended we try to set the initial selection +void TreeNodeWidget::firstScanEnded(const VTreeServer* s) +{ + VInfo_ptr selInfo=VInfo::createFromPath(firstSelectionPath_); + if(selInfo && selInfo->server() == s->realServer()) + { + view_->setCurrentSelection(selInfo); + } +} + +void TreeNodeWidget::slotAttsChanged() +{ + lastAtts_->setCurrent(atts_->current()); +} + +void TreeNodeWidget::notifyChange(VProperty* p) +{ + if(p == layoutProp_) + { + if(layoutProp_->value() == "compact") + { + setViewLayoutMode(CompactLayoutMode); + } + else + { + setViewLayoutMode(StandardLayoutMode); + } + } +} + +void TreeNodeWidget::writeSettings(VComboSettings* vs) +{ + vs->put("type",type_); + vs->put("dockId",id_); + + VInfo_ptr sel=currentSelection(); + if(sel) + { + vs->put("selection",sel->storedPath()); + } + + bcWidget_->writeSettings(vs); + + states_->writeSettings(vs); + atts_->writeSettings(vs); + icons_->writeSettings(vs); + + DashboardWidget::writeSettings(vs); +} + +void TreeNodeWidget::readSettings(VComboSettings* vs) +{ + std::string type=vs->get("type",""); + if(type != type_) + return; + + //The selection on last exit. We will use it later when the server is fully loaded. + firstSelectionPath_=vs->get("selection",""); + + //This will not emit the changed signal. So the "observers" will + //not notice the change. + states_->readSettings(vs); + atts_->readSettings(vs); + icons_->readSettings(vs); + + lastAtts_->setCurrent(atts_->current()); + + //The model at this point is inactive (not using its data). We make it active: + // -it will instruct its data provider to filter the data according + // to the current settings + // -it will load and display the data + model_->active(true); + + //-------------------------- + //Breadcrumbs + //-------------------------- + + bcWidget_->readSettings(vs); + + //Synchronise the action and the breadcrumbs state + //This will not emit the trigered signal of the action!! + actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); + + attrFilterMenu_->reload(); + iconFilterMenu_->reload(); + stateFilterMenu_->reload(); + + DashboardWidget::readSettings(vs); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +/***************************** LICENSE START *********************************** + + Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms + of the Apache License version 2.0. In applying this license, ECMWF does not + waive the privileges and immunities granted to it by virtue of its status as + an Intergovernmental Organization or submit itself to any jurisdiction. + + ***************************** LICENSE END *************************************/ + +#ifndef TREENODEWIDGET_HPP_ +#define TREENODEWIDGET_HPP_ + +#include "ui_TreeNodeWidget.h" + +#include "NodeWidget.hpp" +#include "VProperty.hpp" + +class AttributeFilter; +class NodeStateFilter; +class ServerFilter; +class VParamFilterMenu; +class VSettings; +class VTreeServer; +class TreeNodeWidget : public NodeWidget, public VPropertyObserver, protected Ui::TreeNodeWidget +{ +Q_OBJECT + +public: + TreeNodeWidget(ServerFilter*,QWidget* parent=0); + ~TreeNodeWidget(); + + void populateDockTitleBar(DashboardDockTitleWidget* tw); + + void rerender(); + bool initialSelectionInView(); + void writeSettings(VComboSettings*); + void readSettings(VComboSettings*); + + void notifyChange(VProperty*); + +protected Q_SLOTS: + void on_actionBreadcrumbs_triggered(bool b); + void slotSelectionChangedInView(VInfo_ptr info); + void slotAttsChanged(); + void firstScanEnded(const VTreeServer*); + +protected: + enum ViewLayoutMode {StandardLayoutMode,CompactLayoutMode}; + + void initAtts(); + void detachedChanged() {} + void setViewLayoutMode(ViewLayoutMode); + + VParamFilterMenu *stateFilterMenu_; + VParamFilterMenu *attrFilterMenu_; + VParamFilterMenu *iconFilterMenu_; + + ViewLayoutMode viewLayoutMode_; + VProperty* layoutProp_; + + static AttributeFilter* lastAtts_; + + std::string firstSelectionPath_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeNodeWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeNodeWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,68 @@ + + + TreeNodeWidget + + + + 0 + 0 + 683 + 491 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + + + + + true + + + Breadcrumbs + + + Sho/hide breadcrumbs + + + + + true + + + Frozen + + + Do not update panel when node changes + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,28 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TreeView.hpp" + +#include + +TreeView::TreeView(QWidget* parent) : QTreeView(parent) +{ + //!!!!We need to do it because: + //The background colour between the views left border and the nodes cannot be + //controlled by delegates or stylesheets. It always takes the QPalette::Highlight + //colour from the palette. Here we set this to transparent so that Qt could leave + //this are empty and we will fill it appropriately in our delegate. + + QPalette pal=palette(); + pal.setColor(QPalette::Highlight,QColor(255,255,255,0)); + setPalette(pal); +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TreeView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TreeView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TreeView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TreeView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,21 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ +#ifndef VIEWER_SRC_TREEVIEW_HPP_ +#define VIEWER_SRC_TREEVIEW_HPP_ + +#include + +class TreeView : public QTreeView +{ +public: + explicit TreeView(QWidget* parent=0); +}; + +#endif /* VIEWER_SRC_TREEVIEW_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerCollector.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerCollector.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerCollector.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerCollector.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,197 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggerCollector.hpp" + +#include "UiLog.hpp" +#include "VAttribute.hpp" +#include "VItem.hpp" +#include "VItemPathParser.hpp" +#include "VNode.hpp" + +#include + +#define _UI_TRIGGERCOLLECTOR_DEBUG + +TriggerListCollector::~TriggerListCollector() +{ + clear(); +} + +bool TriggerListCollector::add(VItem* t, VItem* dep,Mode mode) +{ + TriggerListItem *item=new TriggerListItem(t,dep,mode) ; + items_.push_back(item); + return true; + +#if 0 + if(dep) + { + UiLog().dbg() << " dep=" << dep->typeName() << " " + dep->strName(); + UiLog().dbg() << " =" << item->dep_->typeName() << " " << item->dep_->strName()); + } +#endif +} + +void TriggerListCollector::setDependency(bool b) +{ + extended_=b; + clear(); +} + +void TriggerListCollector::clear() +{ + /*for(size_t i=0; i < items_.size(); i++) + { + delete items_[i]; + }*/ + items_.clear(); +} + + +bool TriggerChildCollector::add(VItem* t, VItem*,Mode) +{ + if(!t->isAncestor(node_)) + { + // child_ is a kid of node_ whose trigger is outside its subtree + return collector_->add(t,child_,TriggerCollector::Child); + } + return false; +} + +bool TriggerParentCollector::add(VItem* t, VItem*,Mode) +{ + return collector_->add(t,parent_,TriggerCollector::Parent); +} + +bool TriggeredCollector::add(VItem* trigger, VItem*,Mode) +{ + if(VNode *n=trigger->isNode()) + { + n->addTriggeredData(node_); + } + return false; + + //else if(trigger->isAttribute()) + // trigger->parent()->addTriggeredData(node_,trigger); +} + +const std::set& TriggerTableItem::modes() const +{ + if(modes_.empty()) + { + for(std::size_t i=0; i < deps_.size(); i++) + { + modes_.insert(deps_[i].mode()); + } + } + return modes_; +} + +//===================================== +// TriggerTableCollector +//===================================== + +TriggerTableCollector::~TriggerTableCollector() +{ + clear(); +} + +bool TriggerTableCollector::add(VItem* trigger, VItem* dep,Mode mode) +{ + Q_ASSERT(trigger); + + TriggerTableItem *item=0; + for(std::size_t i=0; i < items_.size(); i++) + { + if(items_[i]->item() == trigger) + { + item=items_[i]; + break; + } + } + + if(!item) + { + item=new TriggerTableItem(trigger); + items_.push_back(item); + } + + item->addDependency(dep,mode); + return true; +} + +void TriggerTableCollector::setDependency(bool b) +{ + extended_=b; + clear(); +} + +void TriggerTableCollector::clear() +{ + for(size_t i=0; i < items_.size(); i++) + { + delete items_[i]; + } + items_.clear(); +} + +bool TriggerTableCollector::contains(TriggerTableItem* item) const +{ + return (std::find(items_.begin(),items_.end(), item) != items_.end()); +} + +bool TriggerTableCollector::contains(const VNode* node,bool attrParents) const +{ + for(size_t i=0; i < items_.size(); i++) + { + if(VItem* it=items_[i]->item()) + { + if(VNode *n=it->isNode()) + { + if(n == node) + return true; + } + else if(attrParents) + { + if (VAttribute *a=it->isAttribute()) + if(a->parent() == node) + return true; + } + } + + } + + return false; +} + +TriggerTableItem* TriggerTableCollector::find(const VItem* item) const +{ + for(size_t i=0; i < items_.size(); i++) + { + if(items_[i]->item() == item) + return items_[i]; + } + return 0; +} + +TriggerTableItem* TriggerTableCollector::findByContents(const VItem* item) const +{ + if(!item) + return 0; + + for(size_t i=0; i < items_.size(); i++) + { + if(item->sameContents(items_[i]->item())) + { + return items_[i]; + } + } + return 0; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerCollector.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerCollector.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerCollector.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerCollector.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,203 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TRIGGERCOLLECTOR_HPP +#define TRIGGERCOLLECTOR_HPP + +#include +#include +#include + +#include + +class TriggerListItem; +class VItem; + +#include "VNode.hpp" + +class TriggerCollector +{ +public: + TriggerCollector() {} + virtual ~TriggerCollector() {} + + enum Mode { Normal = 0, // Normal trigger_node + Parent = 1, // Through parent + Child = 2, // Through child + Hierarchy = 3 // Through child + }; + + virtual bool add(VItem*, VItem*,Mode) = 0; + virtual bool scanParents() { return false; } + virtual bool scanKids() { return false; } + virtual bool scanSelf() { return true; } + +private: + TriggerCollector(const TriggerCollector&); + TriggerCollector& operator=(const TriggerCollector&); +}; + +class TriggerListCollector : public TriggerCollector +{ +public: + TriggerListCollector(bool extended) : + extended_(extended) {} + + ~TriggerListCollector(); + bool add(VItem*, VItem*,Mode); + bool scanParents() { return extended_; } + bool scanKids() { return extended_; } + void setDependency(bool); + void clear(); + size_t size() const {return items_.size();} + + const std::vector& items() const {return items_;} + +protected: + bool extended_; + std::vector items_; +}; + +class TriggerChildCollector : public TriggerCollector +{ +public: + TriggerChildCollector(VItem *n,VItem* child,TriggerCollector* collector) : + node_(n), child_(child), collector_(collector) {} + + bool add(VItem*, VItem*,Mode); + +private: + VItem* node_; + VItem* child_; + TriggerCollector* collector_; +}; + +class TriggerParentCollector : public TriggerCollector +{ +public: + TriggerParentCollector(VItem* parent,TriggerCollector* collector) : + parent_(parent), collector_(collector) {} + + bool add(VItem*, VItem*,Mode); + +private: + VItem* parent_; + TriggerCollector* collector_; +}; + +class TriggeredCollector : public TriggerListCollector +{ +public: + TriggeredCollector(VNode* n) : + TriggerListCollector(false), node_(n) {} + bool add(VItem*, VItem*,Mode); + +private: + VItem* node_; +}; + +class TriggerListItem +{ +public: + TriggerListItem(VItem* t,VItem* dep,TriggerCollector::Mode mode) : + t_(t), dep_(dep), mode_(mode) {} + + VItem* item() const {return t_;} + VItem* dep() const {return dep_;} + TriggerCollector::Mode mode() const {return mode_;} + +protected: + VItem* t_; //trigger or triggered + VItem* dep_; + TriggerCollector::Mode mode_; +}; + +class TriggerDependencyItem +{ +public: + TriggerDependencyItem(VItem* dep,TriggerCollector::Mode mode) : + dep_(dep), mode_(mode) {} + + VItem* dep() const {return dep_;} + TriggerCollector::Mode mode() const {return mode_;} + +protected: + VItem* dep_; + TriggerCollector::Mode mode_; +}; + +class TriggerTableItem +{ +public: + TriggerTableItem(VItem* t) :t_(t){} + + void addDependency(VItem* dep,TriggerCollector::Mode mode) + {deps_.push_back(TriggerDependencyItem(dep,mode));} + + VItem* item() const {return t_;} + const std::vector& dependencies() const {return deps_;} + const std::set& modes() const; + +protected: + VItem* t_; //trigger or triggered + std::vector deps_; + mutable std::set modes_; +}; + + +class TriggerTableCollector : public TriggerCollector +{ +public: + TriggerTableCollector(bool extended) : + extended_(extended) {} + + ~TriggerTableCollector(); + bool add(VItem*, VItem*,Mode); + bool scanParents() { return extended_; } + bool scanKids() { return extended_; } + void setDependency(bool); + void clear(); + size_t size() const {return items_.size();} + + bool contains(TriggerTableItem*) const; + bool contains(const VNode*,bool attrParents=true) const; + TriggerTableItem* find(const VItem* item) const; + TriggerTableItem* findByContents(const VItem* item) const; + const std::vector& items() const {return items_;} + +protected: + bool extended_; + std::vector items_; +}; + +#if 0 +class nl1 : public trigger_lister { + int n_; + graph_layout& t_; + node* g_; + bool e_; +public: + + nl1(graph_layout& t,node* g,bool e) : n_(0), t_(t), g_(g), e_(e) {} + + void next_node(node& n,node* p,int mode,node* t) { + t_.relation(&n,g_,p,mode,t); + n_++; + } + + Boolean parents() { return e_; } + Boolean kids() { return e_; } + + int count() { return n_; } +}; +#endif + + +#endif // TRIGGERCOLLECTOR_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/trigger.css ecflow-4.11.1/Viewer/ecflowUI/src/trigger.css --- ecflow-4.9.0/Viewer/ecflowUI/src/trigger.css 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/trigger.css 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,85 @@ +/*============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================*/ + +p +{ + border:3px; + border-style:solid; + border-color:#FF0000; + padding: 1em; +} + +a:link +{ + text-decoration:none; + color: #0645AD; +} + +a:hover +{ + text-decoration:bold; + color: #0645AD; +} + +a:link.chp +{ + text-decoration:none; + color: #1a1f20; + font-weight: bold; +} + +th +{ + padding-left: 4px; + padding-top: 4px; + padding-bottom: 4px; + background-color: #5f6e95; + color: #ffffff; + font-size: 12px; + text-align: left; +} + +td.trigger_title { + padding-left: 2px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #5f6e95; + color: #ffffff; +} + +td.direct_title { + padding-left: 2px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #5f6e95; + color: #ffffff; +} + +td.title { + padding-left: 2px; + padding-top: 1px; + padding-bottom: 1px; + background-color: #d9e0ef; + /*background-color: #98a7c2;*/ + color: #1a1f20; +} + +td.trigger { + padding-left: 2px; + padding-top: 2px; + padding-bottom: 10px; + /*background-color: #5f6e95; + color: #ffffff;*/ +} +td +{ + background-color: #F5F5F5; + color: #000000; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,162 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TriggerEditor.hpp" + +#include + +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "Highlighter.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "SessionHandler.hpp" + +TriggerEditorWidget::TriggerEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); + +#if 0 + QLayoutItem *item; + item=grid_->itemAtPosition(1,0); + Q_ASSERT(item); + item->setAlignment(Qt::AlignLeft|Qt::AlignTop); +#endif + + //The document becomes the owner of the highlighte + new Highlighter(te_->document(),"trigger"); + te_->setShowLineNumbers(false); +} + +TriggerEditor::TriggerEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"trigger",parent) +{ + w_=new TriggerEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "trigger"); + + //Data is built dynamically so we store it + QStringList data=a->data(); + + if(data.count() != 3) + return; + + QString txt=data[2]; + + oriText_=txt; + w_->te_->setPlainText(txt); + + QString typeInHeader; + if(data[1]=="0") + { + typeInCmd_="trigger"; + typeInHeader="Trigger"; + } + else + { + typeInCmd_="complete"; + typeInHeader="Complete"; + } + typeInHeader+=" expression"; + + header_->setInfo(QString::fromStdString(info_->path()),typeInHeader); + + connect(w_->te_,SIGNAL(textChanged()), + this,SLOT(slotValueChanged())); + + checkButtonStatus(); + + readSettings(); +} + +TriggerEditor::~TriggerEditor() +{ + writeSettings(); +} + +void TriggerEditor::apply() +{ + if(typeInCmd_.isEmpty()) + return; + + std::string txt=w_->te_->toPlainText().toStdString(); + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change",typeInCmd_.toStdString(),txt); + CommandHandler::run(info_,cmd); +} + +void TriggerEditor::resetValue() +{ + w_->te_->setPlainText(oriText_); + checkButtonStatus(); +} + +void TriggerEditor::slotValueChanged() +{ + checkButtonStatus(); +} + +bool TriggerEditor::isValueChanged() +{ + return (oriText_ != w_->te_->toPlainText()); +} + +void TriggerEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("TriggerEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.setValue("fontSize",w_->te_->font().pointSize()); + settings.endGroup(); +} + +void TriggerEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("TriggerEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(310,200)); + } + + if(settings.contains("fontSize")) + { + QFont f=w_->te_->font(); + int fSize=settings.value("fontSize").toInt(); + if(fSize > 0 && fSize < 100) + f.setPointSize(fSize); + + w_->te_->setFont(f); + } + + settings.endGroup(); +} + +static AttributeEditorMaker makerStr("trigger"); + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,51 @@ +#ifndef TRIGGEREDITOR_HPP +#define TRIGGEREDITOR_HPP + +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ui_TriggerEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +class LabelEditor; + +class TriggerEditorWidget : public QWidget, protected Ui::TriggerEditorWidget +{ +friend class TriggerEditor; +public: + TriggerEditorWidget(QWidget *parent=0); +}; + +class TriggerEditor : public AttributeEditor +{ +Q_OBJECT +public: + TriggerEditor(VInfo_ptr,QWidget* parent=0); + ~TriggerEditor(); + +protected Q_SLOTS: + void slotValueChanged(); + +protected: + void apply(); + void resetValue(); + bool isValueChanged(); + void readSettings(); + void writeSettings(); + + TriggerEditorWidget* w_; + QString oriText_; + QString typeInCmd_; +}; + +#endif // TRIGGEREDITOR_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ + + + TriggerEditorWidget + + + + 0 + 0 + 329 + 227 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 1 + + + + + + + + + PlainTextEdit + QPlainTextEdit +
    PlainTextEdit.hpp
    +
    +
    + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggeredScanner.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggeredScanner.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggeredScanner.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggeredScanner.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,64 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggeredScanner.hpp" + +#include + +#include "TriggerCollector.hpp" +#include "VNode.hpp" + +void TriggeredScanner::clear() +{ + total_=0; + current_=0; +} + +void TriggeredScanner::start(VServer* s) +{ + clear(); + assert(s); + total_=s->totalNum(); + current_=0; + Q_EMIT scanStarted(); + scan(s); + Q_EMIT scanFinished(); + clear(); +} + +//Scan the the whole tree to find for each node all the nodes that it or its +//attributes trigger. +void TriggeredScanner::scan(VNode *n) +{ + TriggeredCollector tc(n); + n->triggers(&tc); + + updateProgress(); + + for(int i=0; i < n->numOfChildren(); i++) + scan(n->childAt(i)); +} + +void TriggeredScanner::updateProgress() +{ + current_++; + if(current_ > 0 && current_ % batchSize_ == 0) + { + Q_EMIT scanProgressed(progress()); + } +} + +int TriggeredScanner::progress() const +{ + if(total_ > 0) + { + return static_cast(100.*static_cast(current_)/static_cast(total_)); + } + return 0; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggeredScanner.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggeredScanner.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggeredScanner.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggeredScanner.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TRIGGEREDSCANNER_HPP +#define TRIGGEREDSCANNER_HPP + +#include + +class VNode; +class VServer; + +class TriggeredScanner : public QObject +{ +Q_OBJECT + +public: + TriggeredScanner(QObject* parent) : QObject(parent), total_(0), current_(0), batchSize_(100) {} + + void clear(); + void start(VServer*); + +Q_SIGNALS: + void scanStarted(); + void scanFinished(); + void scanProgressed(int percent); + +private: + void scan(VNode*); + void updateProgress(); + int progress() const; + + int total_; + int current_; + int batchSize_; +}; + +#endif // TRIGGEREDSCANNER_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,399 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggerItemWidget.hpp" + +#include "Highlighter.hpp" +#include "ServerHandler.hpp" +#include "TriggerCollector.hpp" +#include "TriggeredScanner.hpp" +#include "VNode.hpp" +#include "VSettings.hpp" + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) +#include +#endif + +//======================================================== +// +// TriggerItemWidget +// +//======================================================== + +TriggerItemWidget::TriggerItemWidget(QWidget *parent) : QWidget(parent) +{ + //This item will listen to any changes in nodes + handleAnyChange_=true; + + //We will not keep the contents when the item becomes unselected + unselectedFlags_.clear(); + + setupUi(this); + + //The collectors + triggerCollector_=new TriggerTableCollector(false); + triggeredCollector_=new TriggerTableCollector(false); + + //Scanner + scanner_=new TriggeredScanner(this); + + connect(scanner_,SIGNAL(scanStarted()), + this,SLOT(scanStarted())); + + connect(scanner_,SIGNAL(scanFinished()), + this,SLOT(scanFinished())); + + connect(scanner_,SIGNAL(scanProgressed(int)), + this,SLOT(scanProgressed(int))); + + //Messages + messageLabel_->hide(); + messageLabel_->setShowTypeTitle(false); + + //Dependency info inti must precede dependency init + //since they depend on each other + + //dependency info is off by default + dependInfoTb_->setChecked(false); + on_dependInfoTb__toggled(false); + + //Dependency is off by default + dependTb_->setProperty("triggerDepend","1"); + dependTb_->setChecked(false); + on_dependTb__toggled(false); + + //Expression + exprTb_->setChecked(true); + + //The document becomes the owner of the highlighter + new Highlighter(exprTe_->document(),"trigger"); + exprTe_->setReadOnly(true); + exprTe_->setBackgroundVisible(true); + + //Set the height of the trigger expression display area + QFont fTe; + fTe.setBold(true); + QFontMetrics fm(fTe); + exprTe_->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); + exprTe_->setFixedHeight(fm.size(0,"A\nA\nA").height()+fm.height()/2); + + connect(triggerTable_,SIGNAL(depInfoWidgetClosureRequested()), + this,SLOT(slotHandleDefInfoWidgetClosure())); + + connect(triggerTable_,SIGNAL(linkSelected(VInfo_ptr)), + this,SLOT(slotLinkSelected(VInfo_ptr))); + + connect(triggerTable_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SLOT(slotInfoPanelCommand(VInfo_ptr,QString))); + + connect(triggerTable_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SLOT(slotDashboardCommand(VInfo_ptr,QString))); +} + +TriggerItemWidget::~TriggerItemWidget() +{ + clearContents(); + delete triggerCollector_; + delete triggeredCollector_; +} + +QWidget* TriggerItemWidget::realWidget() +{ + return this; +} + +void TriggerItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + + //set the info + adjust(info); + + //messageLabel_->hide(); + + //Info must be a node + load(); +} + +void TriggerItemWidget::load() +{ + clearTriggers(); + + if(info_ && info_->isNode() && info_->node()) + { + VNode* n=info_->node(); + Q_ASSERT(n); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); +#endif + + //Display trigger expression + std::string te,ce; + n->triggerExpr(te,ce); + QString txt=QString::fromStdString(te); + if(txt.isEmpty()) txt=tr("No trigger expression is available for the selected node!"); + exprTe_->setPlainText(txt); + + triggerTable_->setInfo(info_); + + //Load table + triggerTable_->beginTriggerUpdate(); + + //collect the list of triggers of this node + triggerCollector_->setDependency(dependency()); + n->triggers(triggerCollector_); + + triggeredCollector_->setDependency(dependency()); + n->triggered(triggeredCollector_,triggeredScanner()); + + triggerTable_->setTriggerCollector(triggerCollector_,triggeredCollector_); + triggerTable_->endTriggerUpdate(); + + triggerTable_->resumeSelection(); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QGuiApplication::restoreOverrideCursor(); +#endif + } +} + +void TriggerItemWidget::clearContents() +{ + InfoPanelItem::clear(); + exprTe_->clear(); + triggerTable_->clear(); + + if(!active_) + triggerTable_->clearSelection(); + + //At this point the tables are cleared so it is safe to clear the collectors + triggerCollector_->clear(); + triggeredCollector_->clear(); +} + +void TriggerItemWidget::clearTriggers() +{ + exprTe_->clear(); + triggerTable_->clear(); + + //At this point the tables are cleared so it is safe to clear the collectors + triggerCollector_->clear(); + triggeredCollector_->clear(); +} + +void TriggerItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + //If we are here this item is active but not selected! + + //When it becomes suspended we need to clear everything since the + //tree is probably cleared at this point + if(suspended_) + { + clearTriggers(); + } + //When we leave the suspended state we need to reload everything + else + { + load(); + } + } + + Q_ASSERT(!flags.isSet(SelectedChanged)); + + checkActionState(); +} + +void TriggerItemWidget::checkActionState() +{ + if(suspended_) + { + dependTb_->setEnabled(false); + return; + } + + dependTb_->setEnabled(true); +} + +void TriggerItemWidget::on_dependTb__toggled(bool b) +{ + load(); + + //when we activate the dependencies we always show the + //dependency details as well + if(b) + { + dependInfoTb_->setEnabled(true); + if(dependInfoTb_->isChecked() == false) + { + dependInfoTb_->setChecked(true); + } + } + else + { + dependInfoTb_->setChecked(false); + dependInfoTb_->setEnabled(false); + } +} + +void TriggerItemWidget::on_dependInfoTb__toggled(bool b) +{ + triggerTable_->slotShowDependencyInfo(b); +} + +void TriggerItemWidget::slotHandleDefInfoWidgetClosure() +{ + dependInfoTb_->setChecked(false); +} + +void TriggerItemWidget::on_exprTb__toggled(bool b) +{ + exprTe_->setVisible(b); +} + +bool TriggerItemWidget::dependency() const +{ + return dependTb_->isChecked(); +} + + +void TriggerItemWidget::slotLinkSelected(VInfo_ptr info) +{ + InfoPanelItem::linkSelected(info); +} + +void TriggerItemWidget::slotInfoPanelCommand(VInfo_ptr info,QString cmd) +{ + InfoPanelItem::relayInfoPanelCommand(info,cmd); +} + +void TriggerItemWidget::slotDashboardCommand(VInfo_ptr info,QString cmd) +{ + InfoPanelItem::relayDashboardCommand(info,cmd); +} + +#if 0 +void TriggerItemWidget::infoProgressStart(const std::string& text,int max) +{ + messageLabel_->showInfo(QString::fromStdString(text)); + messageLabel_->startProgress(max); +} + +void TriggerItemWidget::infoProgress(const std::string& text,int value) +{ + messageLabel_->progress(QString::fromStdString(text),value); +} + +#endif + +void TriggerItemWidget::scanStarted() +{ + messageLabel_->showInfo("Mapping trigger connections in the whole tree ..."); + messageLabel_->startProgress(100); +} + +void TriggerItemWidget::scanFinished() +{ + messageLabel_->stopProgress(); + messageLabel_->hide(); +} + +void TriggerItemWidget::scanProgressed(int value) +{ + std::string text=""; + messageLabel_->progress(QString::fromStdString(text),value); +} + +void TriggerItemWidget::writeSettings(VComboSettings* vs) +{ + vs->beginGroup("triggers"); + vs->putAsBool("dependency",dependency()); + vs->putAsBool("dependencyInfo",dependInfoTb_->isChecked()); + vs->putAsBool("expression",exprTb_->isChecked()); + + triggerTable_->writeSettings(vs); + vs->endGroup(); +} + +void TriggerItemWidget::readSettings(VComboSettings* vs) +{ + vs->beginGroup("triggers"); + + dependTb_->setChecked(vs->getAsBool("dependency",dependency())); + +// dependInfoTb_ is initialised by dependTb_ !! +#if 0 + if(dependTb_->isChecked()) + { + dependInfoTb_->setChecked(vs->getAsBool("dependencyInfo",dependInfoTb_->isChecked())); + } +#endif + + exprTb_->setChecked(vs->getAsBool("expression",exprTb_->isChecked())); + triggerTable_->readSettings(vs); + vs->endGroup(); +} + +//------------------------- +// Update +//------------------------- + +void TriggerItemWidget::nodeChanged(const VNode* n, const std::vector& aspect) +{ + //We do not track changes when the item is not selected + if(!selected_ || !active_) + return; + + if(!info_ || !info_->isNode()) + return; + + //If the triggers are not scanned there must have been a major change and + //we need to reload the item + if(!info_->node()->root()->triggeredScanned()) + { + load(); + return; + } + + //For certain changes we need to reload the triggers + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::ADD_REMOVE_ATTR || *it == ecf::Aspect::EXPR_TRIGGER) + { + load(); + return; + } + } + + //For other changes we only reload the triggers if the change happened to an item in the collected triggers + for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) + { + if(*it == ecf::Aspect::NODE_VARIABLE || *it == ecf::Aspect::METER || *it == ecf::Aspect::LIMIT || + *it == ecf::Aspect::EVENT) + { + if(triggerCollector_->contains(n,true) || triggeredCollector_->contains(n,true)) + { + load(); + return; + } + } + } + + //For the rest of the changes in we rerender the collected items that might have changed + triggerTable_->nodeChanged(n,aspect); +} + +static InfoPanelItemMaker maker1("triggers"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,72 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TRIGGERITEMWIDGET_HPP_ +#define TRIGGERITEMWIDGET_HPP_ + +#include + +#include "InfoPanelItem.hpp" +#include "VInfo.hpp" + +#include "ui_TriggerItemWidget.h" + +class QActionGroup; +class TriggeredScanner; +class TriggerTableCollector; + +class TriggerItemWidget : public QWidget, public InfoPanelItem, protected Ui::TriggerItemWidget +{ + friend class TriggerBrowser; + +Q_OBJECT + +public: + explicit TriggerItemWidget(QWidget *parent=0); + ~TriggerItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + + void nodeChanged(const VNode*, const std::vector&); + void defsChanged(const std::vector&) {} + + bool dependency() const; + + void writeSettings(VComboSettings* vs); + void readSettings(VComboSettings* vs); + +protected Q_SLOTS: + void on_dependTb__toggled(bool); + void on_dependInfoTb__toggled(bool b); + void on_exprTb__toggled(bool b); + void scanStarted(); + void scanFinished(); + void scanProgressed(int); + void slotHandleDefInfoWidgetClosure(); + void slotLinkSelected(VInfo_ptr info); + void slotInfoPanelCommand(VInfo_ptr info,QString cmd); + void slotDashboardCommand(VInfo_ptr info,QString cmd); + +protected: + void load(); + void updateState(const ChangeFlags&); + TriggeredScanner* triggeredScanner() const {return scanner_;} + void checkActionState(); + void clearTriggers(); + + TriggerTableCollector* triggerCollector_; + TriggerTableCollector* triggeredCollector_; + TriggeredScanner *scanner_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,143 @@ + + + TriggerItemWidget + + + + 0 + 0 + 620 + 728 + + + + Form + + + + 1 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Show trigger expression + + + Expression + + + true + + + Qt::ToolButtonTextOnly + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show dependencies (with darker background) + + + Dependencies + + + true + + + Qt::ToolButtonTextOnly + + + + + + + Show dependency details + + + Text + + + + :/viewer/dependency.svg:/viewer/dependency.svg + + + true + + + + + + + + + + + + + + + + + + true + + + Text + + + + + true + + + Table + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    + + TriggerTableWidget + QWidget +
    TriggerTableWidget.hpp
    + 1 +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,299 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TriggerTableModel.hpp" + +#include "IconProvider.hpp" +#include "ServerHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VIcon.hpp" +#include "VNode.hpp" + +#include + +TriggerTableModel::TriggerTableModel(Mode mode,QObject *parent) : + QAbstractItemModel(parent), + tc_(0), + mode_(mode) +{ +} + +TriggerTableModel::~TriggerTableModel() +{ +} + + +void TriggerTableModel::clearData() +{ + beginResetModel(); + tc_=0; + endResetModel(); +} + +void TriggerTableModel::beginUpdate() +{ + beginResetModel(); +} + +void TriggerTableModel::endUpdate() +{ + endResetModel(); +} + + +void TriggerTableModel::setTriggerCollector(TriggerTableCollector *tc) +{ + //beginResetModel(); + tc_ = tc; + //endResetModel(); +} + +bool TriggerTableModel::hasData() const +{ + if(tc_) + return tc_->size() > 0; + else + return false; +} + +int TriggerTableModel::columnCount( const QModelIndex& /*parent */) const +{ + return 1; +} + +int TriggerTableModel::rowCount( const QModelIndex& parent) const +{ + if(!hasData()) + return 0; + + //Parent is the root: + if(!parent.isValid()) + { + return tc_->size(); + } + + return 0; +} + +Qt::ItemFlags TriggerTableModel::flags ( const QModelIndex & index) const +{ + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QVariant TriggerTableModel::data( const QModelIndex& index, int role ) const +{ + if(!index.isValid() || !hasData() || (role < Qt::UserRole && + role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::TextAlignmentRole && + role != Qt::ToolTipRole && role != Qt::ForegroundRole)) + { + return QVariant(); + } + + int row=index.row(); + if(row < 0 || row >= static_cast(tc_->size())) + return QVariant(); + + //QString id=columns_->id(index.column()); + + const std::vector& items=tc_->items(); + VItem *t=items[row]->item(); + Q_ASSERT(t); + + if(index.column() == 0) + { + if(VAttribute* a=t->isAttribute()) + { + if(role == Qt::DisplayRole) + { + QStringList d=a->data(); + if(VNode* pn=a->parent()) + d.append(QString::fromStdString(pn->absNodePath())); + return d; + } + else if(role == Qt::ToolTipRole) + { + return a->toolTip(); + } + + } + else if(VNode* vnode=t->isNode()) + { + if(role == Qt::DisplayRole) + { + return QString::fromStdString(vnode->absNodePath()); + } + else if(role == Qt::BackgroundRole) + { + if(vnode->isSuspended()) + { + QVariantList lst; + lst << vnode->stateColour() << vnode->realStateColour(); + return lst; + } + else + return vnode->stateColour() ; + } + else if(role == Qt::ForegroundRole) + return vnode->stateFontColour(); + + else if(role == Qt::ToolTipRole) + { + QString txt=vnode->toolTip(); + //txt+=VIcon::toolTip(vnode,icons_); + return txt; + } + else if(role == IconRole) + { + return VIcon::pixmapList(vnode,0); + } + else if(role == NodeTypeRole) + { + if(vnode->isTask()) return 2; + else if(vnode->isSuite()) return 0; + else if(vnode->isFamily()) return 1; + else if(vnode->isAlias()) return 3; + return 0; + } + else if(role == NodeTypeForegroundRole) + { + return vnode->typeFontColour(); + } + else if(role == NodePointerRole) + { + return qVariantFromValue((void *) vnode); + } + + else if(role == Qt::TextAlignmentRole) + { + return( mode_==NodeMode)?Qt::AlignCenter:Qt::AlignLeft; + } + + } + } + + //We express the table cell background colour through the UserRole. The + //BackgroundRole is already used for the node rendering + if(role == Qt::UserRole && mode_ != NodeMode) + { + const std::set& modes=items[row]->modes(); + if(modes.find(TriggerCollector::Normal) != modes.end()) + return QVariant(); + else + return QColor(233,242,247); + } + + return QVariant(); +} + +QVariant TriggerTableModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal) + return QAbstractItemModel::headerData( section, orient, role ); + + return QVariant(); +} + +QModelIndex TriggerTableModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { + return createIndex(row,column); + } + + return QModelIndex(); + +} + +QModelIndex TriggerTableModel::parent(const QModelIndex &child) const +{ + return QModelIndex(); +} + +VInfo_ptr TriggerTableModel::nodeInfo(const QModelIndex& index) +{ + //For invalid index no info is created. + if(!index.isValid()) + { + VInfo_ptr res; + return res; + } + + if(index.row() >=0 && index.row() <= static_cast(tc_->items().size())) + { + TriggerTableItem* d=tc_->items()[index.row()]; + return VInfo::createFromItem(d->item()); + } + + VInfo_ptr res; + return res; +} + +QModelIndex TriggerTableModel::itemToIndex(TriggerTableItem *item) +{ + if(item) + { + for(std::size_t i=0; i < tc_->items().size(); i++) + if(tc_->items()[i] == item) + return index(i,0); + } + + return QModelIndex(); +} + +TriggerTableItem* TriggerTableModel::indexToItem(const QModelIndex& index) const +{ + if(!hasData()) + return 0; + + int row=index.row(); + if(row < 0 || row >= static_cast(tc_->size())) + return 0; + + const std::vector& items=tc_->items(); + return items[row]; +} + +void TriggerTableModel::nodeChanged(const VNode* node, const std::vector&) +{ + if(!hasData()) + return; + + int num=tc_->items().size(); + for(int i=0; i < num; i++) + { + if(VItem* item=tc_->items()[i]->item()) + { + if(VNode* n=item->isNode()) + { + if(n == node) + { + QModelIndex idx=index(i,0); + Q_EMIT dataChanged(idx,idx); + } + } + + else if(VAttribute* a=item->isAttribute()) + { + if(a->parent() == node) + { + QModelIndex idx=index(i,0); + Q_EMIT dataChanged(idx,idx); + } + } + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,69 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ +#define VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ + +#include +#include + +#include "VInfo.hpp" +#include "TriggerCollector.hpp" + +#include "Aspect.hpp" + +class NodeQueryResult; + +class TriggerTableModel : public QAbstractItemModel +{ +public: + enum Mode {TriggerMode,TriggeredMode,NodeMode}; + + explicit TriggerTableModel(Mode mode,QObject *parent=0); + ~TriggerTableModel(); + + //The custom roles must have the same numerical value as in AbstractNodeModel.hpp because the + //core delegate was written to only handle the custom roles it defines! + enum CustomItemRole {FilterRole = Qt::UserRole+1, IconRole = Qt::UserRole+2, + NodeTypeRole = Qt::UserRole + 13, + NodeTypeForegroundRole = Qt::UserRole + 14, + NodePointerRole = Qt::UserRole + 17}; + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + TriggerTableCollector* triggerCollector() const {return tc_;} + void setTriggerCollector(TriggerTableCollector *tc); + + void clearData(); + bool hasData() const; + + void beginUpdate(); + void endUpdate(); + + TriggerTableItem* indexToItem(const QModelIndex&) const; + VInfo_ptr nodeInfo(const QModelIndex&); + QModelIndex itemToIndex(TriggerTableItem*); + + void nodeChanged(const VNode* node, const std::vector&); + +protected: + TriggerTableCollector* tc_; + Mode mode_; +}; + +#endif /* VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,248 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "TriggerTableView.hpp" + +#include +#include +#include +#include +#include + +#include "ActionHandler.hpp" +#include "AttributeEditor.hpp" +#include "NodeQueryResultModel.hpp" +#include "NodeQueryViewDelegate.hpp" +#include "TriggerTableModel.hpp" +#include "TriggerViewDelegate.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "VNode.hpp" + +TriggerTableView::TriggerTableView(QWidget* parent) : + QTreeView(parent), + model_(NULL), + needItemsLayout_(false) +{ + setProperty("view","trigger"); + + actionHandler_=new ActionHandler(this,this); + + //Set the model + setModel(model_); + + //Create delegate to the view + delegate_=new TriggerViewDelegate(this); + setItemDelegate(delegate_); + + connect(delegate_,SIGNAL(sizeHintChangedGlobal()), + this,SLOT(slotSizeHintChangedGlobal())); + + setHeaderHidden(true); + setRootIsDecorated(false); + setAllColumnsShowFocus(true); + setUniformRowHeights(true); + setMouseTracking(true); + setSortingEnabled(false); + setSelectionMode(QAbstractItemView::ExtendedSelection); + + //!!!!We need to do it because: + //The background colour between the view's left border and the nodes cannot be + //controlled by delegates or stylesheets. It always takes the QPalette::Highlight + //colour from the palette. Here we set this to transparent so that Qt could leave + //this area empty and we will fill it appropriately in our delegate. + QPalette pal=palette(); + pal.setColor(QPalette::Highlight,QColor(128,128,128,0));//Qt::transparent); + setPalette(pal); + + //Context menu + enableContextMenu(true); + + //Selection + connect(this,SIGNAL(clicked(const QModelIndex&)), + this,SLOT(slotSelectItem(const QModelIndex&))); + + connect(this,SIGNAL(doubleClicked(const QModelIndex&)), + this,SLOT(slotDoubleClickItem(const QModelIndex&))); + +} + +TriggerTableView::~TriggerTableView() +{ + delete actionHandler_; +} + +//We should only call it once!!! +void TriggerTableView::setModel(TriggerTableModel* model) +{ + Q_ASSERT(model_==0); + model_=model; + QTreeView::setModel(model); +} + +void TriggerTableView::enableContextMenu(bool enable) +{ + if (enable) + { + setContextMenuPolicy(Qt::CustomContextMenu); + + connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + } + else + { + setContextMenuPolicy(Qt::NoContextMenu); + + disconnect(this, SIGNAL(customContextMenuRequested(const QPoint &)), + this, SLOT(slotContextMenu(const QPoint &))); + } +} + + +//Collects the selected list of indexes +QModelIndexList TriggerTableView::selectedList() +{ + QModelIndexList lst; + Q_FOREACH(QModelIndex idx,selectedIndexes()) + { + if(idx.column() == 0) + lst << idx; + } + return lst; +} + +// this is called even if the user clicks outside of the node list to deselect all +void TriggerTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) +{ + QModelIndexList lst=selectedIndexes(); + if(lst.count() > 0) + { + TriggerTableItem* item=model_->indexToItem(lst.front()); + if(item && item->item()) + { + Q_EMIT selectionChanged(item); + } + } + + QTreeView::selectionChanged(selected, deselected); + + //Q_EMIT selectionChanged(); +} + +void TriggerTableView::slotSelectItem(const QModelIndex&) +{ + QModelIndexList lst=selectedIndexes(); + + if(lst.count() > 0) + { + TriggerTableItem* item=model_->indexToItem(lst.front()); + if(item && item->item()) + { + Q_EMIT clicked(item); + } + } +} + +void TriggerTableView::setCurrentItem(TriggerTableItem* item) +{ + QModelIndex idx=model_->itemToIndex(item); + if(idx.isValid()) + { + setCurrentIndex(idx); + } +} + +void TriggerTableView::slotDoubleClickItem(const QModelIndex& index) +{ + VInfo_ptr info=model_->nodeInfo(index); + if(info) + { + Q_EMIT linkSelected(info); + } +} + +void TriggerTableView::slotContextMenu(const QPoint &position) +{ + QModelIndexList lst=selectedList(); + //QModelIndex index=indexAt(position); + QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); + + handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); +} + + +void TriggerTableView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) +{ + //Node actions + if(indexClicked.isValid()) //indexLst[0].isValid() && indexLst[0].column() == 0) + { + std::vector nodeLst; + for(int i=0; i < indexLst.count(); i++) + { + VInfo_ptr info=model_->nodeInfo(indexLst[i]); + if(info && !info->isEmpty()) + nodeLst.push_back(info); + } + + actionHandler_->contextMenu(nodeLst,globalPos); + } + + //Desktop actions + else + { + } +} + +void TriggerTableView::slotViewCommand(VInfo_ptr info,QString cmd) +{ + if(cmd == "lookup") + { + Q_EMIT linkSelected(info); + } + + else if(cmd == "edit") + { + if(info && info->isAttribute()) + { + AttributeEditor::edit(info,this); + } + } +} + +void TriggerTableView::reload() +{ + //model_->reload(); + //expandAll(); +} + +void TriggerTableView::rerender() +{ + if(needItemsLayout_) + { + doItemsLayout(); + needItemsLayout_=false; + } + else + { + viewport()->update(); + } +} + +void TriggerTableView::slotRerender() +{ + rerender(); +} + + +void TriggerTableView::slotSizeHintChangedGlobal() +{ + needItemsLayout_=true; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,66 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TRIGGERTABLEVIEW_HPP +#define TRIGGERTABLEVIEW_HPP + +#include + +#include "VInfo.hpp" + +class ActionHandler; +class NodeQueryResultModel; +class TriggerViewDelegate; +class TriggerTableItem; +class TriggerTableModel; + +class TriggerTableView : public QTreeView +{ +Q_OBJECT + +public: + explicit TriggerTableView(QWidget *parent=0); + ~TriggerTableView(); + + void setModel(TriggerTableModel* model); + + void reload(); + void rerender(); + void setCurrentItem(TriggerTableItem*); + void enableContextMenu(bool enable); + +public Q_SLOTS: + void slotSelectItem(const QModelIndex&); + void slotDoubleClickItem(const QModelIndex&); + void slotContextMenu(const QPoint &position); + void slotViewCommand(VInfo_ptr,QString); + void slotRerender(); + void slotSizeHintChangedGlobal(); + void selectionChanged (const QItemSelection &selected, const QItemSelection &deselected); + +Q_SIGNALS: + void selectionChanged(TriggerTableItem*); + void clicked(TriggerTableItem*); + void linkSelected(VInfo_ptr); + void selectionChanged(); + void infoPanelCommand(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + +protected: + QModelIndexList selectedList(); + void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); + + TriggerTableModel* model_; + ActionHandler* actionHandler_; + bool needItemsLayout_; + TriggerViewDelegate* delegate_; +}; + +#endif // TRIGGERTABLEVIEW_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,373 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggerTableWidget.hpp" + +#include "Highlighter.hpp" +#include "TriggerItemWidget.hpp" +#include "TriggerTableModel.hpp" +#include "TriggerViewDelegate.hpp" +#include "VSettings.hpp" + +TriggerTableWidget::TriggerTableWidget(QWidget *parent) : + QWidget(parent) +{ + setupUi(this); + + nodeCollector_=new TriggerTableCollector(false); + + depInfoCloseTb_->setProperty("triggertitle","1"); + depInfoCloseTb_->parent()->setProperty("triggertitle","1"); + + //Format labels + triggerLabel_->setProperty("triggertitle","1"); + triggeredLabel_->setProperty("triggertitle","1"); + depLabel_->setProperty("triggertitle","1"); + + depLabelText_=tr(" Dependency details"); + depLabel_->setText(depLabelText_); + + //Node - model + view + nodeModel_ = new TriggerTableModel(TriggerTableModel::NodeMode,this); + nodeView_->setModel(nodeModel_); + + //Set the height of the node display area + QFont fNode; + QFontMetrics fm(fNode); + nodeView_->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); + nodeView_->setFixedHeight(fm.size(0,"A").height()+fm.height()/3); + + //Set the size of the left and right arrow labels + leftArrowLabel_->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); + leftArrowLabel_->setFixedHeight(nodeView_->height()); + leftArrowLabel_->setFixedWidth(nodeView_->height()); + + rightArrowLabel_->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); + rightArrowLabel_->setFixedHeight(nodeView_->height()); + rightArrowLabel_->setFixedWidth(nodeView_->height()); + + //Trigger - model + view + triggerModel_ = new TriggerTableModel(TriggerTableModel::TriggerMode,this); + triggerView_->setModel(triggerModel_); + + //triggered - model + view + triggeredModel_ = new TriggerTableModel(TriggerTableModel::TriggeredMode,this); + triggeredView_->setModel(triggeredModel_); + + //normal selection + connect(triggerView_,SIGNAL(selectionChanged(TriggerTableItem*)), + this,SLOT(slotTriggerSelection(TriggerTableItem*))); + + connect(triggerView_,SIGNAL(clicked(TriggerTableItem*)), + this,SLOT(slotTriggerClicked(TriggerTableItem*))); + + connect(triggeredView_,SIGNAL(selectionChanged(TriggerTableItem*)), + this,SLOT(slotTriggeredSelection(TriggerTableItem*))); + + connect(triggeredView_,SIGNAL(clicked(TriggerTableItem*)), + this,SLOT(slotTriggeredClicked(TriggerTableItem*))); + + //lookup selection + connect(triggerView_,SIGNAL(linkSelected(VInfo_ptr)), + this,SIGNAL(linkSelected(VInfo_ptr))); + + connect(triggeredView_,SIGNAL(linkSelected(VInfo_ptr)), + this,SIGNAL(linkSelected(VInfo_ptr))); + + //relay commands + connect(nodeView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); + + connect(nodeView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + connect(triggerView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); + + connect(triggerView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + connect(triggeredView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), + this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); + + connect(triggeredView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), + this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); + + //anchor clicked in text browser + connect(depBrowser_,SIGNAL(anchorClicked(const QUrl&)), + this,SLOT(anchorClicked(const QUrl&))); +} + +TriggerTableWidget::~TriggerTableWidget() +{ + delete nodeCollector_; +} + +void TriggerTableWidget::clear() +{ + info_.reset(); + + nodeModel_->clearData(); + triggerModel_->clearData(); + triggeredModel_->clearData(); + + depLabel_->setText(depLabelText_); + depBrowser_->clear(); +} + +void TriggerTableWidget::clearSelection() +{ + lastSelectedItem_.reset(); +} + +void TriggerTableWidget::setInfo(VInfo_ptr info) +{ + info_=info; + + nodeModel_->beginUpdate(); + nodeCollector_->clear(); + if(info_) + { + nodeCollector_->add(info_->item(),0,TriggerCollector::Normal); + } + nodeModel_->setTriggerCollector(nodeCollector_); + nodeModel_->endUpdate(); +} + +void TriggerTableWidget::slotTriggerClicked(TriggerTableItem* item) +{ + Q_ASSERT(item); + + if(!depBrowser_->document()->isEmpty() && lastSelectedItem_ && lastSelectedItem_->hasData()) + { + if(item->item() && item->item()->sameContents(lastSelectedItem_->item())) + return; + } + + slotTriggerSelection(item); +} + +void TriggerTableWidget::slotTriggerSelection(TriggerTableItem* item) +{ + Q_ASSERT(item); + Q_ASSERT(item->item()); + + lastSelectedItem_=VInfo::createFromItem(item->item()); + + if(!depInfoWidget_->isVisible()) + return; + + QString txt=tr("  triggers these parents/children of "); + QString tgName,tgType; + QColor col(255,255,255); + + VItem* currentItem=0; + if(info_) + currentItem=info_->item(); + + if(currentItem) + { + tgName=currentItem->name(); + tgType=QString::fromStdString(currentItem->typeName()); + } + txt.replace("","" + tgName + ""); + txt.replace("",tgType); + + tgName.clear(); + tgType.clear(); + if(item->item()) + { + tgName=item->item()->name(); + tgType=QString::fromStdString(item->item()->typeName()); + } + txt.replace("","" + tgName + ""); + txt.replace("",tgType); + + depLabel_->setText(txt); + depBrowser_->reload(item); +} + + +void TriggerTableWidget::slotTriggeredClicked(TriggerTableItem* item) +{ + Q_ASSERT(item); + + if(!depBrowser_->document()->isEmpty() && lastSelectedItem_ && lastSelectedItem_->hasData()) + { + if(item->item() && item->item()->sameContents(lastSelectedItem_->item())) + return; + } + + slotTriggeredSelection(item); +} + +void TriggerTableWidget::slotTriggeredSelection(TriggerTableItem* item) +{ + Q_ASSERT(item); + Q_ASSERT(item->item()); + + lastSelectedItem_=VInfo::createFromItem(item->item()); + + if(!depInfoWidget_->isVisible()) + return; + + QString txt=tr("  is triggered by these parents/children of "); + QString tgType,tgName; + QColor col(255,255,255); + + VItem* currentItem=0; + if(info_) + currentItem=info_->item(); + + if(currentItem) + { + tgName=currentItem->name(); + tgType=QString::fromStdString(currentItem->typeName()); + } + txt.replace("","" + tgName + ""); + txt.replace("",tgType); + + tgName.clear(); + tgType.clear(); + if(item->item()) + { + tgName=item->item()->name(); + tgType=QString::fromStdString(item->item()->typeName()); + } + txt.replace("","" + tgName + ""); + txt.replace("",tgType); + + depLabel_->setText(txt); + depBrowser_->reload(item); +} + +void TriggerTableWidget::slotShowDependencyInfo(bool b) +{ + depInfoWidget_->setVisible(b); + + //When the depinfo panel becomes visible we need to update + //its contents + if(b && lastSelectedItem_ && lastSelectedItem_->hasData()) + { + if(TriggerTableCollector *tc=triggerModel_->triggerCollector()) + { + if(TriggerTableItem *ti=tc->find(lastSelectedItem_->item())) + { + slotTriggerSelection(ti); + return; + } + } + if(TriggerTableCollector *tc=triggeredModel_->triggerCollector()) + { + if(TriggerTableItem *ti=tc->find(lastSelectedItem_->item())) + { + slotTriggeredSelection(ti); + return; + } + } + } +} + +void TriggerTableWidget::on_depInfoCloseTb__clicked() +{ + if(depInfoWidget_->isVisible()) + Q_EMIT depInfoWidgetClosureRequested(); +} + +void TriggerTableWidget::anchorClicked(const QUrl& link) +{ + VInfo_ptr info=VInfo::createFromPath(info_->server(),link.toString().toStdString()); + if(info) + Q_EMIT linkSelected(info); +} + +void TriggerTableWidget::beginTriggerUpdate() +{ + triggerModel_->beginUpdate(); + triggeredModel_->beginUpdate(); +} + +void TriggerTableWidget::endTriggerUpdate() +{ + triggerModel_->endUpdate(); + triggeredModel_->endUpdate(); +} + +void TriggerTableWidget::setTriggerCollector(TriggerTableCollector *tc1,TriggerTableCollector *tc2) +{ + triggerModel_->setTriggerCollector(tc1); + triggeredModel_->setTriggerCollector(tc2); +} + +void TriggerTableWidget::resumeSelection() +{ + //try to reselect the last selected item + if(lastSelectedItem_) + { + lastSelectedItem_->regainData(); + if(lastSelectedItem_->hasData()) + { + if(TriggerTableCollector* tc=triggerModel_->triggerCollector()) + { + if(TriggerTableItem* ti=tc->findByContents(lastSelectedItem_->item())) + { + triggerView_->setCurrentItem(ti); + return; + } + } + + if(TriggerTableCollector* tc=triggeredModel_->triggerCollector()) + { + if(TriggerTableItem* ti=tc->findByContents(lastSelectedItem_->item())) + { + triggeredView_->setCurrentItem(ti); + return; + } + } + } + else + { + lastSelectedItem_.reset(); + } + } +} + +void TriggerTableWidget::nodeChanged(const VNode* node, const std::vector& aspect) +{ + //The node view only contains one item (=info_) so we simply rerender it to get the + //update + if(info_ && info_->node() == node) + nodeView_->rerender(); + + triggerModel_->nodeChanged(node,aspect); + triggeredModel_->nodeChanged(node,aspect); +} + + +void TriggerTableWidget::writeSettings(VComboSettings* vs) +{ + vs->beginGroup("triggerTable"); + vs->putQs("splitter1",splitter1_->saveState()); + vs->putQs("splitter2",splitter2_->saveState()); + vs->endGroup(); +} + +void TriggerTableWidget::readSettings(VComboSettings* vs) +{ + vs->beginGroup("triggerTable"); + if(vs->containsQs("splitter1")) + { + splitter1_->restoreState(vs->getQs("splitter1").toByteArray()); + } + if(vs->containsQs("splitter2")) + { + splitter2_->restoreState(vs->getQs("splitter2").toByteArray()); + } + vs->endGroup(); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,73 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + + +#ifndef TRIGGERTABLEWIDGET_HPP_ +#define TRIGGERTABLEWIDGET_HPP_ + +#include +#include "VInfo.hpp" + +#include "Aspect.hpp" + +#include "ui_TriggerTableWidget.h" + +class TriggerTableItem; +class TriggerTableModel; +class TriggerTableCollector; +class TriggerItemWidget; +class VComboSettings; + +class TriggerTableWidget : public QWidget, private Ui::triggerTableWidget +{ + Q_OBJECT + +public: + explicit TriggerTableWidget(QWidget *parent = 0); + ~TriggerTableWidget(); + + void setInfo(VInfo_ptr); + void setTriggerCollector(TriggerTableCollector *tc1,TriggerTableCollector *tc2); + void clear(); + void clearSelection(); + void resumeSelection(); + void beginTriggerUpdate(); + void endTriggerUpdate(); + void nodeChanged(const VNode* node, const std::vector& aspect); + void writeSettings(VComboSettings* vs); + void readSettings(VComboSettings* vs); + +public Q_SLOTS: + void slotShowDependencyInfo(bool); + +protected Q_SLOTS: + void slotTriggerSelection(TriggerTableItem* item); + void slotTriggerClicked(TriggerTableItem*); + void slotTriggeredSelection(TriggerTableItem* item); + void slotTriggeredClicked(TriggerTableItem*); + void on_depInfoCloseTb__clicked(); + void anchorClicked(const QUrl& link); + +Q_SIGNALS: + void linkSelected(VInfo_ptr); + void infoPanelCommand(VInfo_ptr,QString); + void dashboardCommand(VInfo_ptr,QString); + void depInfoWidgetClosureRequested(); + +private: + VInfo_ptr info_; + TriggerTableCollector* nodeCollector_; + TriggerTableModel *nodeModel_; + TriggerTableModel *triggerModel_; + TriggerTableModel *triggeredModel_; + VInfo_ptr lastSelectedItem_; + QString depLabelText_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTableWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTableWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,243 @@ + + + triggerTableWidget + + + + 0 + 0 + 940 + 579 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + :/viewer/trigger_left_arrow.svg + + + true + + + + + + + + + + + + + :/viewer/trigger_right_arrow.svg + + + true + + + + + + + + + + Qt::Vertical + + + 2 + + + true + + + + Qt::Horizontal + + + 2 + + + false + + + + + 2 + + + + + <html><head/><body><p><span style=" font-weight:600;">Triggers of</span> the selected node</p></body></html> + + + + + + + false + + + + + + + + + 2 + + + + + &nbsp;Nodes <b>triggered by</b> the selected node + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + 16 + 16 + + + + + + + :/viewer/dock_dependency.svg + + + true + + + + + + + <html><head/><body><p><br/></p></body></html> + + + + + + + Close + + + + + + + :/viewer/images/dock_close.svg:/viewer/images/dock_close.svg + + + + 16 + 16 + + + + true + + + + + + + + + + + + + + + + + TriggerTextWidget + QTextBrowser +
    TriggerTextWidget.hpp
    +
    + + TriggerTableView + QTreeView +
    TriggerTableView.hpp
    +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTextWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTextWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTextWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTextWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggerTextWidget.hpp" + +#include +#include + +#include "TriggerCollector.hpp" +#include "VItemPathParser.hpp" + +TriggerTextWidget::TriggerTextWidget(QWidget* parent) : QTextBrowser(parent) +{ + setOpenExternalLinks(false); + setOpenLinks(false); + setReadOnly(true); + + setProperty("trigger","1"); + + //Set css for the text formatting + QString cssDoc; + QFile f(":/viewer/trigger.css"); + //QTextStream in(&f); + if(f.open(QIODevice::ReadOnly | QIODevice::Text)) + { + cssDoc=QString(f.readAll()); + } + f.close(); + document()->setDefaultStyleSheet(cssDoc); +} + +void TriggerTextWidget::reload(TriggerTableItem* item) +{ + QString s=""; + s+=makeHtml(item,"Triggers directly triggering the selected node","Triggers"); + s+="
    "; + setHtml(s); +} + +QString TriggerTextWidget::makeHtml(TriggerTableItem *ti,QString directTitle,QString modeText) const +{ + QString s; + const std::vector& items=ti->dependencies(); + + for(unsigned int i=0; i < items.size(); i++) + { + VItem *t=items[i].dep(); + TriggerCollector::Mode mode=items[i].mode(); + + if(!t) + continue; + + s+=""; + if(mode == TriggerCollector::Parent) + s+="parent"; + else + s+="child"; + + QString type=QString::fromStdString(t->typeName()); + QString path=QString::fromStdString(t->fullPath()); + QString anchor=QString::fromStdString(VItemPathParser::encode(t->fullPath(),t->typeName())); + + s+=" " + type; + //s+=" " + path +""; + s+=" " + path +""; + s+=""; + } + + return s; +} + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTextWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTextWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerTextWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerTextWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,28 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef TRIGGERTEXTWIDGET_HPP +#define TRIGGERTEXTWIDGET_HPP + +#include + +class TriggerTableItem; + +class TriggerTextWidget : public QTextBrowser +{ +public: + explicit TriggerTextWidget(QWidget *parent=0); + void reload(TriggerTableItem* item); + +private: + QString makeHtml(TriggerTableItem *ti,QString directTitle,QString modeText) const; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerViewDelegate.cpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerViewDelegate.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerViewDelegate.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,176 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "TriggerViewDelegate.hpp" + +#include +#include +#include +#include +#include + +#include "AbstractNodeModel.hpp" +#include "Animation.hpp" +#include "FontMetrics.hpp" +#include "IconProvider.hpp" +#include "PropertyMapper.hpp" +#include "ServerHandler.hpp" +#include "UiLog.hpp" + +static std::vector propVec; + +#if 0 +static QColor typeFgColourClassic=QColor(Qt::white); +static QColor typeBgColourClassic=QColor(150,150,150); +static QColor childCountColour=QColor(90,91,92); +#endif + +struct TriggerNodeDelegateBox : public NodeDelegateBox +{ + TriggerNodeDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=2; + rightMargin=2; + topPadding=0; + bottomPadding=0; + leftPadding=2; + rightPadding=2; + } +}; + +struct TriggerAttrDelegateBox : public AttrDelegateBox +{ + TriggerAttrDelegateBox() { + topMargin=2; + bottomMargin=2; + leftMargin=2; + rightMargin=2; + topPadding=0; + bottomPadding=0; + leftPadding=2; + rightPadding=0; + } +}; + +TriggerViewDelegate::TriggerViewDelegate(QWidget *parent) : + TreeNodeViewDelegate(0,parent) +{ + //borderPen_=QPen(QColor(230,230,230)); + borderPen_=QPen(QColor(220,220,220)); + + nodeBox_=new TriggerNodeDelegateBox; + attrBox_=new TriggerAttrDelegateBox; + + nodeBox_->adjust(font_); + attrBox_->adjust(attrFont_); + + //Property + if(propVec.empty()) + { + //Base settings + addBaseSettings(propVec); + } + + prop_=new PropertyMapper(propVec,this); + + updateSettings(); +} + +TriggerViewDelegate::~TriggerViewDelegate() +{ +} + +void TriggerViewDelegate::updateSettings() +{ + //Update the settings handled by the base class + updateBaseSettings(); +} + +QSize TriggerViewDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex & index ) const +{ + return nodeBox_->sizeHintCache; +} + +void TriggerViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QStyleOptionViewItem vopt(option); +#else + QStyleOptionViewItemV4 vopt(option); +#endif + + initStyleOption(&vopt, index); + + //const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); + //const QWidget* widget = vopt.widget; + + //Save painter state + painter->save(); + + //Background + QColor bgcol=index.data(Qt::UserRole).value(); + if(bgcol.isValid()) + painter->fillRect(vopt.rect,bgcol); + + if(index.column() == 0) + { + QVariant tVar=index.data(Qt::DisplayRole); + painter->setFont(font_); + + //Node + if(tVar.type() == QVariant::String) + { + QString text=index.data(Qt::DisplayRole).toString(); + + //If the textalignment is AlignCenter we node is displayed + //as centered in the opt rect! + QVariant vTa=index.data(Qt::TextAlignmentRole); + if(!vTa.isNull()) + { + if(vTa.toInt() == Qt::AlignCenter) + { + int w=nodeWidth(index,text); + int dw=(vopt.rect.width()-w)/2; + if(dw > 0) + vopt.rect.moveLeft(dw); + } + } + + renderNode(painter,index,vopt,text); + } + + //Render attributes + else if(tVar.type() == QVariant::StringList) + { + QStringList lst=tVar.toStringList(); + if(lst.count() > 0) + { + QMap::const_iterator it=attrRenderers_.find(lst.at(0)); + if(it != attrRenderers_.end()) + { + QSize size; + AttributeRendererProc a=it.value(); + (this->*a)(painter,lst,vopt,size); + } + } + } + } + + //Render the horizontal border for rows. We only render the top border line. + //With this technique we miss the bottom border line of the last row!!! + //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); + QRect bgRect=option.rect; + painter->setPen(borderPen_); + painter->drawLine(bgRect.topLeft(),bgRect.topRight()); + + painter->restore(); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/TriggerViewDelegate.hpp ecflow-4.11.1/Viewer/ecflowUI/src/TriggerViewDelegate.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/TriggerViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/TriggerViewDelegate.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,44 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef TRIGGERVIEWDELEGATE_HPP +#define TRIGGERVIEWDELEGATE_HPP + +#include +#include +#include +#include +#include + +#include "TreeNodeViewDelegate.hpp" +#include "VProperty.hpp" + +#include + +class ModelColumn; + +class TriggerViewDelegate : public TreeNodeViewDelegate +{ +public: + explicit TriggerViewDelegate(QWidget *parent=0); + ~TriggerViewDelegate(); + + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + +protected: + void updateSettings(); + + QPen borderPen_; + +}; + +#endif // TRIGGERVIEWDELEGATE_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/UiLogS.cpp ecflow-4.11.1/Viewer/ecflowUI/src/UiLogS.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/UiLogS.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/UiLogS.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,29 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "UiLogS.hpp" + +#include "ServerHandler.hpp" + +//--------------------------------- +// UiFunctionLog +//--------------------------------- + +UiFunctionLogS::UiFunctionLogS(ServerHandler* server,const std::string& funcName) : + UiFunctionLog(((server)?server->longName():""),funcName) +{ +} + +//--------------------------------- +// UiLog +//--------------------------------- + +UiLogS::UiLogS(ServerHandler* server) : UiLog((server)?server->longName():"") +{ +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/UiLogS.hpp ecflow-4.11.1/Viewer/ecflowUI/src/UiLogS.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/UiLogS.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/UiLogS.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,33 @@ +//============================================================================ +// Copyright 2009-2018 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef UILOGS_HPP +#define UILOGS_HPP + +#include "UiLog.hpp" + +class ServerHandler; + +#define UI_FUNCTION_LOG_S(server) UiFunctionLogS __fclog(server,BOOST_CURRENT_FUNCTION); + +class UiFunctionLogS : public UiFunctionLog +{ +public: + UiFunctionLogS(ServerHandler* server,const std::string& funcName); +}; + +class UiLogS : public UiLog +{ +public: + UiLogS(ServerHandler* server); +}; + + +#endif // UILOG_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/UpdateTimer.cpp ecflow-4.11.1/Viewer/ecflowUI/src/UpdateTimer.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/UpdateTimer.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/UpdateTimer.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,20 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "UpdateTimer.hpp" + +void UpdateTimer::drift(int dValSec, int maxValMin) +{ + double v=interval() + dValSec*1000; + if( v > maxValMin*1000*60) + v=maxValMin*1000*60; + + setInterval(v); +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/UpdateTimer.hpp ecflow-4.11.1/Viewer/ecflowUI/src/UpdateTimer.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/UpdateTimer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/UpdateTimer.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,22 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef UPDATETIMER_HPP_ +#define UPDATETIMER_HPP_ + +#include + +class UpdateTimer : public QTimer +{ +public: + UpdateTimer(QObject* parent=0) : QTimer(parent) {} + void drift(int,int); +}; + +#endif // UPDATETIMER_HPP_ diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableAddDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/VariableAddDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableAddDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,140 @@ + + + VariableAddDialog + + + + 0 + 0 + 286 + 161 + + + + Add variable + + + true + + + + + + + 0 + 0 + + + + Add new variable for + + + + + + + + + + + + + &Name: + + + nameEdit_ + + + + + + + false + + + + + + + &Value: + + + valueEdit_ + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    +
    + + + + buttonBox_ + accepted() + VariableAddDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox_ + rejected() + VariableAddDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditor.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditor.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditor.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditor.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,152 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VariableEditor.hpp" + +#include + +#include "AttributeEditorFactory.hpp" +#include "CommandHandler.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VGenVarAttr.hpp" +#include "SessionHandler.hpp" + +VariableEditorWidget::VariableEditorWidget(QWidget* parent) : QWidget(parent) +{ + setupUi(this); + + QLayoutItem *item; + item=grid_->itemAtPosition(1,0); + Q_ASSERT(item); + item->setAlignment(Qt::AlignLeft|Qt::AlignTop); +} + +VariableEditor::VariableEditor(VInfo_ptr info,QWidget* parent) : + AttributeEditor(info,"variable",parent), + readOnly_(false) +{ + w_=new VariableEditorWidget(this); + addForm(w_); + + VAttribute* a=info_->attribute(); + + Q_ASSERT(a); + Q_ASSERT(a->type()); + Q_ASSERT(a->type()->name() == "var" || a->type()->name() == "genvar"); + + if(a->data().count() < 2) + return; + + QString name=a->data().at(1); + QString val; + if(a->data().count() > 2) + val=a->data().at(2); + + oriVal_=val; + + w_->nameLabel_->setText(name); + w_->valueTe_->setPlainText(val); + w_->valueTe_->setFocus(); + + QString typeLabel=(a->type()->name() == "var")?"User variable":"Generated variable"; + readOnly_=VGenVarAttr::isReadOnly(name.toStdString()); + if(readOnly_) + { + w_->valueTe_->setReadOnly(true); + typeLabel+=" (read only)"; + } + else + { + connect(w_->valueTe_,SIGNAL(textChanged()), + this,SLOT(slotValueChanged())); + } + + header_->setInfo(QString::fromStdString(info_->path()),typeLabel); + + checkButtonStatus(); + + readSettings(); +} + +VariableEditor::~VariableEditor() +{ + writeSettings(); +} + +void VariableEditor::apply() +{ + if(!readOnly_) + { + std::string val=w_->valueTe_->toPlainText().toStdString(); + std::string name=w_->nameLabel_->text().toStdString(); + + if(val != oriVal_.toStdString()) + { + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","variable",name,val); + CommandHandler::run(info_,cmd); + } + } +} + +void VariableEditor::resetValue() +{ + w_->valueTe_->setPlainText(oriVal_); + checkButtonStatus(); +} + +void VariableEditor::slotValueChanged() +{ + checkButtonStatus(); +} + +bool VariableEditor::isValueChanged() +{ + return (oriVal_ != w_->valueTe_->toPlainText()); +} + +void VariableEditor::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableEditor")), + QSettings::NativeFormat); + + //We have to clear it so that should not remember all the previous values + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void VariableEditor::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableEditor")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(310,200)); + } + + settings.endGroup(); +} + +static AttributeEditorMaker makerStrVar("var"); +static AttributeEditorMaker makerStrGenvar("genvar"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditor.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditor.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditor.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditor.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VARIABLEEDITOR_HPP +#define VARIABLEEDITOR_HPP + +#include "ui_VariableEditorWidget.h" + +#include "AttributeEditor.hpp" +#include "VInfo.hpp" + +class VariableEditor; + +class VariableEditorWidget : public QWidget, protected Ui::VariableEditorWidget +{ +friend class VariableEditor; +public: + VariableEditorWidget(QWidget *parent=0); +}; + +class VariableEditor : public AttributeEditor +{ +Q_OBJECT +public: + VariableEditor(VInfo_ptr,QWidget* parent=0); + ~VariableEditor(); + +protected Q_SLOTS: + void slotValueChanged(); + +protected: + void apply(); + void resetValue(); + bool isValueChanged(); + void readSettings(); + void writeSettings(); + + VariableEditorWidget* w_; + QString oriVal_; + bool readOnly_; +}; + +#endif // VARIABLEEDITOR_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditorWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditorWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableEditorWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,64 @@ + + + VariableEditorWidget + + + + 0 + 0 + 329 + 227 + + + + Form + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + TextLabel + + + + + + + Value: + + + + + + + + 0 + 1 + + + + + + + + Name: + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,1294 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VariableItemWidget.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "IconProvider.hpp" +#include "LineEdit.hpp" +#include "ModelColumn.hpp" +#include "SessionHandler.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "VariableModel.hpp" +#include "VariableModelData.hpp" +#include "VariableSearchLine.hpp" +#include "VConfig.hpp" +#include "VProperty.hpp" +#include "WidgetNameProvider.hpp" + +#define _UI_VARIABLEITEMWIDGET_DEBUG +#define _UI_VARIABLESORTMODELTEST_DEBUG + +//====================================== +// +// VariablePropDialog +// +//====================================== + +VariablePropDialog::VariablePropDialog(VariableModelDataHandler *data,int defineIndex,QString name, QString value,bool frozen,QWidget *parent) : + QDialog(parent), + genVar_(false), + data_(data), + defineIndex_(defineIndex), + oriName_(name), + cleared_(false), + suspended_(false) +{ + setupUi(this); + setAttribute(Qt::WA_DeleteOnClose); + setModal(false); + + QString wt="Edit variable"; + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + Q_ASSERT(data_); + Q_ASSERT(data_->count() > 0); + Q_ASSERT(data_->count() > defineIndex_); + + nodeName_=QString::fromStdString(data_->data(0)->name()); + nodeType_=QString::fromStdString(data_->data(0)->type()); + nodeTypeCapital_=nodeType_; + if(nodeTypeCapital_.size() > 0) + { + QChar s=nodeTypeCapital_.at(0); + s=s.toUpper(); + nodeTypeCapital_.replace(0,1,s); + } + + data_->addObserver(this); + + genVar_=data_->data(defineIndex)->isGenVar(name.toStdString()); + + QString path=QString::fromStdString(data_->data(0)->fullPath()); + QString h=EditorInfoLabel::formatKeyLabel("Node to modify: ") + "" + + EditorInfoLabel::formatNodeName(nodeName_) + "
    "; + h+= EditorInfoLabel::formatKeyLabel("Node path: ") + EditorInfoLabel::formatNodePath(path) + "
    "; + + VariableModelData* defineData=data_->data(defineIndex_); + Q_ASSERT(defineData); + defineNodeName_=QString::fromStdString(defineData->name()); + defineNodeType_=QString::fromStdString(defineData->type()); + + genVar_=defineData->isGenVar(name.toStdString()); + h+=EditorInfoLabel::formatKeyLabel("Variable type: "); + h+=(genVar_)?tr("generated variable"):tr("user variable"); + + bool readOnly=defineData->isReadOnly(name.toStdString()); + if(readOnly) + { + h+=" (read only)"; + } + + if(defineIndex_ > 0) + { + QString definePath=QString::fromStdString(defineData->fullPath()); + h+="
    " + EditorInfoLabel::formatKeyLabel("Inherited from: ") + EditorInfoLabel::formatNodePath(definePath); + } + header_->setText(h); + valueEdit_->setProperty("form","1"); + nameEdit_->setText(name); + valueEdit_->setPlainText(value); + valueEdit_->setFocus(); + + if(frozen || readOnly) + { + nameEdit_->setReadOnly(true); + valueEdit_->setReadOnly(true); + + QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); + Q_ASSERT(sb); + sb->setEnabled(false); + } + + messageLabel_->hide(); + + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +VariablePropDialog::~VariablePropDialog() +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + Q_ASSERT(data_); + data_->removeObserver(this); + writeSettings(); +} + +void VariablePropDialog::accept() +{ + QString name=nameEdit_->text(); + //QString value=valueEdit_->toPlainText(); + + Q_ASSERT(data_); + Q_ASSERT(data_->count() > 0); + Q_ASSERT(data_->count() > defineIndex_); + + //var does not exists in SELECTED node + if(!data_->data(0)->hasName(name.toStdString())) + { + for(int i=1; i < data_->count(); i++) + { + //but exists in one of the parents + if(data_->data(i)->hasName(name.toStdString())) + { + QString type=QString::fromStdString(data_->data(i)->type()); + QString path =QString::fromStdString(data_->data(i)->fullPath()); + if(QMessageBox::question(0,tr("Confirm: create new variable"), + tr("Variable ") + name + tr(" is originally defined in ") + type + " " + path + + tr(". A new user variable will be created for ") + nodeType_ + " " + nodeName_ + + tr(" and shadow the original one.

    Do you want to proceed?"), + QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return; + } + else + { + QDialog::accept(); + return; + } + } + } + + //It is a ne variable + if(QMessageBox::question(0,tr("Confirm: create new variable"), + tr("You are about to create a new variable in ") + nodeType_ + " " + nodeName_ + "." + + tr("
    Do you want to proceed?"), + QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return; + } + else + { + QDialog::accept(); + return; + } + } + else if(data_->data(0)->isGenVar(name.toStdString())) + { + if(QMessageBox::question(0,QObject::tr("Confirm: change variable"), + tr("You are about to modify a generated variable.
    Do you want to proceed?"), + QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return; + } + else + { + QDialog::accept(); + return; + } + } + + QDialog::accept(); +} + +void VariablePropDialog::on_nameEdit__textEdited(QString) +{ + messageLabel_->hide(); +} + +void VariablePropDialog::on_valueEdit__textChanged() +{ + messageLabel_->hide(); +} + +QString VariablePropDialog::name() const +{ + return nameEdit_->text(); +} + +QString VariablePropDialog::value() const +{ + return valueEdit_->toPlainText(); +} + +void VariablePropDialog::notifyCleared(VariableModelDataHandler*) +{ + //When we are not in suspended mode and the data_ is cleared + //we need to close the dialogue + if(!suspended_) + close(); + + //However, when the suspended mode finished the data_ is cleared and reloaded before + //this dialogue gets the notification about the suspended mode change. So + //we delay the decision on what to do unitl we receieve this notification in + //slotSuspendedChanged() +} + +void VariablePropDialog::notifyUpdated(VariableModelDataHandler*) +{ + Q_ASSERT(data_); + + QString name=nameEdit_->text(); + QString value=valueEdit_->toPlainText(); + + bool st=false; + QString v=QString::fromStdString(data_->value(defineNodeName_.toStdString(),name.toStdString(),st)); + if(!st) + { + messageLabel_->showWarning("Variable " + name + " is not defined any more in " + defineNodeType_ + + " " + defineNodeName_ + "!"); + } + else if(v != value) + { + messageLabel_->showWarning("The value of variable " + name + " changed in " + defineNodeType_ + + " " + defineNodeName_ + "!"); + } + else + { + messageLabel_->hide(); + } +} + +void VariablePropDialog::slotSuspendedChanged(bool s) +{ + if(cleared_) + return; + + if(s) + { + messageLabel_->showWarning("The server holding " + nodeType_ + " " + nodeName_ + + " is being reloaded. \ + Until it is finished variables cannot be edited!"); + + suspendEdit(true); + } + else + { + messageLabel_->clear(); + messageLabel_->hide(); + suspendEdit(false); + + //We we have just left the suspended mode we need to chek if the data we + //show is still available + notifyUpdated(data_); + } +} + +void VariablePropDialog::suspendEdit(bool st) +{ + suspended_=st; + + if(st) + { + QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); + Q_ASSERT(sb); + sb->setEnabled(false); + form_->setEnabled(false); + } + else + { + QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); + Q_ASSERT(sb); + sb->setEnabled(true); + form_->setEnabled(true); + } +} + +void VariablePropDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariablePropDialog")), + QSettings::NativeFormat); + + //We have to clear it not to remember all the previous windows + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void VariablePropDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariablePropDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(350,250)); + } + + settings.endGroup(); +} + + +//====================================== +// +// VariableAddDialog +// +//====================================== + +VariableAddDialog::VariableAddDialog(VariableModelDataHandler *data,QWidget *parent) : + QDialog(parent), + data_(data), + cleared_(false), + suspended_(false) +{ + setupUi(this); + + init(); + nameEdit_->setFocus(); + + QString wt="Add variable"; + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + readSettings(); + + WidgetNameProvider::nameChildren(this); +} + +VariableAddDialog::VariableAddDialog(VariableModelDataHandler *data,QString name, QString value,QWidget *parent) : + QDialog(parent), + data_(data), + cleared_(false) +{ + setupUi(this); + + init(); + + nameEdit_->setText(name + "_copy"); + valueEdit_->setText(value); + nameEdit_->setFocus(); + + QString wt="Add variable"; + wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); + setWindowTitle(wt); + + readSettings(); +} + +VariableAddDialog::~VariableAddDialog() +{ + data_->removeObserver(this); + writeSettings(); +} + +void VariableAddDialog::init() +{ + setAttribute(Qt::WA_DeleteOnClose); + setModal(false); + + Q_ASSERT(data_); + Q_ASSERT(data_->count() > 0); + + nodeName_=QString::fromStdString(data_->data(0)->name()); + nodeType_=QString::fromStdString(data_->data(0)->type()); + nodeTypeCapital_=nodeType_; + if(nodeTypeCapital_.size() > 0) + { + QChar s=nodeTypeCapital_.at(0); + s=s.toUpper(); + nodeTypeCapital_.replace(0,1,s); + } + data_->addObserver(this); + + label_->setText(tr("Add new variable to ") + + nodeType_ + " " + + nodeName_ + "") ; + //+ "
    Path: " + + // QString::fromStdString(data_->data(0)->fullPath())); + + messageLabel_->hide(); +} + + +void VariableAddDialog::accept() +{ + QString name=nameEdit_->text(); + + if(name.simplified().isEmpty()) + { + QMessageBox::critical(0,tr("Invalid variable name"), + tr("Variable name cannot be empty! Please specify a valid name!"), + QMessageBox::Ok,QMessageBox::Ok); + return; + } + + Q_ASSERT(data_); + Q_ASSERT(data_->count() > 0); + + if(data_->data(0)->hasName(name.toStdString())) + { + QString q; + if(data_->data(0)->isGenVar(name.toStdString())) + { + q=tr("Generated variable ") + name + tr(" is already defined in ") + + nodeType_ + " " + nodeName_ + "" + + tr("
    . A new user variable will be created and the original variable will be hidden. \ +
    Do you want to proceed?"); + } + else + { + q=tr("User variable ") + name + tr(" is already defined in ") + + nodeType_ + " " + nodeName_ + "" + + tr(".
    Do you want to overwrite it?"); + } + + if(QMessageBox::question(0,tr("Confirm: overwrite variable"),q, + QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return; + } + else + { + QDialog::accept(); + } + return; + } + + for(int i=1; i count(); i++) + { + if(data_->data(i)->hasName(name.toStdString())) + { + QString nodeName=QString::fromStdString(data_->data(i)->name()); + QString nodeType=QString::fromStdString(data_->data(i)->type()); + + QString q; + if(data_->data(i)->isGenVar(name.toStdString())) + { + q=tr("Generated variable"); + } + else + { + q=tr("User variable"); + } + q+=" " + name + tr(" is already defined in ") + + nodeType + " " + nodeName + "" + + tr("
    . A new user variable will be created for ") + nodeType_ + " " + + nodeName_ + tr(" and shadow the original one. \ +
    Do you want to proceed?"); + + if(QMessageBox::question(0,tr("Confirm: overwrite variable"),q, + QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) + { + return; + } + else + { + QDialog::accept(); + } + return; + } + } + + QDialog::accept(); +} + +QString VariableAddDialog::name() const +{ + return nameEdit_->text(); +} + +QString VariableAddDialog::value() const +{ + return valueEdit_->text(); +} + +void VariableAddDialog::notifyCleared(VariableModelDataHandler*) +{ + //When we are not in suspended mode and the data_ is cleared + //we need to close the dialogue + if(!suspended_) + close(); + + //However, when the suspended mode finished the data_ is cleared and reloaded before + //this dialogue gets the notification about the suspended mode change. So + //we delay the decision on what to do unitl we receieve this notification in + //slotSuspendedChanged() + +#if 0 + messageLabel_->showWarning(nodeTypeCapital_ + " " + nodeName_ + + " is not the node to modify any more in the Variables panel. Please close the dialog!"); + + suspendEdit(true); + + data_->removeObserver(this); + cleared_=true; +#endif +} + +void VariableAddDialog::slotSuspendedChanged(bool s) +{ + if(cleared_) + return; + + if(s) + { + messageLabel_->showWarning("The server holding " + nodeType_ + " " + nodeName_ + + " is being reloaded. \ + Until it is finished variables cannot be added!"); + + suspendEdit(true); + } + else + { + messageLabel_->clear(); + messageLabel_->hide(); + suspendEdit(false); + + //We we have just left the suspended mode, so we need to chek if the data_ we + //show is still available + if(!data_ || data_->count() == 0 || + nodeName_ != QString::fromStdString(data_->data(0)->name()) || + nodeType_ != QString::fromStdString(data_->data(0)->type())) + { + close(); + } + } +} + +void VariableAddDialog::suspendEdit(bool st) +{ + suspended_=st; + + if(st) + { + QPushButton* sb=buttonBox_->button(QDialogButtonBox::Ok); + Q_ASSERT(sb); + sb->setEnabled(false); + form_->setEnabled(false); + } + else + { + QPushButton* sb=buttonBox_->button(QDialogButtonBox::Ok); + Q_ASSERT(sb); + sb->setEnabled(true); + form_->setEnabled(true); + } +} + +void VariableAddDialog::writeSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableAddDialog")), + QSettings::NativeFormat); + + //We have to clear it not to remember all the previous windows + settings.clear(); + + settings.beginGroup("main"); + settings.setValue("size",size()); + settings.endGroup(); +} + +void VariableAddDialog::readSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + Q_ASSERT(cs); + QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableAddDialog")), + QSettings::NativeFormat); + + settings.beginGroup("main"); + if(settings.contains("size")) + { + resize(settings.value("size").toSize()); + } + else + { + resize(QSize(320,220)); + } + + settings.endGroup(); +} + +//======================================================== +// +// VariableItemWidget +// +//======================================================== + +VariableItemWidget::VariableItemWidget(QWidget *parent) : + shadowProp_(0), + canSaveLastSelection_(true), + tableViewColumns_(0) +{ + //This item displays all the ancestors of the info object + useAncestors_=true; + + setupUi(this); + + data_=new VariableModelDataHandler(); + + //table view columns + tableViewColumns_=ModelColumn::def("table_columns"); + if(!tableViewColumns_) + { + UiLog().warn() << "Cannot find ModelColumn object for \"table_columns\""; + } + + //The model and the sort-filter model + model_=new VariableModel(data_,this); + sortModel_= new VariableSortModel(model_,this); + sortModel_->setDynamicSortFilter(true); + + //Set the model on the view + varView->setModel(sortModel_); + + varView->setSelectionBehavior(QAbstractItemView::SelectRows); + varView->setSelectionMode(QAbstractItemView::SingleSelection); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + varView->header()->setSectionResizeMode(1,QHeaderView::ResizeToContents); + varView->header()->setStretchLastSection(false); +#endif + + //Show shadowed variables by default + bool showShadowed = true; + + //But we overwrite it with the config settings. This property is directly edited + //via the toolbutton, so we do not need to observe it. + shadowProp_=VConfig::instance()->find("panel.variable.showShadowed"); + if(shadowProp_) + { + showShadowed=shadowProp_->value().toBool(); + } + sortModel_->slotShowShadowed(showShadowed); + shadowTb->setChecked(showShadowed); + + //Search and filter interface: we have a menu attached to a toolbutton and a + //stackedwidget connected up. + + //Populate the toolbuttons menu + findModeTb->addAction(actionFilter); + findModeTb->addAction(actionSearch); + + //The filter line editor + filterLine_=new LineEdit; + stackedWidget->addWidget(filterLine_); + + //The search line editor. It is a custom widget handling its own signals and slots. + searchLine_=new VariableSearchLine(this); + stackedWidget->addWidget(searchLine_); + searchLine_->setView(varView); + + //The filter editor changes + connect(filterLine_,SIGNAL(textChanged(QString)), + this,SLOT(slotFilterTextChanged(QString))); + + //The selection changes in the view + connect(varView->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), + this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); + + //Init the find mode selection + if(sortModel_->matchMode() == VariableSortModel::FilterMode) + { + actionFilter->trigger(); + } + else + { + actionSearch->trigger(); + } + + //Add context menu actions to the view + QAction* sep1=new QAction(this); + sep1->setSeparator(true); + QAction* sep2=new QAction(this); + sep2->setSeparator(true); + QAction* sep3=new QAction(this); + sep3->setSeparator(true); + QAction* sep4=new QAction(this); + sep4->setSeparator(true); + + //Build context menu + varView->addAction(actionAdd); + varView->addAction(sep1); + varView->addAction(actionCopy); + varView->addAction(actionCopyFull); + //varView->addAction(actionPaste); + varView->addAction(sep2); + varView->addAction(actionDelete); + varView->addAction(sep3); + varView->addAction(actionProp); + varView->addAction(sep4); + varView->addAction(actionAddToTableView); + + //Add actions for the toolbuttons + addTb->setDefaultAction(actionAdd); + deleteTb->setDefaultAction(actionDelete); + propTb->setDefaultAction(actionProp); + exportTb->setDefaultAction(actionExport); + + //TODO: implement it + actionExport->setEnabled(false); + exportTb->setVisible(false); + + //Initialise action state (it depends on the selection) + checkActionState(); +} + +VariableItemWidget::~VariableItemWidget() +{ + +} + +QWidget* VariableItemWidget::realWidget() +{ + return this; +} + +//A new info object is set +void VariableItemWidget::reload(VInfo_ptr info) +{ + assert(active_); + + if(suspended_) + return; + + clearContents(); + adjust(info); + + data_->reload(info); + varView->expandAll(); + varView->resizeColumnToContents(0); + + if(data_->count() > 0) + { + actionAdd->setText(tr("Add &new variable to ") + + QString::fromStdString(data_->data(0)->name())); + + actionAdd->setToolTip(tr("Add new variable to ") + + "" + QString::fromStdString(data_->data(0)->name()) + ""); + } + + checkActionState(); +} + +void VariableItemWidget::clearContents() +{ + InfoPanelItem::clear(); + data_->clear(); + lastSelection_.reset(); + expanded_.clear(); + actionAdd->setText(tr("Add &new variable")); +} + + +void VariableItemWidget::slotItemSelected(const QModelIndex& idx,const QModelIndex& /*prevIdx*/) +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + + //remembers the last clicked variable + UiLog().dbg() << idx; + if(canSaveLastSelection_) + lastSelection_=model_->indexToInfo(sortModel_->mapToSource(idx)); + + checkActionState(); +} + +void VariableItemWidget::updateState(const FlagSet& flags) +{ + if(flags.isSet(SuspendedChanged)) + { + if(info_) + { + //If it just became non-suspended we need to refresh all the data!!! + if(!suspended_) + { + canSaveLastSelection_=false; + data_->reload(info_); + + restoreExpandState(); + + //After any change done we need to reselect the current row. See issue ECFLOW-613. + regainSelection(); + canSaveLastSelection_=true; + } + else + { + saveExpandState(); + } + } + + //It is very important to only emit the signal when the + //data has been updated! In this way the dialogues can check if their contents are + //in sync with what data_ stores. + Q_EMIT suspendedChanged(suspended_); + } + checkActionState(); +} + +void VariableItemWidget::checkActionState() +{ + QModelIndex index=sortModel_->mapToSource(varView->currentIndex()); + + if(suspended_ || !info_) + { + actionAdd->setEnabled(false); + actionProp->setEnabled(false); + actionDelete->setEnabled(false); + actionCopy->setEnabled(false); + actionCopyFull->setEnabled(false); + actionAddToTableView->setEnabled(false); + return; + } + + //The index is invalid (no selection) + if(!index.isValid()) + { + actionProp->setEnabled(false); + actionDelete->setEnabled(false); + actionCopy->setEnabled(false); + actionCopyFull->setEnabled(false); + actionAddToTableView->setEnabled(false); + } + else + { + //Variables + if(model_->isVariable(index)) + { + if(frozen_) + { + actionDelete->setEnabled(false); + } + else + { + int block=-1; + if(model_->indexToData(index,block)) + { + if(block==0) + actionDelete->setEnabled(true); + else + actionDelete->setEnabled(false); + } + else + actionDelete->setEnabled(false); + } + actionProp->setEnabled(true); + actionCopy->setEnabled(true); + actionCopyFull->setEnabled(true); + + if(tableViewColumns_) + { + QString varName=model_->data(index).toString(); + actionAddToTableView->setEnabled(tableViewColumns_->indexOf(varName) == -1); + } + else + { + actionAddToTableView->setEnabled(false); + } + } + //Server or nodes + else + { + if(frozen_) + { + actionDelete->setEnabled(false); + } + else + { + actionDelete->setEnabled(false); + } + actionProp->setEnabled(false); + actionCopy->setEnabled(false); + actionCopyFull->setEnabled(false); + actionAddToTableView->setEnabled(false); + } + } + + if(frozen_) + { + actionAdd->setEnabled(false); + } + else + { + actionAdd->setEnabled(true); + } +} + +void VariableItemWidget::editItem(const QModelIndex& index) +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG + UiLog().dbg() << " index=" << index; +#endif + + QString name; + QString value; + bool genVar; + + QModelIndex vIndex=sortModel_->mapToSource(index); +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UiLog().dbg() << "vIndex=" << vIndex; +#endif + + int block=-1; + if(model_->indexToData(vIndex,block)) + { + Q_ASSERT(data_->count() > 0); + Q_ASSERT(block >=0); + +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UiLog().dbg() << " block=" << block; +#endif + + if(model_->variable(vIndex,name,value,genVar)) + { + //Start the edit dialog (will be deleted on close - deleteOnClose is set) + VariablePropDialog* d=new VariablePropDialog(data_,block,name,value,frozen_,this); + connect(d,SIGNAL(accepted()), + this,SLOT(slotVariableEdited())); + connect(this,SIGNAL(suspendedChanged(bool)), + d,SLOT(slotSuspendedChanged(bool))); + d->show(); + } + } +} + +void VariableItemWidget::duplicateItem(const QModelIndex& index) +{ + if(frozen_) + return; + +#if 0 + QString name; + QString value; + bool genVar; + + QModelIndex vIndex=sortModel_->mapToSource(index); + + VariableModelData* data=model_->indexToData(vIndex); + + //Get the data from the model + if(data && model_->variable(vIndex,name,value,genVar)) + { + //Start add dialog + VariableAddDialog d(data,name,value,this); + + if(d.exec() == QDialog::Accepted) + { + data->alter(d.name().toStdString(),d.value().toStdString()); + //data->add(d.name().toStdString(),d.value().toStdString()); + } + } +#endif + +} + +void VariableItemWidget::addItem(const QModelIndex& index) +{ + if(frozen_) + return; + + if(data_->count() > 0) + { + //Start add dialog (will be deleted on close - deleteOnClose is set) + VariableAddDialog* d=new VariableAddDialog(data_,this); + connect(d,SIGNAL(accepted()), + this,SLOT(slotVariableAdded())); + connect(this,SIGNAL(suspendedChanged(bool)), + d,SLOT(slotSuspendedChanged(bool))); + d->show(); + + } +} + +void VariableItemWidget::removeItem(const QModelIndex& index) +{ + if(frozen_) + return; + + QString name; + QString value; + bool genVar; + + QModelIndex vIndex=sortModel_->mapToSource(index); + + VariableModelData* data=model_->indexToData(vIndex); + + //Get the data from the model + if(data && model_->variable(vIndex,name,value,genVar)) + { + std::string nodePath=data->fullPath(); + std::string nodeName=data->name(); + + if(QMessageBox::question(0,tr("Confirm: delete variable"), + tr("Are you sure that you want to delete variable ") + name + " from " + + QString::fromStdString(data->type()) + " " + QString::fromStdString(data->name()) + "?", + QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Ok) + { + //data might have been changed while the dialog was open + //so we cant use the data object but need to look up the variable again + int block=-1; + int row=-1; + data_->findVariable(name.toStdString(),nodePath,genVar,block,row); + if(block != -1 && row != -1) + { + data_->data(block)->remove(name.toStdString()); + } + else + { + std::string msg="Could not delete variable=" + name.toStdString() + + " from node " + nodeName + "!"; + + if(block == -1) + msg+=" Node does not exist."; + else + msg+=" Variable does not exist."; + + UserMessage::message(UserMessage::ERROR,true,msg); + } + } + } +} + +//Called when the variable has been edited in the dialogue +void VariableItemWidget::slotVariableEdited() +{ + VariablePropDialog* d=static_cast(sender()); + Q_ASSERT(d); + + if(data_->count() > 0) + { + //This will call the ServerComThread so we + //do not know if it was successful or not. The model will be + //updated through the observer when the value will actually + //change. + //We always perform the alter variable operation on the selected + //node i.e. on block 0 = data(0) !!! + data_->data(0)->alter(d->name().toStdString(),d->value().toStdString()); + } +} + +void VariableItemWidget::slotVariableAdded() +{ + VariableAddDialog* d=static_cast(sender()); + Q_ASSERT(d); + Q_ASSERT(data_->count() > 0); + //We always perform the alter variable operation on the selected + //node i.e. on block 0 = data(0) !!! + data_->data(0)->alter(d->name().toStdString(),d->value().toStdString()); +} + +void VariableItemWidget::on_varView_doubleClicked(const QModelIndex& index) +{ + if(!suspended_) + editItem(index); +} + +void VariableItemWidget::on_actionProp_triggered() +{ + QModelIndex index=varView->currentIndex(); + editItem(index); +} + +void VariableItemWidget::on_actionAdd_triggered() +{ + QModelIndex index=varView->currentIndex(); + addItem(index); +} + +void VariableItemWidget::on_actionDelete_triggered() +{ + QModelIndex index=varView->currentIndex(); + removeItem(index); +} + +void VariableItemWidget::on_actionFilter_triggered() +{ + findModeTb->setIcon(actionFilter->icon()); + sortModel_->setMatchMode(VariableSortModel::FilterMode); + filterLine_->clear(); + searchLine_->clear(); + + //Notify stackedwidget + stackedWidget->setCurrentIndex(0); +} + +void VariableItemWidget::on_actionSearch_triggered() +{ + findModeTb->setIcon(actionSearch->icon()); + sortModel_->setMatchMode(VariableSortModel::SearchMode); + filterLine_->clear(); + searchLine_->clear(); + + //Notify stackedwidget + stackedWidget->setCurrentIndex(1); + +} + +void VariableItemWidget::on_actionCopy_triggered() +{ + QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); + QString name, val; + bool gen; + + if(model_->variable(idx,name,val,gen)) + { + QString txt; + if(idx.column() == 0) + toClipboard(name); + else if(idx.column() == 1) + toClipboard(val); + } +} + +void VariableItemWidget::on_actionCopyFull_triggered() +{ + QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); + QString name, val; + bool gen; + + if(model_->variable(idx,name,val,gen)) + { + toClipboard(name + "=" + val); + } +} + +void VariableItemWidget::on_actionAddToTableView_triggered() +{ + if(tableViewColumns_) + { + QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); + QString name=model_->data(idx).toString(); + tableViewColumns_->addExtraItem(name,name); + } +} + +void VariableItemWidget::on_shadowTb_clicked(bool showShadowed) +{ + if(shadowProp_) + { + shadowProp_->setValue(showShadowed); + } + sortModel_->slotShowShadowed(showShadowed); +} + +void VariableItemWidget::toClipboard(QString txt) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QClipboard* cb=QGuiApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#else + QClipboard* cb=QApplication::clipboard(); + cb->setText(txt, QClipboard::Clipboard); + cb->setText(txt, QClipboard::Selection); +#endif +} + +void VariableItemWidget::slotFilterTextChanged(QString text) +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + sortModel_->setMatchText(text); + regainSelection(); +} + +void VariableItemWidget::nodeChanged(const VNode* node, const std::vector& aspect) +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + canSaveLastSelection_=false; + if(data_->nodeChanged(node,aspect)) + { + //After any change done we need to reselect the current row. See issue ECFLOW-613. + regainSelection(); + } + canSaveLastSelection_=true; +} + +void VariableItemWidget::defsChanged(const std::vector& aspect) +{ +#ifdef _UI_VARIABLEITEMWIDGET_DEBUG + UI_FUNCTION_LOG +#endif + canSaveLastSelection_=false; + if(data_->defsChanged(aspect)) + { + //After any change we need to reselect the current row. See issue ECFLOW-613. + regainSelection(); + } + canSaveLastSelection_=true; +} + +//Try to regain the selection stored in lastSelection_ potentailly after a +//full model reset!!! +void VariableItemWidget::regainSelection() +{ + if(lastSelection_) + { + lastSelection_->regainData(); + if(lastSelection_->hasData()) + { + QModelIndex idx=model_->infoToIndex(lastSelection_); + if(idx.isValid()) + { + varView->setCurrentIndex(sortModel_->mapFromSource(idx)); + } + } + else + { + lastSelection_.reset(); + } + } + +#if 0 + QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); + if(idx.column() == 1) + { + idx=model_->index(idx.row(),0,idx.parent()); + } + varView->selectionModel()->clear(); + QModelIndex sortIdx=sortModel_->mapFromSource(idx); + varView->selectionModel()->setCurrentIndex(sortIdx,QItemSelectionModel::Rows| + QItemSelectionModel::Select); +#endif +} + +void VariableItemWidget::saveExpandState() +{ + expanded_.clear(); + for(int i=0; i< varView->model()->rowCount(); i++) + { + expanded_ << varView->isExpanded(varView->model()->index(i,0)); + } +} + +void VariableItemWidget::restoreExpandState() +{ + if(expanded_.isEmpty()) + varView->expandAll(); + else + { + for(int i=0; i< varView->model()->rowCount() && i < expanded_.count(); i++) + { + varView->setExpanded(varView->model()->index(i,0),expanded_[i]); + } + } +} + + +//Register at the factory +static InfoPanelItemMaker maker1("variable"); diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,169 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VARIABLEITEMWIDGET_HPP_ +#define VARIABLEITEMWIDGET_HPP_ + +#include "ui_VariablePropDialog.h" +#include "ui_VariableAddDialog.h" +#include "ui_VariableItemWidget.h" + +#include "InfoPanelItem.hpp" +#include "VariableModelDataObserver.hpp" +#include "VInfo.hpp" + +class LineEdit; +class ModelColumn; +class VariableModel; +class VariableModelData; +class VariableModelDataHandler; +class VariableSortModel; +class VariableSearchLine; +class VProperty; + +class VariablePropDialog : public QDialog, public VariableModelDataObserver, private Ui::VariablePropDialog +{ +Q_OBJECT + +public: + VariablePropDialog(VariableModelDataHandler* data,int defineIndex,QString name,QString value,bool frozen,QWidget* parent=0); + ~VariablePropDialog(); + + QString name() const; + QString value() const; + + void notifyCleared(VariableModelDataHandler*); + void notifyUpdated(VariableModelDataHandler*); + +public Q_SLOTS: + void accept(); + void slotSuspendedChanged(bool s); + +protected Q_SLOTS: + void on_nameEdit__textEdited(QString); + void on_valueEdit__textChanged(); + +protected: + void suspendEdit(bool); + void readSettings(); + void writeSettings(); + + bool genVar_; + VariableModelDataHandler* data_; + int defineIndex_; + QString oriName_; + QString nodeName_; + QString nodeType_; + QString nodeTypeCapital_; + QString defineNodeName_; + QString defineNodeType_; + bool cleared_; + bool suspended_; + +}; + +class VariableAddDialog : public QDialog, public VariableModelDataObserver, private Ui::VariableAddDialog +{ +Q_OBJECT + +public: + VariableAddDialog(VariableModelDataHandler* data,QWidget* parent=0); + VariableAddDialog(VariableModelDataHandler* data,QString name,QString value,QWidget* parent=0); + ~VariableAddDialog(); + + QString name() const; + QString value() const; + + void notifyCleared(VariableModelDataHandler*); + void notifyUpdated(VariableModelDataHandler*) {} + +public Q_SLOTS: + void accept(); + void slotSuspendedChanged(bool s); + +protected: + void init(); + void suspendEdit(bool); + void readSettings(); + void writeSettings(); + + VariableModelDataHandler* data_; + QString nodeName_; + QString nodeType_; + QString nodeTypeCapital_; + bool cleared_; + bool suspended_; +}; + + +class VariableItemWidget : public QWidget, public InfoPanelItem, protected Ui::VariableItemWidget +{ +Q_OBJECT + +public: + explicit VariableItemWidget(QWidget *parent=0); + ~VariableItemWidget(); + + void reload(VInfo_ptr); + QWidget* realWidget(); + void clearContents(); + +public Q_SLOTS: + void slotFilterTextChanged(QString text); + void slotItemSelected(const QModelIndex& idx,const QModelIndex& prevIdx); + +protected Q_SLOTS: + void on_actionProp_triggered(); + void on_actionAdd_triggered(); + void on_actionDelete_triggered(); + void on_varView_doubleClicked(const QModelIndex& index); + void on_actionFilter_triggered(); + void on_actionSearch_triggered(); + void on_actionCopy_triggered(); + void on_actionCopyFull_triggered(); + void on_actionAddToTableView_triggered(); + void on_shadowTb_clicked(bool showShadowed); + void slotVariableEdited(); + void slotVariableAdded(); + +Q_SIGNALS: + void suspendedChanged(bool); + +protected: + void checkActionState(); + void editItem(const QModelIndex& index); + void duplicateItem(const QModelIndex& index); + void addItem(const QModelIndex& index); + void removeItem(const QModelIndex& index); + void updateState(const ChangeFlags&); + void toClipboard(QString txt) const; + void regainSelection(); + void saveExpandState(); + void restoreExpandState(); + + void nodeChanged(const VNode*, const std::vector&); + void defsChanged(const std::vector&); + + VariableModelDataHandler* data_; + VariableModel* model_; + VariableSortModel* sortModel_; + + LineEdit* filterLine_; + VariableSearchLine *searchLine_; + + VProperty* shadowProp_; + VInfo_ptr lastSelection_; + bool canSaveLastSelection_; + QList expanded_; + ModelColumn *tableViewColumns_; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.ui ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableItemWidget.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,272 @@ + + + VariableItemWidget + + + + 0 + 0 + 615 + 482 + + + + Form + + + + 2 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + ... + + + QToolButton::InstantPopup + + + Qt::ToolButtonIconOnly + + + true + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Show <b>shadowed</b> variables. A variable is shadowed when it is redefined in one of the descendants of its node shown in this panel. + + + + + + + :/viewer/show_shadowed.svg:/viewer/show_shadowed.svg + + + true + + + + + + + ... + + + true + + + + + + + ... + + + true + + + + + + + ... + + + true + + + + + + + ... + + + true + + + + + + + + + + + + + :/viewer/images/add.svg:/viewer/images/add.svg + + + Add &new variable + + + Add new variable + + + Ctrl+N + + + + + + :/viewer/edit.svg:/viewer/edit.svg + + + &Edit variable + + + See/edit variable's properties + + + Ctrl+E + + + + + + :/viewer/close.svg:/viewer/close.svg + + + &Delete variable + + + Delete variable + + + Del + + + + + + :/viewer/filesave.svg:/viewer/filesave.svg + + + E&xport + + + Export variables + + + Ctrl+S + + + + + + :/viewer/editcopy.svg:/viewer/editcopy.svg + + + &Copy item text under cursor + + + Copy variable + + + Ctrl+C + + + + + + :/viewer/editpaste.svg:/viewer/editpaste.svg + + + Pa&ste as new + + + Paste as new variable + + + Ctrl+V + + + + + + :/viewer/filter_decor.svg:/viewer/filter_decor.svg + + + &Filter mode + + + Filter mode + + + + + + :/viewer/search_decor.svg:/viewer/search_decor.svg + + + &Search mode + + + Search mode + + + + + Copy text for both Name and Value + + + Copy variable's name and value + + + + + + :/viewer/add_variable_column.svg:/viewer/add_variable_column.svg + + + Add variable to table view + + + + + + VariableView + QTreeView +
    VariableView.hpp
    +
    +
    + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableModel.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableModel.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableModel.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableModel.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,789 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VariableModel.hpp" + +#include +#include + +#include "ServerHandler.hpp" +#include "UIDebug.hpp" +#include "UiLog.hpp" +#include "VariableModelData.hpp" + +QColor VariableModel::varCol_=QColor(40,41,42); +//QColor VariableModel::genVarCol_=QColor(34,51,136); +QColor VariableModel::genVarCol_=QColor(0,115,48); +QColor VariableModel::shadowCol_=QColor(130,130,130); +QColor VariableModel::blockBgCol_=QColor(122,122,122); +QColor VariableModel::blockFgCol_=QColor(255,255,255); + +#define _UI_VARIABLEMODEL_DEBUG + +//======================================================================= +// +// VariabletModel +// +//======================================================================= + +VariableModel::VariableModel(VariableModelDataHandler* data,QObject *parent) : + QAbstractItemModel(parent), + data_(data) +{ + connect(data_,SIGNAL(reloadBegin()), + this,SLOT(slotReloadBegin())); + + connect(data_,SIGNAL(reloadEnd()), + this,SLOT(slotReloadEnd())); + + connect(data_,SIGNAL(clearBegin(int,int)), + this,SLOT(slotClearBegin(int,int))); + + connect(data_,SIGNAL(clearEnd(int,int)), + this,SLOT(slotClearEnd(int,int))); + + connect(data_,SIGNAL(loadBegin(int,int)), + this,SLOT(slotLoadBegin(int,int))); + + connect(data_,SIGNAL(loadEnd(int,int)), + this,SLOT(slotLoadEnd(int,int))); + + connect(data_,SIGNAL(dataChanged(int)), + this,SLOT(slotDataChanged(int))); + + connect(data_,SIGNAL(rerunFilter()), + this,SIGNAL(rerunFilter())); +} + +bool VariableModel::hasData() const +{ + return (data_->count() > 0); +} + +int VariableModel::columnCount( const QModelIndex& /*parent */ ) const +{ + return 2; +} + +int VariableModel::rowCount( const QModelIndex& parent) const +{ + + //Parent is the root: the item must be a node or a server + if(!parent.isValid()) + { + return data_->count(); + } + //The parent is a server or a node + else if(!isVariable(parent)) + { + int row=parent.row(); + return data_->varNum(row); + } + + //parent is a variable + return 0; +} + +Qt::ItemFlags VariableModel::flags ( const QModelIndex & index) const +{ + Qt::ItemFlags defaultFlags; + + defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; + + return defaultFlags; +} + +QVariant VariableModel::data( const QModelIndex& index, int role ) const +{ + if( !index.isValid()) + { + return QVariant(); + } + + //Data lookup can be costly so we immediately return a default value for all + //the cases where the default should be used. + if(role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::ForegroundRole && + role != ReadOnlyRole && role != Qt::ToolTipRole && role != GenVarRole && role != Qt::FontRole && + role != ShadowRole ) + { + return QVariant(); + } + + int row=index.row(); + int level=indexToLevel(index); + + //Server or node + if(level == 1) + { + if(role == ReadOnlyRole) + return QVariant(); + + if(role == Qt:: BackgroundRole) + return blockBgCol_; + + else if(role == Qt::ForegroundRole) + return blockFgCol_; + + + VariableModelData *d=data_->data(row); + if(!d) + { + return QVariant(); + } + + if(index.column() == 0) + { + if(role == Qt::DisplayRole) + { + if(index.row() ==0) + return "defined in " + QString::fromStdString(d->type()) + " " + QString::fromStdString(d->name()); + else + return "inherited from " + QString::fromStdString(d->type()) + " " + QString::fromStdString(d->name()); + } + } + + return QVariant(); + } + + //Variables + else if (level == 2) + { + VariableModelData *d=data_->data(index.parent().row()); + if(!d) + { + return QVariant(); + } + + if(role == Qt::ForegroundRole) + { + if(d->isShadowed(row)) + { + return shadowCol_; + } + + //Generated variable + if(d->isGenVar(row) && index.column() == 0) + return genVarCol_; + else + return varCol_; + } + else if(role == Qt::DisplayRole) + { + if(index.column() == 0) + { + QString s=QString::fromStdString(d->name(row)); + //if(d->isGenVar(row)) + // s+=" (g)"; + return s; + } + else if(index.column() == 1) + { + return QString::fromStdString(d->value(row)); + } + } + else if(role == Qt::ToolTipRole) + { + QString s="User defined variable"; + if(d->isGenVar(row)) + { + s="Generated variable"; + } + if(d->isReadOnly(row)) + s+= " (read only)"; + + if(d->isShadowed(row)) + { + s+=".
    Please note that this variable is shadowed i.e. \ + overwritten in one of the descendants of this node shown in this panel!"; + } + return s; + } + else if(role == ReadOnlyRole) + { + return (d->isReadOnly(row))?true:false; + } + + else if(role == GenVarRole) + { + return (d->isGenVar(row))?true:false; + } + + else if(role == ShadowRole) + { + return (d->isShadowed(row))?true:false; + } + + return QVariant(); + } + + return QVariant(); +} + +bool VariableModel::variable(const QModelIndex& idx, QString& name,QString& value,bool& genVar) const +{ + int block=-1; + int row=-1; + + identify(idx,block,row); + + if(row < 0) + return false; + + if(block >=0 && block < data_->count()) + { + name=QString::fromStdString(data_->data(block)->name(row)); + value=QString::fromStdString(data_->data(block)->value(row)); + genVar=data_->data(block)->isGenVar(row); + return true; + } + + return false; +} + +QVariant VariableModel::headerData( const int section, const Qt::Orientation orient , const int role ) const +{ + if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QAbstractItemModel::headerData( section, orient, role ); + + if(role == Qt::DisplayRole) + { + switch ( section ) + { + case 0: return tr("Name"); + case 1: return tr("Value"); + default: return QVariant(); + } + } + else if(role== Qt::ToolTipRole) + { + switch ( section ) + { + case 0: return tr("Name"); + case 1: return tr("Value"); + default: return QVariant(); + } + } + return QVariant(); +} + +QModelIndex VariableModel::index( int row, int column, const QModelIndex & parent ) const +{ + if(!hasData() || row < 0 || column < 0) + { + return QModelIndex(); + } + + //When parent is the root this index refers to a node or server + if(!parent.isValid()) + { +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + return createIndex(row,column,quintptr(0)); +#else + return createIndex(row,column,0); +#endif + } + + //We are under one of the nodes + else + { + return createIndex(row,column,(parent.row()+1)*1000); + } + + return QModelIndex(); + +} + +QModelIndex VariableModel::parent(const QModelIndex &child) const +{ + if(!child.isValid()) + return QModelIndex(); + + int level=indexToLevel(child); + if(level == 1) + return QModelIndex(); + else if(level == 2) + { + int id=child.internalId(); + int r=id/1000-1; +#ifdef ECFLOW_QT5 + return createIndex(r,child.column(),quintptr(0)); +#else + return createIndex(r,child.column(),0); +#endif + } + + return QModelIndex(); +} + +//---------------------------------------------- +// +// Server to index mapping and lookup +// +//---------------------------------------------- + +int VariableModel::indexToLevel(const QModelIndex& index) const +{ + if(!index.isValid()) + return 0; + + int id=index.internalId(); + if(id >=0 && id < 1000) + { + return 1; + } + return 2; +} + +VariableModelData* VariableModel::indexToData(const QModelIndex& index) const +{ + int block=-1; + return indexToData(index,block); +} + +VariableModelData* VariableModel::indexToData(const QModelIndex& index,int& block) const +{ + int row; + + identify(index,block,row); + + if(block != -1) + return data_->data(block); + + return NULL; +} + +VInfo_ptr VariableModel::indexToInfo(const QModelIndex& index) const +{ + if(VariableModelData* d=indexToData(index)) + { + //It is a block + QModelIndex p=index.parent(); + + //It is a block + //if(!index.parent().isValid()) <-- this did not work + if(!p.isValid()) + return d->info(); + //it is a variable within a block + else + return d->info(index.row()); + } + return VInfo_ptr(); +} + +QModelIndex VariableModel::infoToIndex(VInfo_ptr info) const +{ + if(!info) + return QModelIndex(); + + int block=-1; + int row=-1; + data_->findVariable(info,block,row); + + if(block != -1) + { + QModelIndex blockIndex=index(block,0); + if(row != -1) + { + return index(row,0,blockIndex); + } + return blockIndex; + } + + return QModelIndex(); +} + +//---------------------------------------------- +// +// Server to index mapping and lookup +// +//---------------------------------------------- + +bool VariableModel::isVariable(const QModelIndex & index) const +{ + return (indexToLevel(index) == 2); +} + +void VariableModel::identify(const QModelIndex& index,int& block,int& row) const +{ + block=-1; + row=-1; + + if(!index.isValid()) + { + return; + } + + int level=indexToLevel(index); + + if(level == 1) + { + block=index.row(); + row=-1; + } + else if(level == 2) + { + block=parent(index).row(); + row=index.row(); + } +} + +void VariableModel::slotReloadBegin() +{ + //Reset the whole model + beginResetModel(); +} + +void VariableModel::slotReloadEnd() +{ + endResetModel(); +} + +void VariableModel::slotClearBegin(int block,int num) +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndex parent=index(block,0); + if(!parent.isValid()) + return; + + int rc=rowCount(parent); + UI_ASSERT(num >= 0," num=" << num); + UI_ASSERT(num == rc," num=" << num << + " rowCount=" << rowCount(parent)); + + if(num > 0) + { + beginRemoveRows(parent,0,num-1); + } +} + +void VariableModel::slotClearEnd(int block,int num) +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UI_FUNCTION_LOG +#endif + + QModelIndex parent=index(block,0); + if(!parent.isValid()) + return; + + UI_ASSERT(num >= 0,"num=" << num); + if(num > 0) + { + endRemoveRows(); + } +} + +void VariableModel::slotLoadBegin(int block,int num) +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndex parent=index(block,0); + if(!parent.isValid()) + return; + + int rc=rowCount(parent); + UI_ASSERT(num >= 0,"num=" << num); + UI_ASSERT(rc==0,"rowCount=" << rowCount(parent)); + + if(num > 0) + { + beginInsertRows(parent,0,num-1); + } +} + +void VariableModel::slotLoadEnd(int block,int num) +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndex parent=index(block,0); + if(!parent.isValid()) + return; + + UI_ASSERT(num >= 0,"num=" << num); + if(num > 0) + { + endInsertRows(); + } +} + +//It must be called after any data change +void VariableModel::slotDataChanged(int block) +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UI_FUNCTION_LOG +#endif + QModelIndex blockIndex0=index(block,0); + QModelIndex blockIndex1=index(block,1); + +#ifdef _UI_VARIABLEMODEL_DEBUG + UiLog().dbg() << " emit dataChanged:" << " " << blockIndex0 << " " << blockIndex1; +#endif + + //This will sort and filter the block + Q_EMIT dataChanged(blockIndex0,blockIndex1); +} + +//======================================================================= +// +// VariableSortModel +// +//======================================================================= + +VariableSortModel::VariableSortModel(VariableModel *varModel,QObject* parent) : + QSortFilterProxyModel(parent), + varModel_(varModel), + showShadowed_(true), + matchMode_(FilterMode), + ignoreDuplicateNames_(false) +{ + QSortFilterProxyModel::setSourceModel(varModel_); + setDynamicSortFilter(true); + + connect(varModel_,SIGNAL(filterChanged()), + this,SLOT(slotFilterChanged())); + + connect(varModel_,SIGNAL(rerunFilter()), + this,SLOT(slotRerunFilter())); +} + +void VariableSortModel::setMatchMode(MatchMode mode) +{ + if(matchMode_ == mode) + return; + + matchMode_=mode; + + matchLst_.clear(); + matchText_.clear(); + + //reload the filter model + invalidate(); +} + +void VariableSortModel::setMatchText(QString txt) +{ + matchText_=txt; + + if(matchMode_ == FilterMode) + { + //reload the filter model + invalidate(); + } +} + +void VariableSortModel::print(const QModelIndex idx) +{ + if(rowCount(idx) > 0) + UiLog().dbg() << "--> " << idx << " " << mapToSource(idx) << " " << data(idx); + else + UiLog().dbg() << idx << " " << mapToSource(idx) << " " << data(idx); + + if(rowCount(idx) > 0) UiLog().dbg() << "children: "; + for(int i=0; i < rowCount(idx); i++) + { + print(index(i,0,idx)); + } +} + +void VariableSortModel::slotFilterChanged() +{ + if(matchMode_ == FilterMode) + { + invalidate(); + } +} + +void VariableSortModel::slotRerunFilter() +{ +#ifdef _UI_VARIABLEMODEL_DEBUG + UiLog().dbg() << "VariableSortModel::slotRerunFilter-->"; +#endif + invalidate(); +} + +void VariableSortModel::slotShowShadowed(bool b) +{ + if(showShadowed_ != b) + { + showShadowed_=b; + invalidate(); + } +} + +bool VariableSortModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const +{ + //Node or server. Here we want the nodes and server to stay unsorted. That is the order should stay as + //it is defined in the data handler: the selected node stays on top and its ancestors and the server + //follow each other downwards. This order is reflected in the row index of these items in + //the varModel: the selected node's row is 0, its parent's row is 1, etc. + if(!varModel_->isVariable(sourceLeft)) + { + if(sortOrder() == Qt::AscendingOrder) + return (sourceLeft.row() < sourceRight.row()); + else + return (sourceLeft.row() > sourceRight.row()); + } + //For variables we simply sort according to the string + else + { + //UiLog().dbg() << varModel_->data(sourceLeft,Qt::DisplayRole).toString() << " " << varModel_->data(sourceRight,Qt::DisplayRole).toString(); + return varModel_->data(sourceLeft,Qt::DisplayRole).toString() < varModel_->data(sourceRight,Qt::DisplayRole).toString(); + } + return true; +} + +bool VariableSortModel::filterAcceptsRow(int sourceRow,const QModelIndex& sourceParent) const +{ + if(!sourceParent.isValid()) + return true; + + QModelIndex idx=varModel_->index(sourceRow,0,sourceParent); + + if(!showShadowed_) + { + if(varModel_->data(idx,VariableModel::ShadowRole).toBool()) + return false; + } + + if(matchMode_ != FilterMode || matchText_.simplified().isEmpty()) + return true; + + QModelIndex idx2=varModel_->index(sourceRow,1,sourceParent); + + QString s=varModel_->data(idx,Qt::DisplayRole).toString(); + QString s2=varModel_->data(idx2,Qt::DisplayRole).toString(); + + if(s.contains(matchText_,Qt::CaseInsensitive) || s2.contains(matchText_,Qt::CaseInsensitive)) + { + return true; + } + + return false; +} + +QVariant VariableSortModel::data(const QModelIndex& idx,int role) const +{ + if(role != Qt::UserRole) + { + return QSortFilterProxyModel::data(idx,role); + } + + //We highlight the matching items (the entire row). + if(matchMode_ == SearchMode && matchLst_.count() >0) + { + int col2=(idx.column()==0)?1:0; + QModelIndex idx2=index(idx.row(),col2,idx.parent()); + + if(matchLst_.contains(idx) || matchLst_.contains(idx2)) + return QColor(169,210,176); + } + + return QSortFilterProxyModel::data(idx,role); +} + + +QModelIndexList VariableSortModel::match(const QModelIndex& start,int role,const QVariant& value,int hits,Qt::MatchFlags flags) const +{ + if(matchMode_ != SearchMode) + return QModelIndexList(); + + QModelIndex root; + matchText_=value.toString(); + + matchLst_.clear(); + + if(matchText_.simplified().isEmpty()) + return matchLst_; + + for(int i=0; i < rowCount(); i++) + { + QModelIndex idx=index(i,0); + for(int row=0; row < rowCount(idx);row++) + { + //Name column + QModelIndex colIdx=index(row,0,idx); + QString s=data(colIdx,Qt::DisplayRole).toString(); + + if(s.contains(matchText_,Qt::CaseInsensitive)) + { + matchLst_ << colIdx; + continue; + } + + //Value columns + QModelIndex colIdx1=index(row,1,idx); + s=data(colIdx1,Qt::DisplayRole).toString(); + if(s.contains(matchText_,Qt::CaseInsensitive)) + { + matchLst_ << colIdx; + } + } + } + + return matchLst_; +} + +#if 0 +void VariableSortModel::test() +{ + UI_FUNCTION_LOG + QModelIndex idx; + test(idx); + testSource(idx); +} + +void VariableSortModel::test(const QModelIndex& p) +{ + int num=rowCount(p); + for(int i=0; i < num; i++) + { + QModelIndex idx=index(i,0,p); + QModelIndex sIdx=mapToSource(idx); + if(!sIdx.isValid()) + { + UiLog().dbg() << " idx=" << idx; + Q_ASSERT(sIdx.isValid()); + } + //UiLog().dbg() << idx.data().toString() << " " << sIdx.data().toString(); + + if(idx.data().toString() != sIdx.data().toString()) + { + UI_ASSERT(0,"filter=" << idx.data().toString() << + " source=" << sIdx.data().toString()); + } + + test(idx); + } +} + +void VariableSortModel::testSource(const QModelIndex& p) +{ + int num=varModel_->rowCount(p); + for(int i=0; i < num; i++) + { + QModelIndex sIdx=varModel_->index(i,0,p); + QModelIndex idx=mapFromSource(sIdx); + if(!idx.isValid()) + { + UiLog().dbg() << " idx=" << idx; + Q_ASSERT(sIdx.isValid()); + } + if(idx.data().toString() != sIdx.data().toString()) + { + UI_ASSERT(0,"filter=" << idx.data().toString() << + " source=" << sIdx.data().toString()); + } + testSource(idx); + } +} +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelData.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelData.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelData.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelData.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,982 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VariableModelData.hpp" + +#include "CommandHandler.hpp" +#include "UserMessage.hpp" +#include "UiLog.hpp" +#include "UIDebug.hpp" +#include "VariableModelDataObserver.hpp" +#include "VAttribute.hpp" +#include "VGenVarAttr.hpp" +#include "VItemPathParser.hpp" +#include "VNode.hpp" +#include "VNState.hpp" + +#include + +static std::string defaultStr(""); + +#define _UI_VARIABLEMODELDATA_DEBUG + +//========================================== +// +// VariableModelData +// +//========================================== + +VariableModelData::VariableModelData(VInfo_ptr info) : + info_(info) +{ + reload(); +} + +VariableModelData::~VariableModelData() +{ +} + +void VariableModelData::clear() +{ + vars_.clear(); + genVars_.clear(); +} + +void VariableModelData::reload() +{ + clear(); + + if(info_ && info_->node()) + { + info_->node()->variables(vars_); + info_->node()->genVariables(genVars_); + removeDuplicates(vars_,genVars_); + } +} + +//When this function called duplicates must have already been removed!! +void VariableModelData::reset(const std::vector& vars,const std::vector& genVars) +{ + clear(); + + if(info_ && info_->node()) + { + vars_=vars; + genVars_=genVars; + } +} + +//Remove the generated variables that have the same name as a user variable. User +//variables take precedence over generated variables. +void VariableModelData::removeDuplicates(const std::vector& vars,std::vector& genVars) +{ + std::vector gvOri=genVars; + genVars.clear(); + + for(std::vector::const_iterator it=gvOri.begin(); it != gvOri.end(); ++it) + { + bool hasIt=false; + for(std::vector::const_iterator itV=vars.begin(); itV != vars.end(); ++itV) + { + if((*it).name() == (*itV).name()) + { + hasIt=true; + break; + } + } + if(!hasIt) + genVars.push_back(*it); + } +} + +std::string VariableModelData::fullPath() +{ + if(info_ && info_->node()) + return info_->path(); + + return std::string(); +} + +std::string VariableModelData::name() +{ + return info_->name(); +} + +std::string VariableModelData::type() +{ + if(info_) + { + if(info_->isServer()) + return "server"; + else if(info_->node()) + return info_->node()->nodeType(); + } + + return std::string(); +} + +VNode* VariableModelData::node() const +{ + if(info_ && info_->isNode()) + return info_->node(); + + return NULL; +} + +VInfo_ptr VariableModelData::info(int index) const +{ + if(info_) + { + if(index < 0 || index >= varNum()) + return VInfo_ptr(); + + std::string p=info_->storedPath(); + if(!isGenVar(index)) + { + p=VItemPathParser::encodeAttribute(p,vars_[index].name(),"var"); + } + else + { + p=VItemPathParser::encodeAttribute(p,genVars_[index-vars_.size()].name(),"genvar"); + } + + return VInfo::createFromPath(p); + } + + return VInfo_ptr(); +} + +const std::string& VariableModelData::name(int index) const +{ + if(index < 0 || index >= varNum()) + return defaultStr; + + if(!isGenVar(index)) + { + return vars_.at(index).name(); + } + else + { + return genVars_.at(index-vars_.size()).name(); + } + + return defaultStr; +} + +const std::string& VariableModelData::value(int index) const +{ + if(index < 0 || index >= varNum()) + return defaultStr; + + if(!isGenVar(index)) + { + return vars_.at(index).theValue(); + } + else + { + return genVars_.at(index-vars_.size()).theValue(); + } + + return defaultStr; +} + +const std::string& VariableModelData::value(const std::string n,bool& hasIt) const +{ + hasIt=false; + if(n.empty()) + return defaultStr; + + for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) + { + if((*it).name() == n) + { + hasIt=true; + return (*it).theValue(); + } + } + for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) + { + if((*it).name() == n) + { + hasIt=true; + return (*it).theValue(); + } + } + + return defaultStr; +} + +bool VariableModelData::hasName(const std::string& n) const +{ + for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) + { + if((*it).name() == n) + { + return true; + } + } + + for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) + { + if((*it).name() == n) + { + return true; + } + } + + return false; + +} + +int VariableModelData::indexOf(const std::string& varName,bool genVar) const +{ + int idx=-1; + for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) + { + idx++; + if(!genVar && (*it).name() == varName) + { + return idx; + } + } + + if(!genVar) + return -1; + + for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) + { + idx++; + if((*it).name() == varName) + { + return idx; + } + } + + return -1; +} + +#if 0 +void VariableModelData::buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& name,const std::string& value) +{ + cmd.push_back("ecflow_client"); + cmd.push_back("--alter"); + cmd.push_back(action); + cmd.push_back(type); + + if(!name.empty()) + { + cmd.push_back(name); + cmd.push_back(value); + } + + cmd.push_back(""); + +} +#endif + +void VariableModelData::setValue(int index,const std::string& val) +{ + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"change","variable",name(index),val); + + CommandHandler::run(info_,cmd); +} + +void VariableModelData::alter(const std::string& name,const std::string& val) +{ + std::string mode="add"; + + //Existing name + if(hasName(name)) + { + //Generated variables cannot be changed. We instead add user variable with the same + //name. It will shadow the original gen variable. + if(isGenVar(name)) + { + mode="add"; + } + else + { + mode="change"; + } + } + + std::vector cmd; + VAttribute::buildAlterCommand(cmd,mode,"variable",name,val); + CommandHandler::run(info_,cmd); +} + + +void VariableModelData::add(const std::string& name,const std::string& val) +{ + std::vector cmd; + VAttribute::buildAlterCommand(cmd,(hasName(name))?"change":"add","variable",name,val); + CommandHandler::run(info_,cmd); +} + +void VariableModelData::remove(const std::string& varName) +{ + std::vector cmd; + VAttribute::buildAlterCommand(cmd,"delete","variable",varName,""); + CommandHandler::run(info_,cmd); +} + +bool VariableModelData::isGenVar(int index) const +{ + return (index >= static_cast(vars_.size())); +} + +bool VariableModelData::isGenVar(const std::string& n) const +{ + for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) + { + if((*it).name() == n) + { + return true; + } + } + return false; +} + +bool VariableModelData::isReadOnly(int index) const +{ + return isReadOnly(name(index)); +} + +bool VariableModelData::isReadOnly(const std::string& varName) const +{ + return VGenVarAttr::isReadOnly(varName); +} + +bool VariableModelData::isShadowed(int index) const +{ + return (shadowed_.find(name(index)) != shadowed_.end()); +} + +int VariableModelData::varNum() const +{ + return vars_.size() + genVars_.size(); +} + +void VariableModelData::latestVars(std::vector& v,std::vector& vg) +{ + if(info_ && info_->node()) + { + info_->node()->variables(v); + info_->node()->genVariables(vg); + removeDuplicates(v,vg); + } +} + +bool VariableModelData::updateShadowed(std::set& names) +{ + std::set ori=shadowed_; + shadowed_.clear(); + bool changed=false; + +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " ori shadowed:"; + for(std::set::const_iterator it=ori.begin(); it != ori.end(); ++it) + { + UiLog().dbg() << " " << *it; + } +#endif + + for(std::set::const_iterator it = names.begin(); it != names.end(); ++it) + { + if(hasName(*it)) + { + shadowed_.insert(*it); + if(ori.find(*it) == ori.end()) + changed=true; + } + } + + for(std::size_t i=0; i < vars_.size(); i++) + { + names.insert(vars_[i].name()); + } + for(std::size_t i=0; i < genVars_.size(); i++) + { + names.insert(genVars_[i].name()); + } + +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " shadowed:"; + for(std::set::const_iterator it=shadowed_.begin(); it != shadowed_.end(); ++it) + { + UiLog().dbg() << " " << *it; + } + + UiLog().dbg() << " changed: " << changed; +#endif + + return changed; +} + +bool VariableModelData::checkUpdateNames(const std::vector& v,const std::vector& vg) +{ + for(unsigned int i=0; i < vars_.size(); i++) + { + if(vars_[i].name() != v[i].name()) + { + return true; + } + } + + for(unsigned int i=0; i < genVars_.size(); i++) + { + if(genVars_[i].name() != vg[i].name()) + { + return true; + } + } + + return false; +} + +//Check if the total number of variables will change. It does not update the local data! +int VariableModelData::checkUpdateDiff(std::vector& v,std::vector& vg) +{ + if(info_ && info_->node() && v.empty() && vg.empty()) + { + //get the current set of variables from the node/server. This might be different + //to the ones we store. + latestVars(v,vg); + } + + //Return the change in the total size of variables + return v.size()+vg.size() -(vars_.size() + genVars_.size()); +} + + +//Check if any of the names or values has changed. We suppose that the number of current and new +//variables are the same but some of their names or values have been changed. +bool VariableModelData::update(const std::vector& v,const std::vector& vg) +{ +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UI_FUNCTION_LOG +#endif + +#if 0 + if(info_ && info_->node() && v.empty() && vg.empty()) + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " call latestVars"; +#endif + latestVars(v,vg); + } +#endif + +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " new list of variables:"; + for(std::size_t i=0; i < v.size(); i++) + UiLog().dbg() << " " << v[i].name() << "=" << v[i].theValue(); + UiLog().dbg() << " new list of generated variables:"; + for(std::size_t i=0; i < vg.size(); i++) + UiLog().dbg() << " " << vg[i].name() << "=" << vg[i].theValue(); +#endif + + //We must have the same number of variables + UI_ASSERT(v.size() + vg.size() == vars_.size() + genVars_.size(), + "v.size()=" << v.size() << " vg.size()=" << vg.size() << + " vars_.size()=" << vars_.size() << " genVars_.size()" << genVars_.size()); + + bool changed=false; + if(v.size() != vars_.size() || vg.size() != genVars_.size()) + { + changed=true; +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " variables size changed! var: " << vars_.size() << + " -> " << v.size() << "gen var: " << + genVars_.size() << " -> " << vg.size(); +#endif + } + else + { + UI_ASSERT(v.size() == vars_.size(), + "v.size()=" << v.size() << " vars_.size()=" << vars_.size()); + + UI_ASSERT(vg.size() == genVars_.size(), + "vg.size()=" << vg.size() << " genVars_.size()=" << genVars_.size()); + + for(std::size_t i=0; i < vars_.size(); i++) + { + if(vars_[i].name() != v[i].name() || vars_[i].theValue() != v[i].theValue()) + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " variable changed! name: " << vars_[i].name() << " -> " << + v[i].name() << " value: " << vars_[i].theValue() << " -> " << v[i].theValue(); +#endif + changed=true; + break; + } + } + + if(changed == false) + { + for(std::size_t i=0; i < genVars_.size(); i++) + { + if(genVars_[i].name() != vg[i].name() || genVars_[i].theValue() != vg[i].theValue()) + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " generated variable changed! name: " << genVars_[i].name() << " -> " << + vg[i].name() << " value: " << genVars_[i].theValue() << " -> " << vg[i].theValue(); +#endif + changed=true; + break; + } + } + } + } + + if(changed) + { + vars_=v; + genVars_=vg; +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " updated vars and genvars"; +#endif + } + + return changed; +} + +//========================================== +// +// VariableModelDataHandler +// +//========================================== + +VariableModelDataHandler::VariableModelDataHandler() : server_(0) +{ +} + +VariableModelDataHandler::~VariableModelDataHandler() +{ + clear(); +} + +void VariableModelDataHandler::reload(VInfo_ptr info) +{ + //Notifies the model that a change will happen + Q_EMIT reloadBegin(); + + clear(false); + + if(info && info->node()) + { + server_=info->server(); + + std::vector nodes=info->node()->ancestors(VNode::ChildToParentSort); + + for(std::vector::iterator it=nodes.begin(); it != nodes.end(); ++it) + { + VNode* n=*it; + + if(n->isServer()) + { + VInfo_ptr ptr=VInfoServer::create(n->server()); + data_.push_back(new VariableModelData(ptr)); + } + else + { + VInfo_ptr ptr=VInfoNode::create(n); + data_.push_back(new VariableModelData(ptr)); + } + } + + updateShadowed(); + } + + Q_EMIT reloadEnd(); +} + +#if 0 +void VariableModelDataHandler::reload() +{ + //Notifies the model that a change will happen + Q_EMIT reloadBegin(); + + for(std::vector::iterator it=data_.begin(); it != data_.end(); ++it) + { + (*it)->reload(); + } + + updateShadowed(); + + Q_EMIT reloadEnd(); +} +#endif + +bool VariableModelDataHandler::updateShadowed() +{ +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UI_FUNCTION_LOG +#endif + + bool shadowChanged=false; + + names_.clear(); + + if(data_.size()== 0) + return shadowChanged; + + //There are no shadowed vars in the first node + std::size_t num=data_[0]->varNum(); + for(std::size_t i=0; i < num; i++) + { + names_.insert(data_[0]->name(i)); + } + + std::size_t dataNum=data_.size(); + for(std::size_t i=1; i < dataNum; i++) + { + if(data_[i]->updateShadowed(names_)) + shadowChanged=true; + } + +//#ifdef _UI_VARIABLEMODELDATA_DEBUG +#if 0 + UiLog().dbg() << " names:"; + for(std::set::const_iterator it=names_.begin(); it != names_.end(); ++it) + { + UiLog().dbg() << " " + *it; + } +#endif + + return shadowChanged; +} + +void VariableModelDataHandler::clear(bool emitSignal) +{ + if(emitSignal) + Q_EMIT reloadBegin(); + + for(std::vector::iterator it=data_.begin(); it != data_.end(); ++it) + { + delete *it; + } + + server_=0; + data_.clear(); + names_.clear(); + + broadcastClear(); + + if(emitSignal) + Q_EMIT reloadEnd(); +} + +int VariableModelDataHandler::varNum(int index) const +{ + if(index >=0 && index < static_cast(data_.size())) + return data_.at(index)->varNum(); + + return -1; +} + +VariableModelData* VariableModelDataHandler::data(int index) const +{ + if(index >=0 && index < static_cast(data_.size())) + return data_.at(index); + + return 0; +} + +//It is called when a node changed. +bool VariableModelDataHandler::nodeChanged(const VNode* node, const std::vector& aspect) +{ +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UI_FUNCTION_LOG +#endif + int dataIndex=-1; + for(std::size_t i=0; i < data_.size(); i++) + { + if(data_[i]->node() == node) + { + dataIndex=i; + break; + } + } + +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " dataIndex=" << dataIndex; +#endif + Q_ASSERT(dataIndex != -1); + + //Update the given block + bool retVal=updateVariables(dataIndex); + + Q_ASSERT(data_[dataIndex]->node()); + + //Always update the suite as well!! + if(!data_[dataIndex]->node()->isSuite()) + { + for(std::size_t i=dataIndex+1; i < data_.size(); i++) + { + if(data_[i]->node()->isSuite()) + { + if(updateVariables(i)) + retVal=true; + + break; + } + } + } + + if(retVal) + broadcastUpdate(); + + return retVal; +} + +//It is called when the server defs was changed +bool VariableModelDataHandler::defsChanged(const std::vector& aspect) +{ +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UI_FUNCTION_LOG +#endif + + if(data_.size() == 0) + { + return false; + } + + int dataIndex=data_.size()-1; + Q_ASSERT(dataIndex >=0 && dataIndex < static_cast(data_.size())); + VariableModelData* d=data_.at(data_.size()-1); + Q_ASSERT(d); + Q_ASSERT(d->type() == "server"); + + bool retVal=updateVariables(dataIndex); + + if(retVal) + broadcastUpdate(); + + return retVal; +} + +bool VariableModelDataHandler::updateVariables(int dataIndex) +{ +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UI_FUNCTION_LOG + UiLog().dbg() << " dataIndex=" << dataIndex; +#endif + + bool retVal=false; + + //There is no notification about generated variables. Basically they can + //change at any update!! So we have to check all the variables at every update!! + std::vector v; + std::vector vg; + + //Get the current set of variables and check if the total number of variables + //has changed. At this point v and vg do not contain any duplicates (within the + //same block the user variables take precedence over the generated variables) + int cntDiff=data_[dataIndex]->checkUpdateDiff(v,vg); + + //If the number of the variables is not the same that we store + //we reset the given block in the model + if(cntDiff != 0) + { + const int numOld=data_[dataIndex]->varNum(); //the current num in the model + const int numNew=v.size()+vg.size(); //the new num + +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " cntDiff=" << cntDiff << " numOld=" << numOld << + " numNew=" << numNew; +#endif + + //Clear the block's contents in the model + Q_EMIT clearBegin(dataIndex,numOld); + data_[dataIndex]->clear(); + Q_EMIT clearEnd(dataIndex,numOld); + + //Load the new data for the block in the model + Q_EMIT loadBegin(dataIndex,numNew); + data_[dataIndex]->reset(v,vg); + Q_EMIT loadEnd(dataIndex,numNew); + + Q_ASSERT(data_[dataIndex]->varNum() == numNew); + + //At this point the block is filtered and sorted!!!! + + //Check if the shadowed list of variables changed + if(updateShadowed()) + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " emit rerunFilter"; +#endif + //We need to rerun the filter!! + Q_EMIT rerunFilter(); + } +#if 0 + else + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " emit dataChanged"; +#endif + //Update the data item in the model + //Q_EMIT dataChanged(dataIndex); + } +#endif + + retVal=true; + } + //Check if some variables' name or value changed + else + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " cntDiff=" << cntDiff; +#endif + Q_ASSERT(cntDiff==0); + //At this point we must have the same number of vars + const int numNew=v.size()+vg.size(); + Q_ASSERT(data_[dataIndex]->varNum() == numNew); + + //Find out if any names changed + bool nameChanged=data_[dataIndex]->checkUpdateNames(v,vg); + + //Update the names/values + if(data_[dataIndex]->update(v,vg)) + { +#ifdef _UI_VARIABLEMODELDATA_DEBUG + UiLog().dbg() << " Variable name or value changed"; +#endif + //At this point the stored variables are already updated + if(nameChanged) + { + if(updateShadowed()) + { + Q_EMIT rerunFilter(); + } + else + { + //Update the data item in the model + Q_EMIT dataChanged(dataIndex); + } + } + else + { + //Update the data item in the model + Q_EMIT dataChanged(dataIndex); + } + } + retVal=true; + } + + return retVal; +} + +const std::string& VariableModelDataHandler::value(const std::string& node,const std::string& name, bool& hasIt) const +{ + hasIt=false; + for(unsigned int i=0; i < data_.size(); i++) + { + if(data_.at(i)->name() == node) + { + return data_.at(i)->value(name,hasIt); + } + } + + return defaultStr; +} + +void VariableModelDataHandler::findVariable(const std::string& name,const std::string& nodePath, + bool genVar,int& block,int& row) const +{ + block=-1; + row=-1; + for(size_t i=0; i < data_.size(); i++) + { + if(data_[i]->fullPath() == nodePath) + { + block=i; + row=data_[i]->indexOf(name,genVar); + return; + } + } +} + +void VariableModelDataHandler::findVariable(VInfo_ptr info,int& block,int& row) const +{ + block=-1; + row=-1; + + findBlock(info,block); + if(block!= -1 && info && info->isAttribute()) + { + if(VAttribute *a=info->attribute()) + { + std::string name=a->strName(); + std::string tName=a->typeName(); + if(!name.empty() && + (tName=="var" || tName == "genvar") ) + { + row=data_[block]->indexOf(name,(tName == "genvar")); + } + } + } +} + +void VariableModelDataHandler::findBlock(VInfo_ptr info,int& block) const +{ + block=-1; + + if(!info) + return; + + std::string p=info->nodePath(); + if(!p.empty()) + { + int n=count(); + for(int i=0; i < n; i++) + { + if(data_[i]->info_ && data_[i]->info_->nodePath() == p) + { + block=i; + return; + } + } + } +} + +void VariableModelDataHandler::addObserver(VariableModelDataObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + observers_.push_back(o); +} + +void VariableModelDataHandler::removeObserver(VariableModelDataObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + observers_.erase(it); +} + +void VariableModelDataHandler::broadcastClear() +{ + std::vector obsCopy=observers_; + for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) + { + (*it)->notifyCleared(this); + } +} + +void VariableModelDataHandler::broadcastUpdate() +{ + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + { + (*it)->notifyUpdated(this); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelData.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelData.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelData.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelData.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,134 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VARIABLEMODELDATA_H +#define VARIABLEMODELDATA_H + +#include +#include + +#include +#include + +#include "NodeObserver.hpp" +#include "VInfo.hpp" + +class Node; +class ServerHandler; +class VariableModelDataHandler; +class VariableModelDataObserver; + +class VariableModelData +{ + friend class VariableModelDataHandler; + +public: + explicit VariableModelData(VInfo_ptr info); + virtual ~VariableModelData(); + + std::string fullPath(); + std::string name(); + std::string type(); + const std::string& name(int index) const; + const std::string& value(int index) const; + const std::string& value(const std::string name,bool&) const; + VInfo_ptr info() const {return info_;} + VInfo_ptr info(int index) const; + int indexOf(const std::string& varName,bool genVar) const; + bool isGenVar(const std::string& varName) const; + bool isGenVar(int index) const; + bool isReadOnly(int index) const; + bool isReadOnly(const std::string& varName) const; + bool isShadowed(int index) const; + int varNum() const; + bool hasName(const std::string& n) const; + VNode* node() const; + +#if 0 + void buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& name,const std::string& value); +#endif + void clear(); + void setValue(int index,const std::string& val); + void alter(const std::string& name,const std::string& val); + void add(const std::string& name,const std::string& val); + void remove(const std::string& val); + + //const std::vector& vars() const {return vars_;} + //const std::vector& genVars() const {return genVars_;} + +protected: + void reload(); + void removeDuplicates(const std::vector& vars,std::vector& genVars); + void latestVars(std::vector& v,std::vector& gv); + int checkUpdateDiff(std::vector& v,std::vector& gv); + bool checkUpdateNames(const std::vector& v,const std::vector& vg); + void reset(const std::vector& v,const std::vector& gv); + bool update(const std::vector& v,const std::vector& gv); + bool updateShadowed(std::set& names); + + std::vector vars_; + std::vector genVars_; + std::set shadowed_; + VInfo_ptr info_; +}; + +class VariableModelDataHandler : public QObject +{ +Q_OBJECT + +public: + VariableModelDataHandler(); + ~VariableModelDataHandler(); + + void reload(VInfo_ptr info); + void clear(bool emitSignal=true); + int count() const {return static_cast(data_.size());} + int varNum(int index) const; + VariableModelData* data(int index) const; + void findVariable(const std::string& name,const std::string& nodePath, + bool genVar,int& block,int& row) const; + + void findVariable(VInfo_ptr info,int& block,int& row) const; + void findBlock(VInfo_ptr info,int& block) const; + + bool nodeChanged(const VNode* node, const std::vector&); + bool defsChanged(const std::vector&); + const std::string& value(const std::string& node,const std::string& name,bool&) const; + void addObserver(VariableModelDataObserver*); + void removeObserver(VariableModelDataObserver*); + + +Q_SIGNALS: + void reloadBegin(); + void reloadEnd(); + void clearBegin(int,int); + void clearEnd(int,int); + void loadBegin(int,int); + void loadEnd(int,int); + void addRemoveBegin(int,int); + void addRemoveEnd(int); + void dataChanged(int); + void rerunFilter(); + +protected: + //void reload(); + bool updateVariables(int); + bool updateShadowed(); + void broadcastClear(); + void broadcastUpdate(); + + std::vector data_; + ServerHandler* server_; + std::set names_; + std::vector observers_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelDataObserver.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelDataObserver.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableModelDataObserver.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableModelDataObserver.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,23 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VARIABLEMODELDATAOBSERVER_HPP +#define VARIABLEMODELDATAOBSERVER_HPP + +class VariableModelDataHandler; + +class VariableModelDataObserver +{ +public: + VariableModelDataObserver() {} + virtual void notifyCleared(VariableModelDataHandler*)=0; + virtual void notifyUpdated(VariableModelDataHandler*)=0; +}; + +#endif // VARIABLEMODELDATAOBSERVER_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableModel.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableModel.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableModel.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableModel.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,130 @@ +#ifndef VARIABLEMODEL_H +#define VARIABLEMODEL_H + +#include +#include + +#include + +#include "NodeObserver.hpp" +#include "VInfo.hpp" + +class Node; +class VariableModelData; +class VariableModelDataHandler; +class VariableSortModel; + +class VariableModel : public QAbstractItemModel +{ +Q_OBJECT + +friend class VariableSortModel; + +public: + VariableModel(VariableModelDataHandler* data,QObject *parent=0); + + enum CustomItemRole {ReadOnlyRole = Qt::UserRole+1,GenVarRole = Qt::UserRole+2,ShadowRole = Qt::UserRole+3}; + + int columnCount (const QModelIndex& parent = QModelIndex() ) const; + int rowCount (const QModelIndex& parent = QModelIndex() ) const; + + Qt::ItemFlags flags ( const QModelIndex & index) const; + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; + + QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; + QModelIndex parent (const QModelIndex & ) const; + + bool variable(const QModelIndex& index, QString& name,QString& value,bool& genVar) const; + + VariableModelData* indexToData(const QModelIndex& index) const; + VariableModelData* indexToData(const QModelIndex& index,int& block) const; + VInfo_ptr indexToInfo(const QModelIndex& index) const; + QModelIndex infoToIndex(VInfo_ptr info) const; + + bool isVariable(const QModelIndex & index) const; + +public Q_SLOTS: + void slotReloadBegin(); + void slotReloadEnd(); + void slotClearBegin(int block,int num); + void slotClearEnd(int block,int num); + void slotLoadBegin(int block,int num); + void slotLoadEnd(int block,int num); + void slotDataChanged(int); + +Q_SIGNALS: + void filterChanged(); + void rerunFilter(); + +protected: + bool hasData() const; + + int indexToLevel(const QModelIndex&) const; + void identify(const QModelIndex& index,int& parent,int& row) const; + + VariableModelDataHandler* data_; + static QColor varCol_; + static QColor genVarCol_; + static QColor shadowCol_; + static QColor blockBgCol_; + static QColor blockFgCol_; +}; + + +//Filter and sorts the variables + +class VariableSortModel : public QSortFilterProxyModel +{ + Q_OBJECT + +public: + enum MatchMode {FilterMode,SearchMode}; + + VariableSortModel(VariableModel*,QObject *parent=0); + ~VariableSortModel() {} + + MatchMode matchMode() const {return matchMode_;} + void setMatchMode(MatchMode mode); + void setMatchText(QString text); + + bool lessThan(const QModelIndex &left, const QModelIndex &right) const; + bool filterAcceptsRow(int,const QModelIndex &) const; + + //From QSortFilterProxyModel: + //we set the source model in the constructor. So this function should not do anything. + void setSourceModel(QAbstractItemModel*) {} + QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; + + QModelIndexList match(const QModelIndex& start,int role,const QVariant& value,int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags( Qt::MatchStartsWith | Qt::MatchWrap )) const; + +#if 0 + void test(); + void test(const QModelIndex& p); + void testSource(const QModelIndex& p); +#endif + +public Q_SLOTS: + void slotShowShadowed(bool); + +protected Q_SLOTS: + void slotFilterChanged(); + void slotRerunFilter(); + +protected: + void match(QString text); + void print(const QModelIndex idx); + + VariableModel* varModel_; + bool showShadowed_; + + MatchMode matchMode_; + mutable QString matchText_; + mutable QModelIndexList matchLst_; + + QMap nameCnt_; + bool ignoreDuplicateNames_; //Ignore duplicate names across ancestors +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariablePropDialog.ui ecflow-4.11.1/Viewer/ecflowUI/src/VariablePropDialog.ui --- ecflow-4.9.0/Viewer/ecflowUI/src/VariablePropDialog.ui 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariablePropDialog.ui 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,138 @@ + + + VariablePropDialog + + + + 0 + 0 + 625 + 378 + + + + Edit variable + + + true + + + + 4 + + + 6 + + + + + + + + + + + + 0 + + + 0 + + + + + &Name: + + + nameEdit_ + + + + + + + + + + + 0 + 1 + + + + + + + + &Value: + + + valueEdit_ + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Save + + + + + + + + EditorInfoLabel + QWidget +
    EditorInfoLabel.hpp
    + 1 +
    + + MessageLabel + QWidget +
    MessageLabel.hpp
    + 1 +
    +
    + + + + buttonBox_ + accepted() + VariablePropDialog + accept() + + + 257 + 218 + + + 157 + 227 + + + + + buttonBox_ + rejected() + VariablePropDialog + reject() + + + 274 + 218 + + + 283 + 227 + + + + +
    diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableSearchLine.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableSearchLine.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableSearchLine.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,146 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include +#include +#include +#include + +#include "VariableSearchLine.hpp" + +VariableSearchLine::VariableSearchLine(QWidget *parent) : + AbstractSearchLine(parent), + view_(0), + currentResultItem_(-1) +{ + label_->hide(); + closeTb_->hide(); +} + +VariableSearchLine::~VariableSearchLine() +{ + +} + +//This should only be called once!! +void VariableSearchLine::setView(QTreeView* view) +{ + //We should disconnect from the previous view ... + view_=view; + + //We detect when the sorting changes in the view + connect(view_->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), + this,SLOT(slotSortHappened(int,Qt::SortOrder))); + + //At this point the model has to be set on the view!!! + assert(view_->model() != 0); + + //We detect when the model is reset + connect(view_->model(),SIGNAL(modelReset()), + this,SLOT(slotUpdate())); + + //We should detect when the model changes!!!! + connect(view_->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), + this,SLOT(slotUpdate(QModelIndex,QModelIndex))); +} + +void VariableSearchLine::slotFind(QString txt) +{ + if(!view_) + return; + + if(txt.simplified().isEmpty() && resultItems_.isEmpty()) + { + return; + } + + QModelIndex current=view_->currentIndex(); + + //The (sort) model redoes the match. + resultItems_=view_->model()->match(current,Qt::DisplayRole,txt,-1, + Qt::MatchStartsWith | Qt::MatchRecursive | Qt::MatchWrap); + + //The view needs to be rerendered! + view_->dataChanged(QModelIndex(),QModelIndex()); + + if(resultItems_.count() > 0) + { + selectIndex(resultItems_[0]); + currentResultItem_=0; + updateButtons(true); + } + else + { + currentResultItem_=0; + updateButtons(false); + } + +} + +void VariableSearchLine::slotFindNext() +{ + if(!view_) + return; + + + if(status_==true && resultItems_.count() > 0) + { + currentResultItem_++; + if(currentResultItem_ >= resultItems_.count()) + { + currentResultItem_=0; + } + selectIndex(resultItems_[currentResultItem_]); + } +} + +void VariableSearchLine::slotFindPrev() +{ + if(!view_) + return; + + if(status_==true && resultItems_.count() > 0) + { + currentResultItem_--; + if(currentResultItem_ <0) + { + currentResultItem_=resultItems_.count()-1; + } + selectIndex(resultItems_[currentResultItem_]); + } +} + +void VariableSearchLine::selectIndex(const QModelIndex& index) +{ + if(!view_) + return; + + view_->setCurrentIndex(index); + //emit indexSelected(index); +} + +//Called when sorting changed in the view +void VariableSearchLine::slotSortHappened(int,Qt::SortOrder) +{ + slotUpdate(); +} + +void VariableSearchLine::slotUpdate() +{ + slotFind(searchLine_->text()); +} + +void VariableSearchLine::slotUpdate(const QModelIndex&,const QModelIndex&) +{ + slotUpdate(); +} + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableSearchLine.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableSearchLine.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableSearchLine.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,47 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VARIABLESEARCHLINE_HPP_ +#define VARIABLESEARCHLINE_HPP_ + +#include "AbstractSearchLine.hpp" + +#include + +class QTreeView; + +class VariableSearchLine : public AbstractSearchLine +{ + Q_OBJECT + +public: + explicit VariableSearchLine(QWidget *parent); + ~VariableSearchLine(); + void setView(QTreeView* view); + +public Q_SLOTS: + void slotFind(QString); + void slotFindNext(); + void slotFindPrev(); + void slotFindNext(bool) { slotFindNext();} + void slotFindPrev(bool) {slotFindPrev();} + void slotSortHappened(int,Qt::SortOrder); + void slotUpdate(); + void slotUpdate(const QModelIndex&,const QModelIndex&); + +protected: + void selectIndex(const QModelIndex& index); + void clearRequested() {} + + QTreeView* view_; + QModelIndexList resultItems_; + int currentResultItem_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableView.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableView.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableView.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableView.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,323 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VariableView.hpp" + +#include +#include +#include +#include +#include +#include +#include + +#include "IconProvider.hpp" +#include "VariableModel.hpp" +#include "VParam.hpp" + +//======================================================== +// +// VariableViewDelegate +// +//======================================================== + +VariableDelegate::VariableDelegate(QTreeView *parent) : QStyledItemDelegate(parent), view_(parent) +{ + selectPen_=QPen(QColor(8,117,182)); + selectBrush_=QBrush(QColor(65,139,212)); + selectBrushBlock_=QBrush(QColor(48,102,178)); + borderPen_=QPen(QColor(230,230,230)); + genVarPixId_=IconProvider::add(":/viewer/genvar.svg","genvar"); + shadowGenVarPixId_=IconProvider::add(":/viewer/genvar_shadow.svg","genvar_shadow"); +} + +void VariableDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const +{ +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + QStyleOptionViewItem vopt(option); +#else + QStyleOptionViewItemV4 vopt(option); +#endif + + initStyleOption(&vopt, index); + + const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); + const QWidget* widget = vopt.widget; + + //This indicates that the item is a parent item (node or server), + // hence it has branch controls. + bool hasChild=!index.parent().isValid(); + + bool selected=option.state & QStyle::State_Selected; + + //Save painter state + painter->save(); + + //The background rect + QRect bgRect=option.rect; + + //For variables in the first column we want to extend the item + //rect to the left for the background painting. + if(index.column()==0 && !hasChild) + { + bgRect.setX(0); + } + + //Paint item highlight!! It is taken from the UserRole + QColor highCol=index.data(Qt::UserRole).value(); + if(highCol.isValid()) + { + painter->fillRect(bgRect.adjusted(1,1,-1,-1),highCol); + } + //otherwise paint item background!! + else if(!selected) + { + //Paint the item background + QColor bg=index.data(Qt::BackgroundRole).value(); + if(bg.isValid()) + { + painter->fillRect(bgRect,bg); + } + //alternating row colour? + else {} + } + + //Paint selection. This should be transparent. + if(selected) + { + //The selection rect + QRect selectRect; + if(hasChild) + { + selectRect=bgRect.adjusted(0,1,0,-1); + painter->fillRect(selectRect,selectBrushBlock_); + } + else + { + selectRect=bgRect.adjusted(0,1,0,-1); + + //For the first column we extend the selection + //rect to left edge. + if(index.column()==0) + { + selectRect.setX(0); + } + painter->fillRect(selectRect,selectBrush_); + + } + } + + //Render the horizontal border for rows. We only render the top border line. + //With this technique we miss the bottom border line of the last row!!! + painter->setPen(borderPen_); + painter->drawLine(bgRect.topLeft(),bgRect.topRight()); + + if(index.column() == 0 && !hasChild) + { + painter->drawLine(bgRect.topRight(),bgRect.bottomRight()); + } + + //Display text + QString text=index.data(Qt::DisplayRole).toString(); + QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); + QFont f; + QFontMetrics fm(f); + textRect.setWidth(fm.width(text)); + + if(index.column() == 0 && !hasChild) + { + bool hasLock=false; + QPixmap lockPix; + QRect lockRect; + bool hasGen=false; + QPixmap genPix; + QRect genRect; + + textRect.setLeft(option.rect.x()-17); + bool locked=index.data(VariableModel::ReadOnlyRole).toBool(); + if(locked) + { + hasLock=true; + lockPix=IconProvider::lockPixmap(textRect.height()-6); + + lockRect=QRect(textRect.left()-4-lockPix.width(), + textRect.top()+(textRect.height()-lockPix.height())/2, + lockPix.width(),lockPix.height()); + } + + bool gen=index.data(VariableModel::GenVarRole).toBool(); + if(gen && genVarPixId_ >= 0) + { + int pixId=genVarPixId_; + if(index.data(VariableModel::ShadowRole).toBool()) + { + pixId=shadowGenVarPixId_; + } + if(pixId >=0) + { + hasGen=true; + + genPix=IconProvider::pixmap(pixId,textRect.height()-4); + genRect=QRect(textRect.left(), + textRect.top()+(textRect.height()-genPix.height())/2, + genPix.width(), genPix.height()); + + textRect.moveLeft(genRect.right()+4); + } + } + + if(textRect.right()+1 > option.rect.right()) + { + painter->setClipRect(option.rect.adjusted(-option.rect.left(),0,0,0)); + } + QColor fg; + if(option.state & QStyle::State_Selected) + { + fg=Qt::white; + } + else + { + fg=index.data(Qt::ForegroundRole).value(); + if(!fg.isValid()) + fg=Qt::black; + } + + painter->setPen(fg); + + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + if(hasLock) + painter->drawPixmap(lockRect,lockPix); + + if(hasGen) + painter->drawPixmap(genRect,genPix); + + } + else if(index.column() == 0) + { + QColor fg=index.data(Qt::ForegroundRole).value(); + if(!fg.isValid()) + fg=Qt::black; + painter->setPen(fg); + + QRegExp rx("^(.+)\\s(\\S+)$"); + //QRegExp rx("inherited from (\\S+) (\\S+)"); + if(rx.indexIn(text) > -1 && rx.captureCount() == 2) + { + QFont f; + f.setPointSize(f.pointSize()-1); + QFontMetrics fm(f); + QString txt1=rx.cap(1); + textRect.setWidth(fm.width(txt1)); + painter->setFont(f); + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,txt1); + textRect.setLeft(textRect.right()+fm.width("D")); + text=rx.cap(2); + } + + QFont fBold; + fBold.setPointSize(fBold.pointSize()-1); + fBold.setBold(true); + QFontMetrics fmBold(fBold); + textRect.setWidth(fmBold.width(text + "a")); + painter->setFont(fBold); + + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + + } + else if(index.column() == 1) + { + textRect.adjust(2,0,2,0); + + QColor fg; + if(selected) + { + fg=Qt::white; + } + else + { + fg=index.data(Qt::ForegroundRole).value(); + if(!fg.isValid()) + fg=Qt::black; + } + + painter->setPen(fg); + painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); + } + + //Restore painter state + painter->restore(); +} + +QSize VariableDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const +{ + QSize size=QStyledItemDelegate::sizeHint(option,index); + + size+=QSize(0,2); + + //We need to starcth the second column to the right edge of the viewport + if(index.column() == 1) + { + int w1=view_->header()->sectionSize(0); + int w=view_->viewport()->size().width(); + if(w1+size.width() < w) + size.setWidth(w-w1+1); + } + return size; +} + +//======================================================== +// +// VariableView +// +//======================================================== + +VariableView::VariableView(QWidget* parent) : TreeView(parent) +{ + setProperty("var","1"); + + delegate_=new VariableDelegate(this); + setItemDelegate(delegate_); + + setRootIsDecorated(true); + setAllColumnsShowFocus(true); + setUniformRowHeights(true); + //setAlternatingRowColors(true); + setSortingEnabled(true); + + header()->setSortIndicator(0,Qt::AscendingOrder); + + //Context menu + setContextMenuPolicy(Qt::ActionsContextMenu); +} + +//We do not want to render the branches for the variable items. We only +//render the branch for the group items (nodes or server). +void VariableView::drawBranches(QPainter* painter,const QRect& rect,const QModelIndex& index ) const +{ + if(!index.parent().isValid() && index.column()==0) + { + //We need to fill the branch area here. We cannot do it in the delegate + //because when the delegate is called the branch control is already + //rendered, so the delegate would just overpaint it!!! + + if(selectionModel()->rowIntersectsSelection(index.row(),QModelIndex())) + { + painter->fillRect(rect.adjusted(0,1,0,-1),delegate_->selectBrushBlock_); + } + else + { + painter->fillRect(rect.adjusted(0,1,0,0),index.data(Qt::BackgroundRole).value()); + } + + //Draw the branch with the default method + QTreeView::drawBranches(painter,rect,index); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VariableView.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VariableView.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VariableView.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VariableView.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,58 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VARIABLEVIEW_HPP_ +#define VARIABLEVIEW_HPP_ + + +#include +#include +#include +#include + +#include "TreeView.hpp" + +class VariableView; + +class VariableDelegate : public QStyledItemDelegate +{ + friend class VariableView; + +public: + explicit VariableDelegate(QTreeView *parent); + void paint(QPainter *painter,const QStyleOptionViewItem &option, + const QModelIndex& index) const; + QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; + +protected: + QPen selectPen_; + QBrush selectBrush_; + QBrush selectBrushBlock_; + QPen borderPen_; + QPixmap lockPix_; + int genVarPixId_; + int shadowGenVarPixId_; + QTreeView* view_; + +}; + +class VariableView : public TreeView +{ +public: + explicit VariableView(QWidget *parent=0); + +protected: + void drawBranches(QPainter* painter,const QRect& rect,const QModelIndex& index ) const; + + VariableDelegate *delegate_; +}; + + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VAttribute.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VAttribute.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VAttribute.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VAttribute.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,158 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VAttribute.hpp" +#include "VAttributeType.hpp" + +#include "VNode.hpp" +#include "UIDebug.hpp" + +#include + +//#define _UI_VATTRIBUTE_DEBUG + + +VAttribute::VAttribute(VNode *parent,int index) : + VItem(parent), + index_(index) +{ +} + +VAttribute::~VAttribute() +{ +} + +ServerHandler* VAttribute::server() const +{ + return (parent_)?parent_->server():NULL; +} + + +VServer* VAttribute::root() const +{ + return (parent_)?parent_->root():NULL; +} + +QString VAttribute::toolTip() const +{ + VAttributeType* t=type(); + return (t)?(t->toolTip(data())):QString(); +} + +QString VAttribute::definition() const +{ + VAttributeType* t=type(); + return (t)?(t->definition(data())):QString(); +} + +const std::string& VAttribute::typeName() const +{ + VAttributeType* t=type(); + assert(t); + static std::string e; + return (t)?(t->strName()):e; +} + +const std::string& VAttribute::subType() const +{ + static std::string e; + return e; +} + +std::string VAttribute::fullPath() const +{ + return (parent_)?(parent_->fullPath() + ":" + strName()):""; +} + +bool VAttribute::sameContents(VItem* item) const +{ + if(!item) + return false; + + if(VAttribute *a=item->isAttribute()) + { + return a->parent() == parent() && + a->type() == type() && + a->name() == name(); + } + return false; +} + +QString VAttribute::name() const +{ + return QString::fromStdString(strName()); +} + +std::string VAttribute::strName() const +{ + static std::string eStr; + return eStr; +} + +bool VAttribute::value(const std::string& key,std::string& val) const +{ + int idx=type()->searchKeyToDataIndex(key); + if(idx != -1) + { + QStringList d=data(); + val=d[idx].toStdString(); + return true; + } + return false; +} + +bool VAttribute::sameAs(QStringList d) const +{ + if(d.count() >=2) + { + VAttributeType* t=type(); + + if(t->name() == d[0]) + { + int idx=t->searchKeyToDataIndex("name"); + if(idx != -1 && idx < d.count()) + { + return name() == d[idx]; + } + } + } + return false; +} + +void VAttribute::buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& name,const std::string& value) +{ + cmd.push_back("ecflow_client"); + cmd.push_back("--alter"); + cmd.push_back(action); + cmd.push_back(type); + + if(!name.empty()) + { + cmd.push_back(name); + cmd.push_back(value); + } + + cmd.push_back(""); +} + +void VAttribute::buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& value) +{ + cmd.push_back("ecflow_client"); + cmd.push_back("--alter"); + cmd.push_back(action); + cmd.push_back(type); + cmd.push_back(value); + + cmd.push_back(""); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VAttribute.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VAttribute.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VAttribute.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VAttribute.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,63 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VATTRIBUTE_HPP +#define VATTRIBUTE_HPP + +#include "VItem.hpp" + +#include +#include +#include + +class AttributeFilter; +class VAttributeType; +class VNode; +class VAttribute; + +class VAttribute : public VItem +{ +public: + VAttribute(VNode *parent,int index); + ~VAttribute(); + + virtual VAttributeType* type() const=0; + virtual const std::string& subType() const; + virtual int lineNum() const {return 1;} + ServerHandler* server() const; + VServer* root() const; + VAttribute* isAttribute() const {return const_cast(this);} + QString toolTip() const; + QString name() const; + std::string strName() const; + const std::string& typeName() const; + std::string fullPath() const; + virtual QStringList data(bool firstLine=false) const=0; + QString definition() const; + bool sameAs(QStringList d) const; + bool sameContents(VItem*) const; + bool value(const std::string& key,std::string& val) const; + + static void buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& name,const std::string& value); + + static void buildAlterCommand(std::vector& cmd, + const std::string& action, const std::string& type, + const std::string& value); + + +protected: + + int index_; +}; + +#endif // VATTRIBUTE_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VAttributeType.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VAttributeType.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VAttributeType.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VAttributeType.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,173 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VAttributeType.hpp" + +#include + +#include +#include +#include +#include + +#include +#include + +#include "VNode.hpp" +#include "VConfigLoader.hpp" +#include "VProperty.hpp" +#include "VFilter.hpp" +#include "VAttribute.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" +#include "UIDebug.hpp" + +std::map VAttributeType::typesMap_; +std::vector VAttributeType::types_; + +//#define _UI_ATTR_DEBUG + +VAttributeType::VAttributeType(const std::string& name) : + VParam(name), + dataCount_(0), + typeId_(types_.size()) +{ + typesMap_[name]=this; + types_.push_back(this); +} + +std::vector VAttributeType::filterItems() +{ + std::vector v; + for(TypeIterator it=types_.begin(); it != types_.end(); ++it) + v.push_back(*it); + + return v; +} + +VAttributeType* VAttributeType::find(const std::string& name) +{ + std::map::const_iterator it=typesMap_.find(name); + if(it != typesMap_.end()) + return it->second; + + return 0; +} + +VAttributeType* VAttributeType::find(int id) +{ + assert(id >=0 && id < static_cast(types_.size())); + return types_[id]; +} + +void VAttributeType::scan(VNode* vnode,std::vector& v) +{ + for(TypeIterator it=types_.begin(); it != types_.end(); ++it) + { + (*it)->scanProc()(vnode,v); + } +} + +int VAttributeType::keyToDataIndex(const std::string& key) const +{ + std::map::const_iterator it=keyToData_.find(key); + if(it != keyToData_.end()) + return it->second; + + return -1; +} + +int VAttributeType::searchKeyToDataIndex(const std::string& key) const +{ + std::map::const_iterator it=searchKeyToData_.find(key); + if(it != searchKeyToData_.end()) + return it->second; + + return -1; +} + +QStringList VAttributeType::searchKeys() const +{ + QStringList lst; + for(std::map::const_iterator it=searchKeyToData_.begin(); it != searchKeyToData_.end(); it++) + { + lst << QString::fromStdString(it->first); + } + return lst; +} + +//Load the attributes parameter file +void VAttributeType::load(VProperty* group) +{ + //We set some extra information on each type and also + //try to reorder the types according to the order defined in the + //parameter file. This order is very important: + // -it defines the rendering order + // -defines the order of the attribute items in the attribute filter + std::vector v; + Q_FOREACH(VProperty* p,group->children()) + { + if(VAttributeType* obj=VAttributeType::find(p->strName())) + { + obj->setProperty(p); + v.push_back(obj); + } + else + { + UserMessage::message(UserMessage::ERROR,true, + "Unknown attribute type=" + p->strName() + " is loaded from parameter file!"); + exit(1); + //UI_ASSERT(0,"Unknown attribute type is read from parameter file: " << p->strName()); + } + } + + //UI_ASSERT(v.size() == types_.size(),"types size=" << types_.size() << "loaded size=" << v.size()); + + if(v.size() == types_.size()) + types_=v; + else + { + UserMessage::message(UserMessage::ERROR,true, + "The number attributes loaded from parameter file do not match expected number! loaded=" + + UserMessage::toString(v.size()) + " expected=" + UserMessage::toString(types_.size())); + exit(1); + } +} + + +static SimpleLoader loader("attribute"); + +//Initialise attribute types + +#include "VLabelAttr.hpp" +#include "VMeterAttr.hpp" +#include "VEventAttr.hpp" +#include "VLimitAttr.hpp" +#include "VLimiterAttr.hpp" +#include "VRepeatAttr.hpp" +#include "VTriggerAttr.hpp" +#include "VDateAttr.hpp" +#include "VTimeAttr.hpp" +#include "VLateAttr.hpp" +#include "VGenVarAttr.hpp" +#include "VUserVarAttr.hpp" + +static VLabelAttrType labelAttrType; +static VMeterAttrType meterAttType; +static VEventAttrType eventAttrType; +static VLimitAttrType limitAttrType; +static VLimiterAttrType limiterAttrType; +static VRepeatAttrType repeatAttrType; +static VTriggerAttrType triggerAttrType; +static VDateAttrType dateAttrType; +static VTimeAttrType timeAttrType; +static VLateAttrType lateAttrType; +static VGenVarAttrType genvarAttrType; +static VUserVarAttrType uservarAttrType; diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VAttributeType.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VAttributeType.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VAttributeType.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VAttributeType.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,62 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VATTRIBUTETYPE_HPP_ +#define VATTRIBUTETYPE_HPP_ + +#include +#include +#include + +#include "VParam.hpp" + +class AttributeFilter; +class VNode; +class VAttribute; + +class VAttributeType : public VParam +{ +public: + virtual ~VAttributeType() {} + + static std::vector filterItems(); + static VAttributeType* find(const std::string& name); + static VAttributeType* find(int id); + static const std::vector& types() {return types_;} + int typeId() const {return typeId_;} + int keyToDataIndex(const std::string& key) const; + int searchKeyToDataIndex(const std::string& key) const; + QStringList searchKeys() const; + virtual QString toolTip(QStringList d) const {return QString();} + virtual QString definition(QStringList d) const {return QString();} + + static void scan(VNode* vnode,std::vector& v); + typedef void (*ScanProc) (VNode* vnode,std::vector& vec); + ScanProc scanProc() {return scanProc_;} + + //Called from VConfigLoader + static void load(VProperty*); + +protected: + explicit VAttributeType(const std::string& name); + + typedef std::vector::const_iterator TypeIterator; + std::map keyToData_; + std::map searchKeyToData_; + int dataCount_; + int typeId_; + ScanProc scanProc_; + +private: + static std::map typesMap_; + static std::vector types_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VConfig.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VConfig.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VConfig.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VConfig.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,658 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VConfig.hpp" +#include "VConfigLoader.hpp" +#include "VProperty.hpp" +#include "VSettings.hpp" + +#include "DirectoryHandler.hpp" +#include "SessionHandler.hpp" +#include "UiLog.hpp" +#include "UserMessage.hpp" + +#include "Version.hpp" + +#include + +#include +#include +#include +#include + +VConfig* VConfig::instance_=0; + +//#define _UI_CONFIG_LOAD_DEBUG + +VConfig::VConfig() +{ + appName_="ecFlowUI"; + appLongName_=appName_ + " (" + ecf::Version::raw() + ")"; +} + +VConfig::~VConfig() +{ + for(std::vector::iterator it=groups_.begin(); it != groups_.end(); ++it) + { + delete *it; + } + groups_.clear(); +} + + +VConfig* VConfig::instance() +{ + if(!instance_) + instance_=new VConfig(); + + return instance_; +} + +void VConfig::init(const std::string& parDirPath) +{ + namespace fs=boost::filesystem; + + fs::path parDir(parDirPath); + + if(fs::exists(parDir) && fs::is_directory(parDir)) + { + //fs::directory_iterator it(parDir); + + //The conf files have to be loaded in alphabetical order!! At least NotifyChange require it! + //So we read the paths into a vector and sort it. + std::vector vec; + copy(fs::directory_iterator(parDir), fs::directory_iterator(), back_inserter(vec)); + std::sort(vec.begin(), vec.end()); + + //The paths are now in alphabetical order + for(std::vector::const_iterator it=vec.begin(); it != vec.end(); ++it) + { + if(fs::is_regular_file(*it)) + { + std::string name=it->filename().string(); + if(name.find("_conf.json") != std::string::npos) + { + loadInit(it->string()); + } + } + } + } + + //Read gui definition for the editable properties + std::string guiFile=DirectoryHandler::concatenate(parDir.string(),"ecflowview_gui.json"); + loadInit(guiFile); + + //Read gui definition for the editable properties tahat can be cutomised per server + std::string guiServerFile=DirectoryHandler::concatenate(parDir.string(),"ecflowview_gui_server.json"); + loadInit(guiServerFile); + + //Load existing user settings for the editable properties + loadSettings(); + +} + +void VConfig::loadInit(const std::string& parFile) +{ + //Parse param file using the boost JSON property tree parser + using boost::property_tree::ptree; + ptree pt; + + try + { + read_json(parFile,pt); + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + std::string errorMessage = e.what(); + UserMessage::message(UserMessage::ERROR, true, + std::string("Fatal error!\nVConfig::load() unable to parse definition file: " + parFile + "\nMessage: " +errorMessage)); + exit(1); + return; + } + + //Loop over the groups + for(ptree::const_iterator itGr = pt.begin(); itGr != pt.end(); ++itGr) + { + ptree ptGr=itGr->second; + + //Get the group name and create it + std::string groupName=itGr->first; + VProperty *grProp=new VProperty(groupName); + groups_.push_back(grProp); + + UiLog().dbg() << "VConfig::loadInit() read config group: " << groupName; + + //Load the property parameters. It will recursively add all the + //children properties. + loadProperty(ptGr,grProp); + + //Add the group we created to the registered configloader + VConfigLoader::process(groupName,grProp); + } + } + +void VConfig::loadProperty(const boost::property_tree::ptree& pt,VProperty *prop) +{ + using boost::property_tree::ptree; + + ptree::const_assoc_iterator itProp; + + //Loop over the possible properties + for(ptree::const_iterator it = pt.begin(); it != pt.end(); ++it) + { + std::string name=it->first; + ptree ptProp=it->second; + +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " VConfig::loadProperty() read item: " << name; +#endif + //Default value + if(name == "default") + { + std::string val=ptProp.get_value(); + prop->setDefaultValue(val); + } + + //If it is just a key/value pair "line" + else if(name == "line" && ptProp.empty()) + { + VProperty *chProp=new VProperty(name); + prop->addChild(chProp); + std::string val=ptProp.get_value(); + + QString prefix=prop->param("prefix"); + if(!prefix.isEmpty()) + val=prefix.toStdString() + "." + val; + +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " VConfig::loadProperty() line: " << val; +#endif + if(VProperty* lineEditProp=find(val)) + { +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " --> link found"; +#endif + chProp->setLink(lineEditProp); + } + else + { +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " --> link NOT found"; +#endif + } + } + //If the property is a "line" (i.e. a line with additional parameters) + else if(prop->name() == "line" && name == "link") + { + std::string val=ptProp.get_value(); + +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " VConfig::loadProperty() line link: " << val; +#endif + if(VProperty* lineEditProp=find(val)) + { +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " --> link found"; +#endif + prop->setLink(lineEditProp); + } + else + { +#ifdef _UI_CONFIG_LOAD_DEBUG + UiLog().dbg() << " --> link NOT found"; +#endif + } + } + + //Here we only load the properties with + //children (i.e. key/value pairs (like "line" etc above) + //are ignored. + else if(!ptProp.empty()) + { + VProperty *chProp=new VProperty(name); + prop->addChild(chProp); + loadProperty(ptProp,chProp); + chProp->adjustAfterLoad(); + } + else + { + QString val=QString::fromStdString(ptProp.get_value()); + prop->setParam(QString::fromStdString(name),val); + } + } +} + +VProperty* VConfig::find(const std::string& path) +{ + VProperty* res=0; + + for(std::vector::const_iterator it=groups_.begin();it != groups_.end(); ++it) + { + VProperty *vGroup=*it; + res=vGroup->find(path); + if(res) + { + return res; + } + } + + return res; +} + +VProperty* VConfig::group(const std::string& name) +{ + for(std::vector::const_iterator it=groups_.begin();it != groups_.end(); ++it) + { + if((*it)->strName() == name) + return *it; + } + + return 0; +} + +VProperty* VConfig::cloneServerGui(VProperty *linkTarget) +{ + VProperty* gr=find("gui_server.server"); + + assert(gr); + + VProperty* cGr=gr->clone(true,false); + + std::vector chVec; + cGr->collectChildren(chVec); + for(std::vector::iterator it=chVec.begin(); it != chVec.end(); ++it) + { + VProperty *p=*it; + if(p->link()) + { + p->setLink(linkTarget->find(p->link()->path())); + } + } + + return cGr; +} + +//Saves the global settings that can be edited through the gui +void VConfig::saveSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + std::string fName=cs->settingsFile(); + + VProperty *guiProp=group("gui"); + + saveSettings(fName,guiProp,NULL,true); +} + +//Saves the settings per server that can be edited through the servers option gui +void VConfig::saveSettings(const std::string& parFile,VProperty* guiProp,VSettings* vs,bool global) +{ + using boost::property_tree::ptree; + ptree pt; + + //Get editable properties. We will operate on the links. + std::vector linkVec; + guiProp->collectLinks(linkVec); + + for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) + { + if(global) + { + if((*it)->changed()) + { + pt.put((*it)->path(),(*it)->valueAsStdString()); + } + } + + else + { + if(!(*it)->useMaster()) + { + pt.put((*it)->path(),(*it)->valueAsStdString()); + } + } + } + + //Add settings stored in VSettings + if(vs) + { + //Loop over the possible properties + for(ptree::const_iterator it = vs->propertyTree().begin(); it != vs->propertyTree().end(); ++it) + { + pt.add_child(it->first,it->second); + } + } + + write_json(parFile,pt); +} + +//Loads the global settings that can be edited through the gui +void VConfig::loadSettings() +{ + SessionItem* cs=SessionHandler::instance()->current(); + std::string parFile=cs->settingsFile(); + + VProperty *guiProp=group("gui"); + + loadSettings(parFile,guiProp,true); +} + +//Loads the settings per server that can be edited through the servers option gui +void VConfig::loadSettings(const std::string& parFile,VProperty* guiProp,bool global) +{ + //We will operate on the links + std::vector linkVec; + guiProp->collectLinks(linkVec); + + //Parse file using the boost JSON property tree parser + using boost::property_tree::ptree; + ptree pt; + + try + { + read_json(parFile,pt); + } + catch (const boost::property_tree::json_parser::json_parser_error& e) + { + if(boost::filesystem::exists(parFile)) + { + std::string errorMessage = e.what(); + UserMessage::message(UserMessage::ERROR, true, + std::string("Error! VConfig::loadSettings() unable to parse settings file: " + parFile + " Message: " +errorMessage)); + } + return; + } + + for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) + { + if(pt.get_child_optional((*it)->path()) != boost::none) + { + std::string val=pt.get((*it)->path()); + + if(!global) + { + (*it)->setUseMaster(false); + } + + (*it)->setValue(val); + } + } + + //nodeMenuMode was introduced in 4.7.0 but was renamed in 4.8.0. We need to make sure + //it is read correctly when 4.8.0 started up for the first time. + //TODO: In versions after 4.8.0 we can remove this code!!! + if(global) + { + std::string prevPath="menu.access.nodeMenuMode"; + std::string actPath="server.menu.nodeMenuMode"; + if(pt.get_child_optional(prevPath) != boost::none) + { + for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) + { + if((*it)->path() == actPath) + { + std::string val=pt.get(prevPath); + (*it)->setValue(val); + break; + } + } + } + } + +} + +void VConfig::loadImportedSettings(const boost::property_tree::ptree& pt,VProperty* guiProp) +{ + std::vector linkVec; + guiProp->collectLinks(linkVec); + + for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) + { + if(pt.get_child_optional((*it)->path()) != boost::none) + { + std::string val=pt.get((*it)->path()); + (*it)->setValue(val); + } + else if((*it)->master()) + { + (*it)->setUseMaster(true); + } + } +} + +void VConfig::importSettings() +{ + boost::property_tree::ptree pt; + + std::string globalRcFile(DirectoryHandler::concatenate(DirectoryHandler::rcDir(),"user.default.options")); + if(readRcFile(globalRcFile,pt)) + { + VProperty* gr=VConfig::find("gui"); + loadImportedSettings(pt,gr); + VConfig::saveSettings(); + } +} + +bool VConfig::readRcFile(const std::string& rcFile,boost::property_tree::ptree& pt) +{ + std::ifstream in(rcFile.c_str()); + + if(!in.good()) + return false;; + + bool hasValue=false; + + std::string line; + while(getline(in,line)) + { + std::string buf; + std::stringstream ssdata(line); + std::vector vec; + + while(ssdata >> buf) + { + vec.push_back(buf); + } + + if(vec.size() >= 1) + { + std::vector par; + boost::split(par,vec[0],boost::is_any_of(":")); + + if(par.size()==2) + { + //Update + if(par[0] == "timeout") + { + pt.put("server.update.updateRateInSec",par[1]); + hasValue=true; + } + else if(par[0] == "poll") + { + pt.put("server.update.update",par[1]); + hasValue=true; + } + + else if(par[0] == "drift") + { + pt.put("server.update.adaptiveUpdate",par[1]); + hasValue=true; + } + else if(par[0] == "maximum") + { + pt.put("server.update.maxAdaptiveUpdateRateInMin",par[1]); + hasValue=true; + } + + //Files + else if(par[0] == "direct_read") + { + pt.put("server.files.readFilesFromDisk",par[1]); + hasValue=true; + } + else if(par[0] == "jobfile_length") + { + pt.put("server.files.maxOutputFileLines",par[1]); + hasValue=true; + } + + //Popup + else if(par[0] == "aborted") + { + pt.put("server.notification.aborted.enabled",par[1]); + pt.put("server.notification.aborted.popup",par[1]); + hasValue=true; + } + else if(par[0] == "restarted") + { + pt.put("server.notification.restarted.enabled",par[1]); + pt.put("server.notification.restarted.popup",par[1]); + hasValue=true; + } + else if(par[0] == "late") + { + pt.put("server.notification.late.enabled",par[1]); + pt.put("server.notification.late.popup",par[1]); + hasValue=true; + } + else if(par[0] == "zombies") + { + pt.put("server.notification.zombie.enabled",par[1]); + pt.put("server.notification.zombie.popup",par[1]); + hasValue=true; + } + else if(par[0] == "aliases") + { + pt.put("server.notification.alias.enabled",par[1]); + pt.put("server.notification.alias.popup",par[1]); + hasValue=true; + } + //Suites + else if(par[0] == "new_suites") + { + pt.put("suite_filter.autoAddNew",par[1]); + hasValue=true; + + } + else if(par[0] == "suites") + { + boost::property_tree::ptree suites; + suites.push_back(std::make_pair(std::string(""),boost::property_tree::ptree(par[1]))); + + for(unsigned int j=1; j < vec.size(); j++) + { + suites.push_back(std::make_pair(std::string(""),boost::property_tree::ptree(vec.at(j)))); + } + + pt.put_child("suite_filter.suites",suites); + + pt.put("suite_filter.enabled","true"); + + hasValue=true; + } + } + } + + } //while(getline) + + in.close(); + + return hasValue; + +} +/* +void VConfig::decodeShowMask() +{ + +option show::status32_ (globals::instance(), "show_mask32", 0); + +option show::status_ (globals::instance(), "show_mask", + (1< 31) { + status32_ = int(status32_) | (1<<(flag_-32)); + } else { + status_ = int(status_ ) | (1<<(flag_)); + } +} + +void show::off() +{ + if (flag_ == show::all) { + status_ = 0xFFFF ; status32_ = 0xFFFF; + status32_ = (int) (status32_) & (~(1<<(show::none-32))); + status32_ = (int) (status32_) & (~(1<<(show::all -32))); + } else if (flag_ == show::none) { + status_ = 0; status32_ = 0; + } else if (flag_ > 31) { + status32_ = int(status32_) & (~(1<<(flag_-32))); + } else { + status_ = int(status_) & (~(1< 31) { + return (int(status32_) & (1<<(flag_-32))) != 0; + } else { + return (int(status_ ) & (1< + +#include + +class VProperty; +class VServerSettings; +class VSettings; + +//This singleton class stores the configuration of the viewer. + +class VConfig +{ + friend class VServerSettings; + +public: + ~VConfig(); + + static VConfig* instance(); + + const std::string& appName() const {return appName_;} + const std::string& appLongName() const {return appLongName_;} + void init(const std::string& parDir); + const std::vector& groups() const {return groups_;} + VProperty* find(const std::string& path); + + VProperty* cloneServerGui(VProperty *linkTarget); + + void saveSettings(); + void importSettings(); + +protected: + VConfig(); + + void loadInit(const std::string& parFile); + void loadProperty(const boost::property_tree::ptree& pt,VProperty *prop); + void loadSettings(); + void saveSettings(const std::string& parFile,VProperty* guiProp,VSettings* vs,bool); + void loadSettings(const std::string& parFile,VProperty* guiProp,bool); + void loadImportedSettings(const boost::property_tree::ptree& pt,VProperty* guiProp); + bool readRcFile(const std::string& rcFile,boost::property_tree::ptree& pt); + + VProperty* group(const std::string& name); + + static VConfig* instance_; + + std::string appName_; + std::string appLongName_; + std::vector groups_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VConfigLoader.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VConfigLoader.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VConfigLoader.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VConfigLoader.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,51 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VConfigLoader.hpp" + +#include + + +typedef std::multimap Map; + +static Map* makers = 0; + +VConfigLoader::VConfigLoader(const std::string& name) +{ + if(makers == 0) + makers = new Map(); + + makers->insert(Map::value_type(name,this)); +} + +VConfigLoader::~VConfigLoader() +{ + // Not called +} + +bool VConfigLoader::process(const std::string& name,VProperty *prop) +{ + Map::size_type entries=makers->count(name); + Map::iterator it=makers->find(name); + + bool retVal=false; + for(Map::size_type cnt=0; cnt != entries; ++cnt, ++it) + { + (*it).second->load(prop); + retVal=true; + } + + /* if(it != makers->end()) + { + (*it).second->load(prop); + return true; + }*/ + return retVal; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VConfigLoader.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VConfigLoader.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VConfigLoader.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VConfigLoader.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,40 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VCONFIGLOADER_HPP_ +#define VCONFIGLOADER_HPP_ + +#include + +class VProperty; + +class VConfigLoader +{ +public: + explicit VConfigLoader(const std::string& name); + virtual ~VConfigLoader(); + + virtual void load(VProperty* group) = 0; + static bool process(const std::string& name,VProperty*); + +private: + // No copy allowed + explicit VConfigLoader(const VConfigLoader&); + VConfigLoader& operator=(const VConfigLoader&); +}; + +template +class SimpleLoader : public VConfigLoader { + void load(VProperty* prop) { T::load(prop); } +public: + explicit SimpleLoader(const std::string& name) : VConfigLoader(name) {} +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VDateAttr.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VDateAttr.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VDateAttr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VDateAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,148 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VDateAttr.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +#include "DateAttr.hpp" +#include "DayAttr.hpp" + +//================================ +// VDateAttrType +//================================ + +VDateAttrType::VDateAttrType() : VAttributeType("date") +{ + dataCount_=2; + searchKeyToData_["date_name"]=NameIndex; + searchKeyToData_["name"]=NameIndex; + scanProc_=VDateAttr::scan; +} + +QString VDateAttrType::toolTip(QStringList d) const +{ + QString t="Type: Date
    "; + if(d.count() == dataCount_) + { + t+="Name: " + d[NameIndex]; + } + return t; +} + +QString VDateAttrType::definition(QStringList d) const +{ + QString t; + if(d.count() == dataCount_) + { + t+=" " + d[NameIndex]; + } + return t; +} + +void VDateAttrType::encode(const DateAttr& d,QStringList& data) +{ + data << qName_ << QString::fromStdString(d.name()); +} + +void VDateAttrType::encode(const DayAttr& d,QStringList& data) +{ + data << qName_ << QString::fromStdString(d.name()); +} + +//===================================================== +// +// VDateAttr +// +//===================================================== + +VDateAttr::VDateAttr(VNode *parent,const DateAttr& t, int index) : + VAttribute(parent,index), + dataType_(DateData) +{ + //name_=t.name(); +} + +VDateAttr::VDateAttr(VNode *parent,const DayAttr& t, int index) : + VAttribute(parent,index), + dataType_(DayData) +{ + //name_=t.name(); +} + +VAttributeType* VDateAttr::type() const +{ + static VAttributeType* atype=VAttributeType::find("date"); + return atype; +} + +QStringList VDateAttr::data(bool /*firstLine*/) const +{ + static VDateAttrType* atype=static_cast(type()); + QStringList s; + if(parent_->node_) + { + if(dataType_ == DateData) + { + const std::vector& v=parent_->node_->dates(); + if(index_ < static_cast(v.size())) + atype->encode(v[index_],s); + } + else if(dataType_ == DayData) + { + const std::vector& v=parent_->node_->days(); + if(index_ < static_cast(v.size())) + atype->encode(v[index_],s); + } + } + return s; +} + +std::string VDateAttr::strName() const +{ + if(parent_->node_) + { + if(dataType_ == DateData) + { + const std::vector& v=parent_->node_->dates(); + if(index_ < static_cast(v.size())) + return v[index_].name(); + } + else if(dataType_ == DayData) + { + const std::vector& v=parent_->node_->days(); + if(index_ < static_cast(v.size())) + return v[index_].name(); + } + } + return std::string(); +} + +void VDateAttr::scan(VNode* vnode,std::vector& vec) +{ + if(vnode->node_) + { + const std::vector& dateV=vnode->node_->dates(); + const std::vector& dayV=vnode->node_->days(); + + int n=static_cast(dateV.size()); + for(int i=0; i < n; i++) + { + vec.push_back(new VDateAttr(vnode,dateV[i],i)); + } + + n=static_cast(dayV.size()); + for(int i=0; i < n; i++) + { + vec.push_back(new VDateAttr(vnode,dayV[i],i)); + } + } +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VDateAttr.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VDateAttr.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VDateAttr.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VDateAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,59 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VDATE_HPP +#define VDATE_HPP + +#include "VAttribute.hpp" +#include "VAttributeType.hpp" + +#include +#include + +class AttributeFilter; +class VAttributeType; +class VNode; + +class DateAttr; +class DayAttr; + +class VDateAttrType : public VAttributeType +{ +public: + explicit VDateAttrType(); + QString toolTip(QStringList d) const; + QString definition(QStringList d) const; + void encode(const DateAttr& d,QStringList& data); + void encode(const DayAttr& d,QStringList& data); + +private: + enum DataIndex {TypeIndex=0,NameIndex=1}; +}; + +class VDateAttr : public VAttribute +{ + +public: + enum DataType {DateData,DayData}; + + VDateAttr(VNode *parent,const DateAttr&,int index); + VDateAttr(VNode *parent,const DayAttr&,int index); + + VAttributeType* type() const; + QStringList data(bool firstLine) const; + std::string strName() const; + + static void scan(VNode* vnode,std::vector& vec); + +protected: + DataType dataType_; +}; + +#endif // VDATE_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VDir.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VDir.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VDir.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VDir.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,110 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VDir.hpp" +#include "DirectoryHandler.hpp" + +#include +#include +#include + +#include + + +VDir::VDir(const std::string& path) : path_(path), fetchMode_(NoFetchMode) +{ +} + +VDir::VDir(const std::string& path,const std::string& pattern) : + path_(path), + pattern_(pattern), + fetchMode_(NoFetchMode) +{ + reload(); +} + +VDir::~VDir() +{ + clear(); +} + +void VDir::path(const std::string& path,bool doReload) +{ + path_=path; + if(doReload) + reload(); +} + +void VDir::clear() +{ + for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) + delete (*it); +} + +void VDir::addItem(const std::string& name, unsigned int size,unsigned int mtime) +{ + VDirItem* item=new VDirItem; + + boost::filesystem::path p(name); + //std::string dirName=p.parent_path().string(); + std::string fileName=p.leaf().string(); + + item->name_=fileName; + item->size_=size; + item->mtime_ = QDateTime::fromTime_t(mtime); + + items_.push_back(item); + +} + +void VDir::reload() +{ + clear(); + + where_="localhost"; + + boost::filesystem::path path(path_); + + boost::filesystem::directory_iterator it(path), eod; + + BOOST_FOREACH(boost::filesystem::path const &p, std::make_pair(it, eod )) + { + if(is_regular_file(p) && boost::algorithm::starts_with(p.filename().string(),pattern_)) + { + VDirItem* item=new VDirItem; + + item->name_ = p.filename().string(); + item->size_ = boost::filesystem::file_size(p); + item->size_ = boost::filesystem::file_size(p); + item->mtime_ = QDateTime::fromTime_t(boost::filesystem::last_write_time(p)); + items_.push_back(item); + + } + } +} + +std::string VDir::fullName(int row) +{ + std::string res; + if(row >=0 && row < static_cast(items_.size())) + { + res=DirectoryHandler::concatenate(path_,items_.at(row)->name_); + } + return res; +} + +int VDir::findByFullName(const std::string& fName) +{ + for(std::size_t i=0; i < items_.size(); i++) + { + if(fullName(i) == fName) + return i; + } + return -1; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VDir.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VDir.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VDir.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VDir.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,78 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VDIR_H +#define VDIR_H + +#include +#include + +#include + +#include + +class VDirItem +{ +public: + std::string name_; + //int mode_; + //int uid_; + //int gid_; + unsigned int size_; + //int atime_; + QDateTime mtime_; + //int ctime_; + std::string method_; +}; + +class VDir +{ +public: + explicit VDir(const std::string& path); + VDir(const std::string& path, const std::string& pattern); + ~VDir(); + + enum FetchMode {NoFetchMode,LocalFetchMode,ServerFetchMode,LogServerFetchMode}; + + const std::string& path() const {return path_;} + void path(const std::string& path,bool reload=true); + + const std::string& where() const {return where_;} + void where(const std::string& w) {where_=w;} + + std::string fullName(int row); + int findByFullName(const std::string& fName); + int count() const {return static_cast(items_.size());} + void clear(); + void reload(); + void addItem(const std::string&, unsigned int, unsigned int); + const std::vector& items() const {return items_;} + + void setFetchDate(QDateTime d) {fetchDate_=d;} + QDateTime fetchDate() const {return fetchDate_;} + void setFetchMode(FetchMode m) {fetchMode_=m;} + FetchMode fetchMode() const {return fetchMode_;} + void setFetchModeStr(const std::string& fetchMethod) {fetchModeStr_=fetchMethod;} + const std::string& fetchModeStr() const {return fetchModeStr_;} + +protected: + std::string path_; + std::string pattern_; + std::string where_; + std::vector items_; + + FetchMode fetchMode_; + std::string fetchModeStr_; + QDateTime fetchDate_; +}; + +class VDir; +typedef boost::shared_ptr VDir_ptr; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VEventAttr.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VEventAttr.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VEventAttr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VEventAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,110 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VEventAttr.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +#include "NodeAttr.hpp" + +//================================ +// VEventAttrType +//================================ + +VEventAttrType::VEventAttrType() : VAttributeType("event") +{ + dataCount_=3; + searchKeyToData_["event_name"]=NameIndex; + searchKeyToData_["event_value"]=ValueIndex; + searchKeyToData_["name"]=NameIndex; + scanProc_=VEventAttr::scan; +} + +QString VEventAttrType::toolTip(QStringList d) const +{ + QString t="Type: Event
    "; + if(d.count() == dataCount_) + { + t+="Name: " + d[NameIndex] + "
    "; + t+="Status: "; + t+=(d[ValueIndex] == "1")?"set (true)":"clear (false)"; + + } + return t; +} + +QString VEventAttrType::definition(QStringList d) const +{ + QString t="event"; + if(d.count() == dataCount_) + { + t+=" " + d[NameIndex]; + } + return t; +} + +void VEventAttrType::encode(const Event& e,QStringList& data) const +{ + data << qName_ << + QString::fromStdString(e.name_or_number()) << + QString::number((e.value()==true)?1:0); +} + +//===================================================== +// +// VEventAttr +// +//===================================================== + +VEventAttr::VEventAttr(VNode *parent,const Event& e, int index) : VAttribute(parent,index) +{ + //name_=e.name_or_number(); +} + +VAttributeType* VEventAttr::type() const +{ + static VAttributeType* atype=VAttributeType::find("event"); + return atype; +} + +QStringList VEventAttr::data(bool /*firstLine*/) const +{ + static VEventAttrType* atype=static_cast(type()); + QStringList s; + if(node_ptr node=parent_->node_) + { + const std::vector& v=parent_->node_->events(); + atype->encode(v[index_],s); + } + return s; +} + +std::string VEventAttr::strName() const +{ + if(parent_->node_) + { + const std::vector& v=parent_->node_->events(); + return v[index_].name_or_number(); + } + return std::string(); +} + +void VEventAttr::scan(VNode* vnode,std::vector& vec) +{ + if(vnode->node_) + { + const std::vector& v=vnode->node_->events(); + int n=static_cast(v.size()); + for(int i=0; i < n; i++) + { + vec.push_back(new VEventAttr(vnode,v[i],i)); + } + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VEventAttr.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VEventAttr.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VEventAttr.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VEventAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,51 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VEVENT_HPP +#define VEVENT_HPP + +#include "VAttribute.hpp" +#include "VAttributeType.hpp" + +#include +#include +#include + +class AttributeFilter; +class VAttributeType; +class VNode; + +class Event; + +class VEventAttrType : public VAttributeType +{ +public: + explicit VEventAttrType(); + QString toolTip(QStringList d) const; + QString definition(QStringList d) const; + void encode(const Event&,QStringList&) const; + +private: + enum DataIndex {TypeIndex=0,NameIndex=1,ValueIndex=2}; +}; + +class VEventAttr : public VAttribute +{ +public: + VEventAttr(VNode *parent,const Event&,int index); + + VAttributeType* type() const; + QStringList data(bool firstLine) const; + std::string strName() const; + + static void scan(VNode* vnode,std::vector& vec); +}; + +#endif // VEVENT_HPP diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFile.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VFile.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFile.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFile.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,299 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "DirectoryHandler.hpp" +#include "VFile.hpp" +#include "UiLog.hpp" + +#include + +const size_t VFile::maxDataSize_=1024*1024*10; + +VFile::VFile(const std::string& name,const std::string& str,bool deleteFile) : + path_(name), + deleteFile_(deleteFile), + storageMode_(DiskStorage), + data_(0), + dataSize_(0), + fp_(0), + fetchMode_(NoFetchMode), + transferDuration_(0), + truncatedTo_(0), + cached_(false) +{ + std::ofstream f(path_.c_str()); + if(f.is_open()) + { + f << str; + f.close(); + } +} + +VFile::VFile(const std::string& name,bool deleteFile) : + path_(name), + deleteFile_(deleteFile), + storageMode_(DiskStorage), + data_(0), + dataSize_(0), + fp_(0), + fetchMode_(NoFetchMode), + transferDuration_(0), + truncatedTo_(0), + cached_(false) +{ +} + +VFile::VFile(bool deleteFile) : + path_(""), + deleteFile_(deleteFile), + storageMode_(MemoryStorage), + data_(0), + dataSize_(0), + fp_(0), + fetchMode_(NoFetchMode), + transferDuration_(0), + truncatedTo_(0), + cached_(false) +{ +} + +VFile::~VFile() +{ + close(); + + UiLog().dbg() << "VFile::~VFile -->"; + print(); + + if(data_) + { + delete [] data_; + UiLog().dbg() << " memory released"; + } + + //TODO: add further/better checks + if(deleteFile_ && + exists() && !path_.empty() && path_ != "/" && path_.size() > 4) + { + unlink(path_.c_str()); + UiLog().dbg() << " file deleted from disk"; + } + else if(!path_.empty() && exists()) + { + UiLog().dbg() << " file was kept on disk"; + } + + UiLog().dbg() << "<-- ~VFile"; +} + +bool VFile::exists() const +{ + if(path_.empty()) + return false; + return (access(path_.c_str(), R_OK) ==0); +} + +VFile_ptr VFile::create(const std::string& path,const std::string& str,bool deleteFile) +{ + return VFile_ptr(new VFile(path,str,deleteFile)); +} + +VFile_ptr VFile::create(const std::string& path,bool deleteFile) +{ + return VFile_ptr(new VFile(path,deleteFile)); +} + +VFile_ptr VFile::create(bool deleteFile) +{ + return VFile_ptr(new VFile(deleteFile)); +} + +VFile_ptr VFile::createTmpFile(bool deleteFile) +{ + std::string tmpFile=DirectoryHandler::tmpFileName(); + std::ofstream f(tmpFile.c_str()); + if(f.is_open()) + { + f.close(); + } + + return VFile_ptr(new VFile(tmpFile,deleteFile)); +} + +void VFile::setStorageMode(StorageMode mode) +{ + if(storageMode_ == mode) + return; + + storageMode_=mode; + + if(storageMode_== DiskStorage) + { + if(dataSize_ > 0) + { + if(path_.empty()) + path_=DirectoryHandler::tmpFileName(); + + fp_ = fopen(path_.c_str(),"w"); + if(fwrite(data_,1,dataSize_,fp_) != dataSize_) + { + + } + fclose(fp_); + fp_=NULL; + delete [] data_; + data_=0; + dataSize_=0; + } + } +} + +bool VFile::write(const std::string& buf,std::string& err) +{ + return write(buf.c_str(),buf.size(),err); +} + +bool VFile::write(const char *buf,size_t len,std::string& err) +{ + //printf("total:%d \n len: %d \n",dataSize_,len); + + //Keep data in memory + if(storageMode_ == MemoryStorage) + { + if(!data_) + { + data_ = new char[maxDataSize_+1]; + } + + if(dataSize_ + len < maxDataSize_) + { + memcpy(data_+dataSize_,buf,len); + dataSize_+=len; + data_[dataSize_] = '\0'; //terminate the string + return true; + } + else + { + setStorageMode(DiskStorage); + } + } + + //Write data to disk + if(storageMode_ == DiskStorage) + { + if(!fp_) + { + if(path_.empty()) + path_=DirectoryHandler::tmpFileName(); + + fp_ = fopen(path_.c_str(),"a"); + } + + if(fwrite(buf,1,len,fp_) != len) + { + //char buf_loc[2048]; + //sprintf(buf_loc,"Write error on %s",out->path().c_str()); + //gui::syserr(buf); + fclose(fp_); + return false; + } + fflush(fp_); + } + + return true; +} + +void VFile::close() +{ + if(fp_) + { + fclose(fp_); + fp_=NULL; + } + if(data_) + { + data_[dataSize_]='\0'; + dataSize_++; + } +} + +/* +std::string VFile::tmpName() +{ + std::string res; + +#if defined(linux) || defined(_AIX) + + char *path = getenv("SCRATCH"); + char *s = (char*) malloc(128); + + if (!path || !access(path, R_OK)) + path=getenv("TMPDIR"); + + if (!path || !access(path, R_OK)) + path=(char*)"/tmp"; + + snprintf(s, 128, "%s/%sXXXXXX", path, "ecflow_ui"); + if(mkstemp(s) != -1) + { + res=std::string(s); + } + + free(s); + +#else + +// char* s=std::string(tmpnam(NULL)); + res=std::string(tmpnam(NULL)); + +#endif + + return res; + +} +*/ + +bool VFile::isEmpty() const +{ + if(storageMode_ == VFile::MemoryStorage) + return dataSize_ == 0; + + if(exists()) + { + struct stat info; + return (::stat( path_.c_str(), &info ) != 0 || info.st_size == 0); + } + return false; +} + +void VFile::print() +{ + std::string str=" VFile contents --> storage:"; + if(storageMode_ == MemoryStorage) + { + str+="memory size:" + boost::lexical_cast(dataSize_); + } + else + { + str+="disk path: " + path_; + } + + UiLog().dbg() << str; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFile.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VFile.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFile.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFile.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,100 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VFILE_INC__ +#define VFILE_INC__ + +#include +#include + +#include +#include + +#include + +class VFile; +typedef boost::shared_ptr VFile_ptr; + +class VFile : public boost::enable_shared_from_this +{ +public: + virtual ~VFile(); + + enum StorageMode {MemoryStorage,DiskStorage}; + enum FetchMode {NoFetchMode,LocalFetchMode,ServerFetchMode,LogServerFetchMode}; + + const std::string& path() const {return path_;} + const std::string& sourcePath() const {return sourcePath_;} + void setSourcePath(const std::string& p) {sourcePath_=p;} + void setContents(const std::string); + bool exists() const; + bool isEmpty() const; + + StorageMode storageMode() const {return storageMode_;} + static const size_t maxDataSize() {return maxDataSize_;} + size_t dataSize() const {return dataSize_;} + const char* data() const {return data_;} + + void setTransferDuration(unsigned int d) {transferDuration_=d;} + unsigned int transferDuration() const {return transferDuration_;} + void setFetchDate(QDateTime d) {fetchDate_=d;} + QDateTime fetchDate() const {return fetchDate_;} + void setFetchMode(FetchMode m) {fetchMode_=m;} + FetchMode fetchMode() const {return fetchMode_;} + void setFetchModeStr(const std::string& fetchMethod) {fetchModeStr_=fetchMethod;} + const std::string& fetchModeStr() const {return fetchModeStr_;} + int truncatedTo() const {return truncatedTo_;} + void setTruncatedTo(int t) {truncatedTo_=t;} + void setCached(bool b) {cached_=b;} + bool cached() const {return cached_;} + void setLog(const std::vector& log) {log_=log;} + void addToLog(const std::string& s) {log_.push_back(s);} + const std::vector& log() const {return log_;} + + bool write(const char *buf,size_t len,std::string& err); + bool write(const std::string& buf,std::string& err); + + void close(); + void print(); + + static VFile_ptr create(const std::string& path,const std::string& contents,bool deleteFile=true); + static VFile_ptr create(const std::string& path,bool deleteFile= true); + static VFile_ptr create(bool deleteFile= true); + static VFile_ptr createTmpFile(bool deleteFile= true); + + //static std::string tmpName(); + +protected: + VFile(const std::string& name,const std::string& str,bool deleteFile=true); + VFile(const std::string& str,bool deleteFile= true); + explicit VFile(bool deleteFile= true); + void setStorageMode(StorageMode); + + std::string path_; + std::string sourcePath_; + bool deleteFile_; + + StorageMode storageMode_; + static const size_t maxDataSize_; + char* data_; + size_t dataSize_; + FILE* fp_; + + FetchMode fetchMode_; + std::string fetchModeStr_; + QDateTime fetchDate_; + unsigned int transferDuration_; + int truncatedTo_; + + bool cached_; + std::vector log_; + +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFileInfo.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VFileInfo.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFileInfo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFileInfo.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,107 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VFileInfo.hpp" + +#include +#include + +QString VFileInfo::formatSize() const +{ + return formatSize(size()); +} + +QString VFileInfo::formatModDate() const +{ + QDateTime dt=lastModified(); + return dt.toString("yyyy-MM-dd hh:mm:ss"); +} + +QString VFileInfo::formatPermissions() const +{ + QString str(permission(QFile::ReadOwner)?"r":"-"); + str+=(permission(QFile::WriteOwner)?"w":"-"); + str+=(permission(QFile::ExeOwner)?"x":"-"); + str+=(permission(QFile::ReadGroup)?"r":"-"); + str+=(permission(QFile::WriteGroup)?"w":"-"); + str+=(permission(QFile::ExeGroup)?"x":"-"); + str+=(permission(QFile::ReadOther)?"r":"-"); + str+=(permission(QFile::WriteOther)?"w":"-"); + str+=(permission(QFile::ExeOther)?"x":"-"); + + return str; +} + +QString VFileInfo::formatSize(unsigned int size) +{ + if(size < 1024) + return QString::number(size) + " B"; + else if(size < 1024*1024) + return QString::number(size/1024) + " KB"; + else if(size < 1024*1024*1024) + return QString::number(size/(1024*1024)) + " MB"; + else + return QString::number(size/(1024*1024*1024)) + " GB"; + + return QString(); +} + +QString VFileInfo::formatDate(const std::time_t& t) +{ + QDateTime dt=QDateTime::fromTime_t(t); + return dt.toString("yyyy-MM-dd hh:mm:ss"); +} + +QString VFileInfo::formatDateAgo(const std::time_t& t) +{ + QString str=QObject::tr("Right now"); + + time_t now = time(0); + + int delta = now - t; + if(delta<0) delta = 0; + + if( t== 0) + { + return QObject::tr("never"); + } + + if(delta ==1) + str=QObject::tr("1 second ago"); + + else if(delta >=1 && delta < 60) + { + str=QString::number(delta) + QObject::tr(" second") + ((delta==1)?"":"s") + QObject::tr(" ago"); + } + + else if(delta >= 60 && delta < 60*60) + { + int val=delta/60; + str=QString::number(val) + QObject::tr(" minute") + ((val==1)?"":"s") + QObject::tr(" ago"); + } + + else if(delta >= 60*60 && delta < 60*60*24) + { + int val=delta/(60*60); + str=QString::number(val) + QObject::tr(" hour") + ((val==1)?"":"s") + QObject::tr(" ago"); + } + + else if(delta >= 60*60*24) + { + int val=delta/(60*60*24); + str=QString::number(val) + QObject::tr(" day") + ((val==1)?"":"s") + QObject::tr(" ago"); + } + + return str; +} + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFileInfo.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VFileInfo.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFileInfo.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFileInfo.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,31 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VFILEINFO_H_ +#define VFILEINFO_H_ + +#include + +#include + +class VFileInfo : public QFileInfo +{ +public: + explicit VFileInfo(const QString& file) : QFileInfo(file) {} + + QString formatSize() const; + QString formatModDate() const; + QString formatPermissions() const; + + static QString formatSize(unsigned int size); + static QString formatDate(const std::time_t& t); + static QString formatDateAgo(const std::time_t& t); +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFilter.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VFilter.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFilter.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFilter.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,803 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#include "VFilter.hpp" + +#include "NodeQuery.hpp" +#include "NodeQueryEngine.hpp" +#include "UIDebug.hpp" +#include "UiLogS.hpp" +#include "VNState.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VIcon.hpp" +#include "VNode.hpp" +#include "VParam.hpp" +#include "VSettings.hpp" +#include "VTree.hpp" + +#include "ServerFilter.hpp" +#include "ServerHandler.hpp" + +#include +#include + +#include + +#define _UI_VFILTER_DEBUG + +//============================================== +// +// VFilter +// +//============================================== + +VParamSet::VParamSet() : empty_(true), complete_(false) +{ +} + +void VParamSet::init(const std::vector& items) +{ + all_=items; + + int maxId=0; + for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + if(static_cast((*it)->id()) > maxId) + maxId=(*it)->id(); + } + + if(maxId > 0) + { + currentCache_.resize(maxId+1,0); + for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + currentCache_[(*it)->id()]=1; + } + } + + setCurrent(all_,false); +} + +//This has to be very fast. Called a millions of times! +bool VParamSet::isSet(VParam* p) const +{ + //assert(p); + //return (current_.find(p) != current_.end()); + //return std::find(current_.begin(),current_.end(),p) != current_.end(); + + //for(size_t i=0; i < current_.size(); i++) + // if(current_[i] == p) + // return true; + + //return true; + return (currentCache_[p->id()]==0)?false:true; + + //return false; +} + +bool VParamSet::isSet(const std::string &name) const +{ + for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) + { + if((*it)->strName() == name) + return true; + } + return false; +} + +QStringList VParamSet::currentAsList() const +{ + QStringList lst; + for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) + { + lst << QString::fromStdString((*it)->strName()); + } + return lst; +} + +void VParamSet::clearCurrent() +{ + current_.clear(); + std::fill(currentCache_.begin(), currentCache_.end(), 0); + empty_=true; + complete_=false; +} + +void VParamSet::addToCurrent(VParam* p) +{ + current_.push_back(p); + uint id=p->id(); + UI_ASSERT(id >=0 && id < currentCache_.size(),"id=" << id + << " currentCache_.size()=" << currentCache_.size()); + currentCache_[id]=1; + empty_=false; + complete_=(current_.size() == all_.size()); +} + +void VParamSet::setCurrent(const std::vector& items,bool broadcast) +{ + clearCurrent(); + + for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + if(std::find(all_.begin(),all_.end(),*it) != all_.end()) + { + addToCurrent(*it); + } + } + + if(broadcast) + Q_EMIT changed(); +} + +void VParamSet::setCurrent(const std::vector& names,bool broadcast) +{ + clearCurrent(); + + for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + if(std::find(names.begin(),names.end(),(*it)->strName()) != names.end()) + { + addToCurrent(*it); + } + } + + if(broadcast) + Q_EMIT changed(); +} + +void VParamSet::setCurrent(QStringList names,bool broadcast) +{ + clearCurrent(); + + for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + if(names.contains(QString::fromStdString((*it)->strName()))) + { + addToCurrent(*it); + } + } + + if(broadcast) + Q_EMIT changed(); +} + + +void VParamSet::writeSettings(VSettings *vs) +{ + std::vector array; + + if(isComplete()) + { + array.push_back("_ALL_"); + } + else + { + for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) + { + array.push_back((*it)->strName()); + } + } + + vs->put(settingsId_,array); +} + +void VParamSet::readSettings(VSettings* vs) +{ + clearCurrent(); + + std::vector array; + + //Try to read the old version (aka V0) of the settings + //In this case an empty list means all is selected! + if(vs->contains(settingsIdV0_)) + { + vs->get(settingsIdV0_,array); + if(array.empty()) + { + setCurrent(all_,false); + return; + } + } + //otherwise read the standard version + else + { + vs->get(settingsId_,array); + } + + for(std::vector::const_iterator it = array.begin(); it != array.end(); ++it) + { + std::string name=*it; + if(name == "_ALL_") + { + setCurrent(all_,false); + return; + } + + for(std::vector::const_iterator itA=all_.begin(); itA != all_.end(); ++itA) + { + if((*itA)->strName() == name) + addToCurrent(*itA); + } + } +} + +//============================================== +// +// StateFilter +// +//============================================== + +NodeStateFilter::NodeStateFilter() : VParamSet() +{ + settingsId_="states"; + settingsIdV0_="state"; + std::vector v=VNState::filterItems(); + init(v); +} + + +//============================================== +// +// AttributeFilter +// +//============================================== + +AttributeFilter::AttributeFilter() : VParamSet() +{ + settingsId_="attributes"; + settingsIdV0_="attribute"; + std::vector v=VAttributeType::filterItems(); + init(v); + + /*for(std::set::const_iterator it=all_.begin(); it != all_.end(); ++it) + { + if((*it)->strName() != "var" && (*it)->strName() != "genvar") + current_.insert(*it); + }*/ +} + +bool AttributeFilter::matchForceShowAttr(const VNode *n,VAttributeType* t) const +{ + if(forceShowAttr_) + { + if(VAttribute *a=forceShowAttr_->attribute()) + return (a->parent() == n && a->type() == t); + } + return false; +} + +void AttributeFilter::setForceShowAttr(VAttribute* a) +{ + forceShowAttr_=VInfoAttribute::create(a); +} + +VAttribute* AttributeFilter::forceShowAttr() const +{ + return (forceShowAttr_)?(forceShowAttr_->attribute()):0; +} + +void AttributeFilter::clearForceShowAttr() +{ + forceShowAttr_.reset(); +} + +void AttributeFilter::updateForceShowAttr() +{ + if(forceShowAttr_) + { + forceShowAttr_->regainData(); + if(forceShowAttr_->hasData()) + { + forceShowAttr_.reset(); + } + } +} + +//============================================== +// +// IconFilter +// +//============================================== + +IconFilter::IconFilter() : VParamSet() +{ + settingsId_="icons"; + settingsIdV0_="icon"; + std::vector v=VIcon::filterItems(); + init(v); +} + +void IconFilter::readSettings(VSettings* vs) +{ + VParamSet::readSettings(vs); + + //If the filter list is not complete we need to be sure that a newly added icon type + //is automatically enabled in the filter. This is based on the contents of the lastNames icon + //file. This file is updated on exit and stores the full list of icon names (existing at exit). + //So we can figure out if a new icon type were introduced since the last startup and we can + //guarantee that is is always enabled for the first time. + if(!isComplete()) + { + const std::vector& lastNames=VIcon::lastNames(); + + //The lastNames are not found. This must be the first startup after lastNames concept were introduced + //or it is a fresh startup after cleaning the config (or the very first one). We enable all the icons. + //It could be only a one-time problem for users who already set theit icon filter. + if(lastNames.empty()) + { + setCurrent(all_,false); + } + else + { + //Check which icons are not in lastNames + for(std::vector::const_iterator itA=all_.begin(); itA != all_.end(); ++itA) + { + //The item is not in lastNames so it must be a newly added icon type. We add it to the filter list + if(std::find(lastNames.begin(),lastNames.end(),(*itA)->strName()) == lastNames.end()) + { + addToCurrent(*itA); + } + } + } + } +} + +//============================================== +// +// NodeFilter +// +//============================================== + +NodeFilterDef::NodeFilterDef(ServerFilter* serverFilter,Scope scope) : + serverFilter_(serverFilter), + nodeState_(0) +{ + nodeState_=new NodeStateFilter; + + //if(scope == NodeStateScope) + // nodeState_=new NodeStateFilter; + + //else if(scope == GeneralScope) + // nodeState_=new NodeStateFilter; + + if(nodeState_) + { + exprStr_="state = all"; + + connect(nodeState_,SIGNAL(changed()), + this,SIGNAL(changed())); + } + + query_=new NodeQuery("tmp",true); + //QStringList sel("aborted"); + //query_->setStateSelection(sel); +} + +NodeFilterDef::~NodeFilterDef() +{ + delete query_; +} + +NodeQuery* NodeFilterDef::query() const +{ + return query_; +} + +void NodeFilterDef::setQuery(NodeQuery* q) +{ + query_->swap(q); + Q_EMIT changed(); +} + +void NodeFilterDef::writeSettings(VSettings *vs) +{ + vs->beginGroup("query"); + query_->save(vs); + vs->endGroup(); +} + +void NodeFilterDef::readSettings(VSettings *vs) +{ + vs->beginGroup("query"); + query_->load(vs); + vs->endGroup(); + + Q_EMIT changed(); +} + +NodeFilter::NodeFilter(NodeFilterDef* def,ServerHandler* server) : + def_(def), + matchMode_(VectorMatch), + server_(server), + forceShowNode_(0) +{ + assert(server_); + + queryEngine_=new NodeFilterEngine(this); +} + +NodeFilter::~NodeFilter() +{ + delete queryEngine_; +} + +void NodeFilter::clear() +{ + clearForceShowNode(); +} + +void NodeFilter::setForceShowNode(VNode* n) +{ + forceShowNode_=n; +#ifdef _UI_VFILTER_DEBUG + if(forceShowNode_) + UiLogS(server_).dbg() << "NodeFilter::setForceShowNode --> " << forceShowNode_->absNodePath(); +#endif +} + +void NodeFilter::clearForceShowNode() +{ + forceShowNode_=0; +} + +//============================================ +// +// TreeNodeFilter +// +//============================================ + +TreeNodeFilter::TreeNodeFilter(NodeFilterDef* def,ServerHandler* server,VTree* tree) : + NodeFilter(def,server), + tree_(tree) +{ +} + +void TreeNodeFilter::clear() +{ + NodeFilter::clear(); + match_=std::vector(); +} + +bool TreeNodeFilter::isNull() +{ + //return def_->nodeState_->isComplete() || def_->nodeState_->isEmpty(); + return def_->nodeState_->isEmpty(); +} + +bool TreeNodeFilter::isComplete() +{ + return def_->nodeState_->isComplete(); +} + +// +bool TreeNodeFilter::update(const std::vector& topChange,std::vector& topFilterChange) +{ +#ifdef _UI_VFILTER_DEBUG + UI_FUNCTION_LOG_S(server_); +#endif + + //nodes_.clear(); + + //If all states are hidden or visible + if(def_->nodeState_->isComplete() || def_->nodeState_->isEmpty()) + { + //deallocate the match vector + match_=std::vector(); + //assert(match_.capacity() == 0); +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " no filter is defined!"; +#endif + return false; + } + +#ifdef _UI_VFILTER_DEBUG + QTime timer; + timer.start(); +#endif + + VServer* root=server_->vRoot(); + if(root->totalNum() > 0) + { + bool fullRun=false; + + //The number of nodes changed: we need to rerun everything + if(static_cast(match_.size()) != root->totalNum() || match_.size() != tree_->nodeVec().size()) + { + //Deallocates the match vector + match_=std::vector(); + //match_.reserve(root->totalNum()); + VNode *n=0; + match_.resize(root->totalNum(),n); + //td::fill(match_.begin(), match_.end(), n); + fullRun=true; + } + + //The topchange vector is empty: it can only happen when we need to rerun everything + else if(topChange.empty()) + { + VNode *n=0; + //match_.clear(); + std::fill(match_.begin(), match_.end(), n); + fullRun=true; + } + + //We rerun everything + if(fullRun) + { + for(int i=0; i < root->numOfChildren(); i++) + { + filterState(root->childAt(i),def_->nodeState_); + } + } + + //We only check the branches defined by the nodes in topChange + else + { + //At this point the tree_->nodeVec() and match must have the same content + assert(tree_->nodeVec().size() == match_.size()); + + //Update the filter results + for(size_t i=0; i < topChange.size(); i++) + { + filterState(topChange[i],def_->nodeState_); + } + +#ifdef _UI_VFILTER_DEBUG + int diffCnt=0; + for(size_t i=0; i < match_.size(); i++) + { + if(tree_->vnodeAt(i) != match_[i]) + diffCnt++; + } + UiLogS(server_).dbg() << " number of differences in filter: " << diffCnt; +#endif + + //We collect the topmost nodes with changes. It could be different to + //topChange so we need this step! + for(size_t i=0; i < topChange.size(); i++) + { + assert(topChange[i]->isSuite()); + collectTopFilterChange(topChange[i],topFilterChange); + } + +#ifdef _UI_VFILTER_DEBUG + assert(static_cast(topFilterChange.size()) <= diffCnt); + if(diffCnt > 0) + assert(topFilterChange.size() >0); +#endif + } + +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " top level nodes that changed in filter:"; + for(size_t i= 0; i < topFilterChange.size(); i++) + UiLogS(server_).dbg() << " " << topFilterChange.at(i)->strName(); +#endif + + } + else + { + match_.clear(); + } + +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " elapsed time: " << timer.elapsed() << " ms"; + UiLogS(server_).dbg() << " filter size: " << match_.size(); + UiLogS(server_).dbg() << " capacity:" << match_.capacity(); +#endif + + return true; +} + +bool TreeNodeFilter::update() +{ + std::vector topChange; + std::vector topFilterChange; + return update(topChange,topFilterChange); +} + +//Finds the top level nodes whose filter status changed +bool TreeNodeFilter::collectTopFilterChange(VNode* node,std::vector& topFilterChange) +{ + int idx=node->index(); + if(tree_->vnodeAt(idx) != match_[idx]) + { + topFilterChange.push_back(node); + return true; + } + + for(int i=0; i < node->numOfChildren(); i++) + { + if(collectTopFilterChange(node->childAt(i),topFilterChange)) + { + break; + } + } + + return false; +} + +bool TreeNodeFilter::filterState(VNode* node,VParamSet* stateFilter) +{ + bool ok=false; + + if(stateFilter->isSet(VNState::toState(node)) || forceShowNode_ == node) + { + ok=true; + } + + for(int i=0; i < node->numOfChildren(); i++) + { + if(filterState(node->childAt(i),stateFilter) == true && ok == false) + { + ok=true; + } + } + + if(ok) + { + match_[node->index()]=node; + } + else + { + match_[node->index()]=NULL; + } + + return ok; +} + +//=========================================================== +// +// TableNodeFilter +// +//=========================================================== + +TableNodeFilter::TableNodeFilter(NodeFilterDef* def,ServerHandler* server) : + NodeFilter(def,server), + matchCount_(0) +{ +} + +//When nothing should be shown +bool TableNodeFilter::isNull() +{ + return false; + //return def_->nodeState_->isComplete(); + //return def_->nodeState_->isNull(); + +} + +//When everything should be shown +bool TableNodeFilter::isComplete() +{ + return false; + //return def_->nodeState_->isComplete(); +} + +void TableNodeFilter::clear() +{ + NodeFilter::clear(); + match_.clear(); + index_.clear(); + matchCount_=0; +} + +int TableNodeFilter::indexOf(const VNode* node) const +{ + switch(matchMode_) + { + case VectorMatch: + return index_[node->index()]; + case AllMatch: + return node->index(); + case NoneMatch: + return -1; + default: + assert(0); + return -1; + } + + return -1; +} + +VNode* TableNodeFilter::nodeAt(int index) const +{ + switch(matchMode_) + { + case VectorMatch: + return match_[index]; + case AllMatch: + return server_->vRoot()->nodeAt(index); + case NoneMatch: + return NULL; + default: + assert(0); + return NULL; + } + + return NULL; +} + +bool TableNodeFilter::update() +{ +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << "TableNodeFilter::update -->"; +#endif + + NodeQuery* q=def_->query_; + Q_ASSERT(q); + if(!q->hasServer(server_->name()) || server_->vRoot()->totalNum() ==0) + { + matchMode_=NoneMatch; + //Deallocates + match_=std::vector(); + index_=std::vector(); + matchCount_=0; +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " no nodes are filtered!"; +#endif + return true; + } + + if(q->query().isEmpty() && q->rootNode().empty()) + { + matchMode_=AllMatch; + //Deallocates + match_=std::vector(); + index_=std::vector(); + matchCount_=server_->vRoot()->totalNum(); +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " all the nodes are filtered!"; +#endif + return true; + } + +#ifdef _UI_VFILTER_DEBUG + QTime timer; + timer.start(); +#endif + + matchMode_=VectorMatch; + match_.clear(); + int num=server_->vRoot()->totalNum(); + if(num != static_cast(index_.size())) + { + //Reallocates + index_=std::vector(); + index_.resize(num,-1); + } + else + { + std::fill(index_.begin(), index_.end(), -1); + } + + queryEngine_->setQuery(def_->query_); + if(queryEngine_->runQuery(server_)) + { + matchCount_=match_.size(); + for(size_t i=0; i < match_.size(); i++) + { + index_[match_[i]->index()]=i; + } + } + else + { + std::fill(index_.begin(),index_.end(),-1); + } + +#ifdef _UI_VFILTER_DEBUG + UiLogS(server_).dbg() << " elapsed time: " << timer.elapsed() << " ms"; + UiLogS(server_).dbg() << " filter size: " << match_.size(); + UiLogS(server_).dbg() << " capacity: " << match_.capacity(); +#endif + + return true; +} + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VFilter.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VFilter.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VFilter.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VFilter.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,218 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +//============================================================================ + +#ifndef VIEWFILTER_HPP_ +#define VIEWFILTER_HPP_ + +#include +#include + +#include +#include + +#include "VInfo.hpp" +#include "VParam.hpp" + +#include "Node.hpp" + +class NodeQuery; +class NodeFilterEngine; +class ServerFilter; +class ServerHandler; +class VAttribute; +class VAttributeType; +class VInfo; +class VNode; +class VSettings; +class VTree; + +#include + +class VParamSet : public QObject +{ +Q_OBJECT + +public: + VParamSet(); + virtual ~VParamSet() {} + + const std::vector& all() const {return all_;} + const std::vector& current() const {return current_;} + QStringList currentAsList() const; + void setCurrent(const std::vector&,bool broadcast=true); + void setCurrent(const std::vector&,bool broadcast=true); + void setCurrent(QStringList,bool broadcast=true); + + bool isEmpty() const {return empty_;} + bool isComplete() const { return complete_;} + bool isSet(const std::string&) const; + bool isSet(VParam*) const; + + void writeSettings(VSettings* vs); + void virtual readSettings(VSettings* vs); + +Q_SIGNALS: + void changed(); + +protected: + void init(const std::vector& items); + void addToCurrent(VParam*); + + std::vector all_; + std::vector current_; + std::string settingsId_; + std::string settingsIdV0_; + +private: + void clearCurrent(); + + std::vector currentCache_; //we use to speed up the check in isSet() + bool empty_; + bool complete_; +}; + +class NodeStateFilter : public VParamSet +{ +public: + NodeStateFilter(); +}; + +class AttributeFilter : public VParamSet +{ +public: + AttributeFilter(); + bool matchForceShowAttr(const VNode*,VAttributeType*) const; + void setForceShowAttr(VAttribute* a); + void clearForceShowAttr(); + void updateForceShowAttr(); + VAttribute* forceShowAttr() const; + +private: + VInfo_ptr forceShowAttr_; +}; + +class IconFilter : public VParamSet +{ +public: + IconFilter(); + void readSettings(VSettings* vs); +}; + + +class TreeNodeFilter; +class TableNodeFilter; + +class NodeFilterDef : public QObject +{ +Q_OBJECT + +friend class TreeNodeFilter; +friend class TableNodeFilter; + +public: + enum Scope {NodeStateScope,GeneralScope}; + NodeFilterDef(ServerFilter*,Scope); + ~NodeFilterDef(); + + NodeStateFilter* nodeState() const {return nodeState_;} + + const std::string& exprStr() const {return exprStr_;} + NodeQuery* query() const; + void setQuery(NodeQuery*); + + void writeSettings(VSettings *vs); + void readSettings(VSettings *vs); + +Q_SIGNALS: + void changed(); + +protected: + ServerFilter *serverFilter_; + std::string exprStr_; + NodeStateFilter *nodeState_; + std::string nodePath_; + std::string nodeType_; + NodeQuery* query_; + + //AttributeFilter *attribute_; + //std::string nodeType_; + //std::string nodeName_; +}; + + +class NodeFilter +{ + friend class NodeFilterEngine; + friend class VTreeServer; + +public: + enum MatchMode {NoneMatch,AllMatch,VectorMatch}; + + NodeFilter(NodeFilterDef* def,ServerHandler*); + virtual ~NodeFilter(); + + virtual void clear(); + virtual bool isNull()=0; + virtual bool isComplete()=0; + virtual int matchCount() const = 0; + virtual bool update()=0; + + VNode* forceShowNode() const {return forceShowNode_;} + void setForceShowNode(VNode*); + void clearForceShowNode(); + +protected: + NodeFilterDef* def_; + NodeFilterEngine* queryEngine_; + std::set type_; + MatchMode matchMode_; + std::vector match_; + ServerHandler * server_; + VNode* forceShowNode_; +}; + +class TreeNodeFilter : public NodeFilter +{ +public: + explicit TreeNodeFilter(NodeFilterDef* def,ServerHandler*,VTree*); + + void clear(); + bool isNull(); + bool isComplete(); + int matchCount() const {return 0;} + bool update(); + bool update(const std::vector& topChange, + std::vector& topFilterChange); + +private: + bool filterState(VNode* node,VParamSet* stateFilter); + bool collectTopFilterChange(VNode* n,std::vector& topFilterChange); + + VTree* tree_; +}; + +class TableNodeFilter : public NodeFilter +{ +public: + explicit TableNodeFilter(NodeFilterDef* def,ServerHandler*); + + void clear(); + bool isNull(); + bool isComplete(); + int matchCount() const {return matchCount_;} + bool update(); + int indexOf(const VNode*) const; + VNode* nodeAt(int index) const; + +private: + std::vector index_; + int matchCount_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VGenVarAttr.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VGenVarAttr.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VGenVarAttr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VGenVarAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,112 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VGenVarAttr.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +#include "NodeAttr.hpp" + +//================================ +// VGenVarAttrType +//================================ + +VGenVarAttrType::VGenVarAttrType() : VAttributeType("genvar") +{ + dataCount_=3; + searchKeyToData_["var_name"]=NameIndex; + searchKeyToData_["var_value"]=ValueIndex; + searchKeyToData_["var_type"]=TypeIndex; + searchKeyToData_["name"]=NameIndex; + scanProc_=VGenVarAttr::scan; +} + +QString VGenVarAttrType::toolTip(QStringList d) const +{ + QString t="Type: User variable
    "; + if(d.count() == dataCount_) + { + t+="Name: " + d[NameIndex] + "
    "; + QString s=d[ValueIndex]; + if(s.size() > 150) s=s.left(150) + "..."; + t+="Value: " + s; + } + return t; +} + +void VGenVarAttrType::encode(const Variable& v,QStringList& data) const +{ + data << qName_ << + QString::fromStdString(v.name()) << + QString::fromStdString(v.theValue()); +} + +//===================================================== +// +// VGenVarAttr +// +//===================================================== + +VGenVarAttr::VGenVarAttr(VNode *parent,const Variable& v, int index) : VAttribute(parent,index) +{ + //name_=v.name(); +} + +VAttributeType* VGenVarAttr::type() const +{ + static VAttributeType* atype=VAttributeType::find("genvar"); + return atype; +} + +QStringList VGenVarAttr::data(bool /*firstLine*/) const +{ + static VGenVarAttrType* atype=static_cast(type()); + QStringList s; + if(parent_->isServer() == 0) + { + std::vector v; + parent_->genVariables(v); + atype->encode(v[index_],s); + } + return s; +} + +std::string VGenVarAttr::strName() const +{ + if(parent_->isServer() == 0) + { + std::vector v; + parent_->genVariables(v); + return v[index_].name(); + } + return std::string(); +} + +void VGenVarAttr::scan(VNode* vnode,std::vector& vec) +{ + if(vnode->isServer() == 0) + { + std::vector v; + vnode->genVariables(v); + int n=static_cast(v.size()); + for(int i=0; i < n; i++) + { + vec.push_back(new VGenVarAttr(vnode,v[i],i)); + } + } +} + +bool VGenVarAttr::isReadOnly(const std::string& varName) +{ + static QStringList readOnlyVars=QStringList() << + "ECF_NODE" << "ECF_HOST" << "ECF_PORT" << "ECF_PID" << + "ECF_VERSION" << "ECF_LISTS"; + return readOnlyVars.contains(QString::fromStdString(varName)); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VGenVarAttr.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VGenVarAttr.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VGenVarAttr.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VGenVarAttr.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,50 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VGENVARATTR_HPP +#define VGENVARATTR_HPP + +#include "VAttribute.hpp" +#include "VAttributeType.hpp" + +#include +#include +#include + +class AttributeFilter; +class VAttributeType; +class VNode; +class Variable; + +class VGenVarAttrType : public VAttributeType +{ +public: + explicit VGenVarAttrType(); + QString toolTip(QStringList d) const; + void encode(const Variable&,QStringList&) const; + +private: + enum DataIndex {TypeIndex=0,NameIndex=1,ValueIndex=2}; +}; + +class VGenVarAttr : public VAttribute +{ +public: + VGenVarAttr(VNode *parent,const Variable&,int index); + + VAttributeType* type() const; + QStringList data(bool firstLine) const; + std::string strName() const; + static void scan(VNode* vnode,std::vector& vec); + static bool isReadOnly(const std::string&); +}; + +#endif // VGENVARATTR_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VIcon.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VIcon.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VIcon.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VIcon.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,546 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VIcon.hpp" + +#include +#include +#include + +#include +#include +#include +#include + +#include "ExprAst.hpp" +#include "Submittable.hpp" + +#include "DirectoryHandler.hpp" +#include "IconProvider.hpp" +#include "UserMessage.hpp" +#include "VConfigLoader.hpp" +#include "VFilter.hpp" +#include "VNode.hpp" +#include "VProperty.hpp" +#include "VConfig.hpp" +#include "VSettings.hpp" + +std::map VIcon::items_; +std::vector VIcon::itemsVec_; +std::vector VIcon::lastNames_; + +//========================================================== +// +// Define VIcon subclasses implementing the various icons. +// These do not need to be visible outside the class so we +// define them here in the cpp file. +// +//========================================================== + +class VWaitIcon : public VIcon +{ +public: + explicit VWaitIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VRerunIcon : public VIcon +{ +public: + explicit VRerunIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VNodeLogIcon : public VIcon +{ +public: + explicit VNodeLogIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VCompleteIcon : public VIcon +{ +public: + explicit VCompleteIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VTimeIcon : public VIcon +{ +public: + explicit VTimeIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VTimeFreeIcon : public VIcon +{ +public: + explicit VTimeFreeIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VDateIcon : public VIcon +{ +public: + explicit VDateIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VZombieIcon : public VIcon +{ +public: + explicit VZombieIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VLateIcon : public VIcon +{ +public: + explicit VLateIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VSlowIcon : public VIcon +{ +public: + explicit VSlowIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VKilledIcon : public VIcon +{ +public: + explicit VKilledIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +class VMigratedIcon : public VIcon +{ +public: + explicit VMigratedIcon(const std::string& name) : VIcon(name) {} + bool show(VNode*); +}; + +//========================================================== +// +// Create VIcon instances +// +//========================================================== + +//This also defines the order the icons will appear in the views +static VNodeLogIcon nodeLogIcon("message"); +static VRerunIcon rerunIcon("rerun"); +static VCompleteIcon completeIcon("complete"); +static VLateIcon lateIcon("late"); +static VTimeIcon timeIcon("time"); +static VTimeFreeIcon timeFreeIcon("time_free"); +static VDateIcon dateIcon("date"); +static VWaitIcon waitIcon("wait"); +static VZombieIcon zombieIcon("zombie"); +static VKilledIcon killedIcon("killed"); +static VSlowIcon slowIcon("slow"); +static VMigratedIcon migratedIcon("migrated"); + +//========================================================== +// +// The VIcon baseclass +// +//========================================================== + +VIcon::VIcon(const std::string& name) : + VParam(name), + pixId_(-1) +{ + items_[name]=this; + itemsVec_.push_back(this); +} + +VIcon::~VIcon() +{ +} + +void VIcon::initPixmap() +{ + if(!prop_) + { + UserMessage::message(UserMessage::WARN, true, + std::string("Warning! VIcon::initPixmap() unable to create icon image for: " + strName())); + return; + } + + //Add icon to iconprovider + if(VProperty* ip=prop_->findChild("icon")) + { + pixId_=IconProvider::add(":/viewer/" + ip->value().toString(),name()); + } +} + +QPixmap VIcon::pixmap(int size) +{ + return IconProvider::pixmap(name(),size); +} + +//=============================================================== +// +// Static methods +// +//=============================================================== + +std::vector VIcon::filterItems() +{ + std::vector v; + for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) + { + v.push_back(it->second); + } + + return v; +} + +VIcon* VIcon::find(const std::string& name) +{ + std::map::const_iterator it=items_.find(name); + if(it != items_.end()) + return it->second; + + return NULL; +} + +//Create the pixmap containing all the relevant icons for the given node according to the filter. +QVariantList VIcon::pixmapList(VNode *vnode,VParamSet *filter) +{ + QVariantList lst; + if(!vnode) + return lst; + + for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) + { + VIcon *v=*it; + + if(!filter || filter->isSet(v)) + { + if(v->show(vnode)) + { + lst << v->pixId_; + } + } + } + + return lst; +} + +//Create the pixmap containing all the relevant icons for the given node according to the filter. +int VIcon::pixmapNum(VNode *vnode,VParamSet *filter) +{ + if(!vnode) + return 0; + + int ret=0; + + for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) + { + VIcon *v=*it; + if(!filter || filter->isSet(v)) + { + if(v->show(vnode)) + { + ret++; + } + } + } + return ret; +} + + +QString VIcon::toolTip(VNode *vnode,VParamSet *filter) +{ + if(!filter || filter->isEmpty()) + return QString(); + + int iconSize=16; + QString txt; + + for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) + { + VIcon *v=*it; + + if(!filter || filter->isSet(v)) + { + if(v->show(vnode)) + { + if(txt.isEmpty()) + { + txt+="
    Icons:"; + } + + txt+=""; + } + } + } + + if(!txt.isEmpty()) + txt+="
    pixId_) + "\' width=\'" + + QString::number(iconSize) + "\' height=\'" + QString::number(iconSize) + "\'>" + v->shortDescription() + "
    "; + + return txt; +} + +QString VIcon::shortDescription() const +{ + QString v; + if(prop_) + v=prop_->param("shortDesc"); + + if(v.isEmpty()) + v=name(); + + return v; +} +void VIcon::names(std::vector& v) +{ + for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) + v.push_back(it->first); +} + +void VIcon::saveLastNames() +{ + lastNames_.clear(); + for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) + lastNames_.push_back(it->first); + + std::string iconFile = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "last_icons.txt"); + VSettings vs(iconFile); + vs.clear(); + vs.put("icons",lastNames_); + vs.write(); +} + +void VIcon::initLastNames() +{ + //It has to be called only once + assert(lastNames_.empty()); + std::string iconFile = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "last_icons.txt"); + VSettings vs(iconFile); + if(vs.read(false)) + vs.get("icons",lastNames_); +} + +void VIcon::load(VProperty* group) +{ + Q_FOREACH(VProperty* p,group->children()) + { + if(VIcon* obj=VIcon::find(p->strName())) + { + obj->setProperty(p); + obj->initPixmap(); + } + } +} + +static SimpleLoader loader("icon"); + + +//========================================================== +// Wait +//========================================================== + +//Task only +bool VWaitIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + return n->isFlagSet(ecf::Flag::WAIT); +} + +//========================================================== +// Rerun +//========================================================== + +//Task only +bool VRerunIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + node_ptr node=n->node(); + if(!node.get()) return false; + + if(Submittable* s = node->isSubmittable()) + { + return (s->try_no() > 1); + } + + return false; +} + +//========================================================== +// Message +//========================================================== + +//Node and server +bool VNodeLogIcon::show(VNode *n) +{ + if(!n) + return false; + + return n->isFlagSet(ecf::Flag::MESSAGE); +} + +//========================================================== +// Complete +//========================================================== + +//Task only +bool VCompleteIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + if(!n->node()) + return false; + + node_ptr node=n->node(); + if(!node.get()) return false; + + if(n->isDefaultStateComplete()) + return true; + + if(AstTop* t = node->completeAst()) + { + if(t->evaluate()) + return true; + } + return false; +} + +//========================================================== +// Date +//========================================================== + +//Node only? +bool VDateIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + node_ptr node=n->node(); + + if(!node.get()) return false; + + return (node->days().size() > 0 || node->dates().size() > 0); +} + +//========================================================== +// Time - hasTimeHolding in ecflowview +//========================================================== + +//Node only? +bool VTimeIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + node_ptr node=n->node(); + + if(TimeDepAttrs *attr = node->get_time_dep_attrs()) + return !attr->time_today_cron_is_free(); + + return false; +} + + +//========================================================== +// TimeFree - hasTime in ecflowview +//========================================================== + +//Node only? +bool VTimeFreeIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + if(timeIcon.show(n)) + return false; + + node_ptr node=n->node(); + if(node->timeVec().size() > 0 || + node->todayVec().size() > 0 || + node->crons().size() > 0) + { + return true; + } + return false; +} + +//========================================================== +// Zombie +//========================================================== + +//Node only? +bool VZombieIcon::show(VNode *n) +{ + if(!n) + return false; + + return n->isFlagSet(ecf::Flag::ZOMBIE); +} + +//========================================================== +// Late +//========================================================== + +//Node and server +bool VLateIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + return n->isFlagSet(ecf::Flag::LATE); +} + +//========================================================== +// Slow +//========================================================== + +//Server only +bool VSlowIcon::show(VNode *n) +{ + if(!n || !n->isServer()) + return false; + + return n->isFlagSet(ecf::Flag::LATE); +} + +//========================================================== +// Killed +//========================================================== + +//Node only? +bool VKilledIcon::show(VNode *n) +{ + if(!n || n->isServer()) + return false; + + return n->isFlagSet(ecf::Flag::KILLED); +} + +//========================================================== +// Migrated +//========================================================== + +bool VMigratedIcon::show(VNode *n) +{ + if(n && (n->isSuite() || n->isFamily())) + { + return n->isFlagSet(ecf::Flag::MIGRATED); + } + return false; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VIcon.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VIcon.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VIcon.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VIcon.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,61 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VICON_HPP_ +#define VICON_HPP_ + +#include +#include +#include +#include + +#include "VParam.hpp" + +#include +#include +#include + +class VNode; +class VParamSet; + +class VIcon : public VParam +{ +public: + explicit VIcon(const std::string& name); + virtual ~VIcon(); + + static std::vector filterItems(); + static QVariantList pixmapList(VNode *vnode,VParamSet *filter); + static int pixmapNum(VNode *vnode,VParamSet *filter); + static QString toolTip(VNode *vnode,VParamSet *filter); + static VIcon* find(const std::string& name); + static void names(std::vector&); + static const std::vector& lastNames() {return lastNames_;} + static void saveLastNames(); + static void initLastNames(); + + QPixmap pixmap(int size); + + //Called from VConfigLoader + static void load(VProperty* group); + +protected: + void initPixmap(); + virtual bool show(VNode*)=0; + QString shortDescription() const; + + int pixId_; + + static std::map items_; + static std::vector itemsVec_; + static std::vector lastNames_; +}; + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/Viewer.hpp ecflow-4.11.1/Viewer/ecflowUI/src/Viewer.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/Viewer.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/Viewer.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,25 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWER_HPP_ +#define VIEWER_HPP_ + +namespace Viewer +{ + enum ViewMode {TreeViewMode,TableViewMode,NoViewMode}; + enum ItemRole {InfoRole,ManualRole,ScriptRole,JobRole,OutputRole,WhyRole,TriggersRole,TimelineRole,VariableRole,EditRole,MessageRole}; + enum AttributeType {NoAttribute,LabelAttribute,MeterAttribute,EventAttribute,RepeatAttribute,TimeAttribute,DateAttribute, + TriggerAttribute,VarAttribute,GenVarAttribute,LateAttribute,LimitAttribute,LimiterAttribute}; + + + enum Param {UnknownParam,UnknownState,ActiveState,AbortedState,NoIcon,WaitIcon,RerunIcon,MessageIcon,CompleteIcon,TimeIcon,DateIcon,ZombieIcon,LateIcon}; +} + +#endif diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ViewerMain.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ViewerMain.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ViewerMain.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ViewerMain.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,178 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include +#include + +#include +#include +#include +#include +#include + +#include "File.hpp" +#include "DiagData.hpp" +#include "MainWindow.hpp" +#include "ServerHandler.hpp" +#include "MenuHandler.hpp" +#include "InfoPanelHandler.hpp" +#include "InputEventLog.hpp" +#include "DirectoryHandler.hpp" +#include "Highlighter.hpp" +#include "NodeQueryHandler.hpp" +#include "CustomCommandHandler.hpp" +#include "Palette.hpp" +#include "ServerList.hpp" +#include "VConfig.hpp" +#include "VIcon.hpp" +#include "VServerSettings.hpp" +#include "VSettingsLoader.hpp" +#include "SessionHandler.hpp" +#include "SessionDialog.hpp" +#include "UiLog.hpp" + +int main(int argc, char **argv) +{ + //if (argc != 3) + //{ + // std::cout << "Usage:" << std::endl; + // std::cout << argv[0] << " " << std::endl; + // return 1; + // + + //Init qt + QApplication app(argc, argv); + + app.setWindowIcon(QPixmap(":/viewer/logo_small.png")); + + QStringList styleLst=QStyleFactory::keys(); + + //Set the style + QString style="Plastique"; + if(styleLst.contains(style)) + { + app.setStyle(style); + } + else + { + style="Fusion"; + if(styleLst.contains(style)) + { + app.setStyle(style); + } + } + + //Set font size for application + //QFont font=app.font(); + //font.setPointSize(9); + //app.setFont(font); + + //Initialise the config and other paths + std::string exe(argv[0]); + DirectoryHandler::init(exe); // we need to tell the Directory class where we started from + + //Set the stylesheet + std::string styleSheetFileName="viewer.qss"; + std::string styleSheetPath=DirectoryHandler::concatenate(DirectoryHandler::etcDir(),styleSheetFileName); + + QFile shFile(QString::fromStdString(styleSheetPath)); + if(shFile.open(QIODevice::ReadOnly | QIODevice::Text)) + { + app.setStyleSheet(shFile.readAll()); + } + shFile.close(); + + //Load the configurable menu items + std::string menuFilename("ecflowview_menus.json"); + std::string menuPath = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), menuFilename); + MenuHandler::readMenuConfigFile(menuPath); + + //Load the custom context menu commands + CustomCommandHistoryHandler::instance()->init(); + CustomSavedCommandHandler::instance()->init(); + MenuHandler::refreshCustomMenuCommands(); + + //Load the info panel definition + std::string panelFile = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), "ecflowview_panels.json"); + InfoPanelHandler::instance()->init(panelFile); + + //Load the queries + std::string queryDir = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "query"); + NodeQueryHandler::instance()->init(queryDir); + + //Initialise the server list. This will update the server list + //from the central the system server list + ServerList::instance()->init(); + + // startup - via the session manager, or straight to the main window? + bool startMainWindow = true; + + //Initialise the session. We have to call this before VConfig::init() because + //some settings VConfig loads are session-dependent. + if (SessionHandler::requestStartupViaSessionManager()) + { + SessionDialog sessionDialog; + if (sessionDialog.exec() != QDialog::Accepted) + startMainWindow = false; + } + else + { + SessionHandler::setTemporarySessionIfReqested(); // user starts with -ts command-line switch? + } + + //Load the global configurations + VConfig::instance()->init(DirectoryHandler::etcDir()); + + //Import server settings from the previous viewer + if(DirectoryHandler::isFirstStartUp()) + { + VConfig::instance()->importSettings(); + VServerSettings::importRcFiles(); + } + + //Update objects with saved user settings (these are now stored in VConfig!!) + VSettingsLoader::process(); + + //Initialise highlighter + Highlighter::init(DirectoryHandler::concatenate(DirectoryHandler::etcDir(), + "ecflowview_highlighter.json")); + + //Initialise the system palette + Palette::load(DirectoryHandler::concatenate(DirectoryHandler::etcDir(), + "ecflowview_palette.json")); + + //Initialise the list containing all the icon names existed on last exit + VIcon::initLastNames(); + + //Start the GUI + if (startMainWindow) + { + //Build the GUI + MainWindow::init(); + + //Show all the windows + MainWindow::showWindows(); + + //Start input event logging + InputEventLog::instance()->start(); + + //Enable (daily) truncation for ui log + UiLog::enableTruncation(); + + //Load extra diagnostic data + DiagData::instance()->load(); + + return app.exec(); + } + else + { + return 0; // user quit from within the session manager + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/viewer.qrc ecflow-4.11.1/Viewer/ecflowUI/src/viewer.qrc --- ecflow-4.9.0/Viewer/ecflowUI/src/viewer.qrc 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/viewer.qrc 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,133 @@ + + + ../images/goto_line.svg + ../images/dock_float.svg + ../images/dock_close.svg + ../images/configure.svg + ../images/reset.svg + ../images/search.svg + ../images/info.svg + ../images/add_info.svg + ../images/add_table.svg + ../images/add_tree.svg + ../images/icon_calendar.svg + ../images/icon_clock.svg + ../images/icon_clock_free.svg + ../images/icon_complete.svg + ../images/icon_killed.svg + ../images/icon_late.svg + ../images/icon_children_hidden.svg + ../images/icon_node_log.svg + ../images/icon_rerun.svg + ../images/icon_slow.svg + ../images/icon_waiting.svg + ../images/icon_zombie.svg + ../images/arrow_up.svg + ../images/arrow_down.svg + ../images/add.svg + ../images/configure.svg + ../images/exit.svg + ../images/add_tab.svg + ../images/add_variable_column.svg + ../images/attribute.svg + ../images/chain.svg + ../images/chain_open.svg + ../images/chain_closed.svg + ../images/clear_left.svg + ../images/close.svg + ../images/close_grey.svg + ../images/close_red.svg + ../images/case_sensitive.svg + ../images/cogwheel.svg + ../images/cogwheel_blue.svg + ../images/colour.svg + ../images/copy_path.svg + ../images/dependency.svg + ../images/dependency_detail.svg + ../images/diag.svg + ../images/directory_arrow.svg + ../images/dock_add_variable_column.svg + ../images/dock_chain_closed.svg + ../images/dock_chain_open.svg + ../images/dock_config.svg + ../images/dock_dependency.svg + ../images/dock_max.svg + ../images/dock_max_disabled.svg + ../images/dock_menu_indicator.png + ../images/dock_restore.svg + ../images/down_arrow.svg + ../images/drawer_open.svg + ../images/drawer_close.svg + ../images/edit.svg + ../images/editcopy.svg + ../images/editpaste.svg + ../images/error.svg + ../images/expression.svg + ../images/favourite.svg + ../images/favourite_empty.svg + ../images/filesave.svg + ../images/filesaveas.svg + ../images/filter_decor.svg + ../images/filter_decor_green.svg + ../images/filter_decor_red.svg + ../images/filter_edit.svg + ../images/filter.svg + ../images/filter_match.svg + ../images/filter_no_match.svg + ../images/font.svg + ../images/fontsize_down.svg + ../images/fontsize_up.svg + ../images/genvar.svg + ../images/genvar_shadow.svg + ../images/grey_info.svg + ../images/overview.svg + ../images/job.svg + ../images/large_file_search.svg + ../images/log_info.svg + ../images/log_error.svg + ../images/logo.png + ../images/logo_small.png + ../images/manage_server.svg + ../images/manual.svg + ../images/menu.svg + ../images/menu_arrow_down.svg + ../images/monitor.svg + ../images/node_log.svg + ../images/notification.svg + ../images/output.svg + ../images/padlock.svg + ../images/path_arrow.svg + ../images/reload.svg + ../images/reload_green.svg + ../images/reload_one.svg + ../images/remove.svg + ../images/rescue.svg + ../images/reset_to_default.svg + ../images/script.svg + ../images/search_decor.svg + ../images/select_all.svg + ../images/server.svg + ../images/server_log.svg + ../images/show_shadowed.svg + ../images/spinning_wheel.gif + ../images/splash_screen.png + ../images/status.svg + ../images/system.svg + ../images/submit.svg + ../images/submit_round.svg + ../images/sync.svg + ../images/sync_black.svg + ../images/terminate.svg + ../images/tip.svg + ../images/trigger.svg + ../images/trigger_left_arrow.svg + ../images/trigger_right_arrow.svg + ../images/unknown.svg + ../images/unselect_all.svg + ../images/variable.svg + ../images/warning.svg + ../images/why.svg + ../images/zombie.svg + trigger.css + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ViewerUtil.cpp ecflow-4.11.1/Viewer/ecflowUI/src/ViewerUtil.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ViewerUtil.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ViewerUtil.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,137 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "ViewerUtil.hpp" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void ViewerUtil::initComboBox(QSettings& settings,QString key,QComboBox* cb) +{ + Q_ASSERT(cb); + QString txt=settings.value(key).toString(); + for(int i=0; i < cb->count(); i++) + { + if(cb->itemText(i) == txt) + { + cb->setCurrentIndex(i); + return; + } + } + + if(cb->currentIndex() == -1) + cb->setCurrentIndex(0); +} + +void ViewerUtil::initComboBoxByData(QString dataValue,QComboBox* cb) +{ + Q_ASSERT(cb); + for(int i=0; i < cb->count(); i++) + { + if(cb->itemData(i).toString() == dataValue) + { + cb->setCurrentIndex(i); + return; + } + } + + if(cb->currentIndex() == -1) + cb->setCurrentIndex(0); +} + +void ViewerUtil::initTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree) +{ + Q_ASSERT(tree); + + QStringList dataColumns=settings.value(key).toStringList(); + for(int i=0; i < tree->model()->columnCount()-1 && i < dataColumns.size(); i++) + { + tree->setColumnWidth(i,dataColumns[i].toInt()); + } +} + +void ViewerUtil::saveTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree) +{ + QStringList dataColumns; + for(int i=0; i < tree->model()->columnCount()-1; i++) + { + dataColumns << QString::number(tree->columnWidth(i)); + } + settings.setValue(key,dataColumns); +} + + +void ViewerUtil::initStacked(QSettings& settings,QString key,QStackedWidget *stacked) +{ + Q_ASSERT(stacked); + + int v=settings.value(key).toInt(); + if(v >= 0 && v < stacked->count()) + stacked->setCurrentIndex(v); +} + +void ViewerUtil::initButtonGroup(QSettings& settings,QString key,QButtonGroup *bg) +{ + Q_ASSERT(bg); + + int v=settings.value(key).toInt(); + if(v >= 0 && v < bg->buttons().count()) + { + bg->buttons().at(v)->setChecked(true); + bg->buttons().at(v)->click(); + } +} + +void ViewerUtil::initCheckableAction(QSettings& settings,QString key,QAction *ac) +{ + Q_ASSERT(ac); + + if(settings.contains(key)) + { + ac->setChecked(settings.value(key).toBool()); + } +} + +QBrush ViewerUtil::lineEditGreenBg() +{ + //return lineEditBg(QColor(189,239,205)); + return lineEditBg(QColor(210,255,224)); +} + +QBrush ViewerUtil::lineEditRedBg() +{ + return lineEditBg(QColor(234,215,214)); +} + +QBrush ViewerUtil::lineEditBg(QColor col) +{ + QLinearGradient grad; + grad.setCoordinateMode(QGradient::ObjectBoundingMode); + grad.setStart(0,0); + grad.setFinalStop(0,1); + + grad.setColorAt(0,col); + grad.setColorAt(0.1,col.lighter(105)); + grad.setColorAt(0.1,col.lighter(108)); + grad.setColorAt(0.9,col.lighter(108)); + grad.setColorAt(0.9,col.lighter(105)); + grad.setColorAt(1,col); + return QBrush(grad); +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/ViewerUtil.hpp ecflow-4.11.1/Viewer/ecflowUI/src/ViewerUtil.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/ViewerUtil.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/ViewerUtil.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,42 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VIEWERUTIL_HPP +#define VIEWERUTIL_HPP + +#include +#include +#include +#include + +class QAction; +class QButtonGroup; +class QComboBox; +class QStackedWidget; +class QTabWidget; +class QTreeView; + +class ViewerUtil +{ +public: + static void initComboBox(QSettings&,QString key,QComboBox* cb); + static void initComboBoxByData(QString dataValue,QComboBox* cb); + static void initTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree); + static void saveTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree); + static void initStacked(QSettings& settings,QString key,QStackedWidget *stacked); + static void initButtonGroup(QSettings& settings,QString key,QButtonGroup *bg); + static void initCheckableAction(QSettings& settings,QString key,QAction *ac); + static QBrush lineEditGreenBg(); + static QBrush lineEditRedBg(); + static QBrush lineEditBg(QColor col); +}; + +#endif // VIEWERUTIL_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VInfo.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VInfo.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VInfo.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VInfo.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,492 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VInfo.hpp" + +#include "ServerHandler.hpp" +#include "UiLog.hpp" +#include "VAttribute.hpp" +#include "VAttributeType.hpp" +#include "VItemPathParser.hpp" +#include "VNode.hpp" + +#include + +//#define _UI_VINFO_DEBUG + +//======================================== +// +// VInfo +// +//======================================== + +VInfo::VInfo(ServerHandler* server,VNode* node,VAttribute* attr) : + server_(server), + node_(node), + attr_(attr) +{ + if(server_) + server_->addServerObserver(this); +} + +VInfo::~VInfo() +{ +#ifdef _UI_VINFO_DEBUG + UiLog().dbg() << "VInfo::~VInfo() --> " << this; +#endif + if(server_) + server_->removeServerObserver(this); + + for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) + (*it)->notifyDelete(this); + +#ifdef _UI_VINFO_DEBUG + UiLog().dbg() << "<-- ~VInfo()"; +#endif +} + +void VInfo::notifyServerDelete(ServerHandler* /*server*/) +{ + server_=0; + node_=0; + attr_=0; + + //This function is called from the server destructor. We do not remove this object from the ServerObservers + dataLost(); +} + +void VInfo::dataLost() +{ + std::vector obsTmp=observers_; + observers_.clear(); + + for(std::vector::iterator it=obsTmp.begin(); it != obsTmp.end(); ++it) + { + VInfoObserver* o=*it; + o->notifyDataLost(this); + } + + attr_=0; +} + +void VInfo::notifyBeginServerClear(ServerHandler* server) +{ + assert(server_==server); + node_=0; + attr_=0; +} + +void VInfo::notifyEndServerClear(ServerHandler* server) +{ + assert(server_==server); + node_=0; + attr_=0; +} + +void VInfo::notifyEndServerScan(ServerHandler* server) +{ + assert(server_==server); + regainData(); +} + +void VInfo::regainData() +{ + if(!server_) + { + dataLost(); + return; + } + + if(!node_) + { + if(isServer()) + { + node_=server_->vRoot(); + return; + } + else if(isNode()) + { + VItemPathParser p(storedPath_); + if(p.itemType() == VItemPathParser::NodeType) + { + node_=server_->vRoot()->find(p.node()); + if(node_) + return; + } + if(!node_) + { + dataLost(); + return; + } + } + } + + if(isAttribute()) + { + VItemPathParser p(storedPath_); + if(p.itemType() == VItemPathParser::AttributeType) + { + if(!node_) + { + node_=server_->vRoot()->find(p.node()); + } + if(node_) + { + attr_=node_->findAttribute(p.type(),p.attribute()); + } + if(!node_ || !attr_) + { + dataLost(); + } + } + } +} + +std::string VInfo::storedNodePath() const +{ + VItemPathParser p(storedPath_); + if(p.itemType() == VItemPathParser::ServerType) + return "/"; + else + return p.node(); +} + +void VInfo::addObserver(VInfoObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it == observers_.end()) + observers_.push_back(o); +} + +void VInfo::removeObserver(VInfoObserver* o) +{ + std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); + if(it != observers_.end()) + observers_.erase(it); +} + +bool VInfo::operator ==(const VInfo& other) +{ + if(server_ == other.server_ && node_ == other.node_ && + storedPath_ == other.storedPath_) + { + if((!attr_ && other.attr_) || + (attr_ && !other.attr_)) + return false; + + else if(attr_ && other.attr_) + { + return (attr_->type() == other.attr_->type() && + attr_->data() == other.attr_->data()); + } + else + return true; + } + return false; +} + +VInfo_ptr VInfo::createParent(VInfo_ptr info) +{ + if(!info) + return VInfo_ptr(); + + if(info->isServer()) + return info; + else if(info->isNode()) + { + return VInfoServer::create(info->server()); + } + else if(info->isAttribute()) + { + return VInfoNode::create(info->node()); + } + + return VInfo_ptr(); +} + +VInfo_ptr VInfo::createFromPath(ServerHandler* s,const std::string& path) +{ + if(!s || path.empty()) + return VInfo_ptr(); + + VItemPathParser p(path); + + if(p.itemType() == VItemPathParser::ServerType) + { + return VInfoServer::create(s); + } + else if(p.itemType() == VItemPathParser::NodeType) + { + if(VNode* n=s->vRoot()->find(p.node())) + return VInfoNode::create(n); + + } + else if(p.itemType() == VItemPathParser::AttributeType) + { + if(VNode* n=s->vRoot()->find(p.node())) + { + if(VAttribute* a=n->findAttribute(p.type(),p.attribute())) + { + return VInfoAttribute::create(a); + } + } + } + + return VInfo_ptr(); +} + +VInfo_ptr VInfo::createFromPath(const std::string& path) +{ + if(path.empty()) + return VInfo_ptr(); + + VItemPathParser p(path); + if(!p.server().empty()) + { + if(ServerHandler* s=ServerHandler::find(p.server())) + { + return createFromPath(s,path); + } + } + return VInfo_ptr(); +} + +VInfo_ptr VInfo::createFromItem(VItem* item) +{ + if(!item) + return VInfo_ptr(); + + if(VServer* s=item->isServer()) + { + return VInfoServer::create(s->server()); + } + else if(VNode* n=item->isNode()) + { + return VInfoNode::create(n); + } + else if(VAttribute* a=item->isAttribute()) + { + return VInfoAttribute::create(a); + } + + return VInfo_ptr(); +} + +//========================================= +// +// VInfoServer +// +//========================================= + +VInfoServer::VInfoServer(ServerHandler *server) : VInfo(server,NULL) +{ + if(server_) + { + node_=server_->vRoot(); + storedPath_=VItemPathParser::encodeWithServer(server_->name(),"/","server"); + } +} + +VInfo_ptr VInfoServer::create(ServerHandler *server) +{ + return VInfo_ptr(new VInfoServer(server)); +} + +bool VInfoServer::hasData() const +{ + return server_ != 0; +} + +void VInfoServer::accept(VInfoVisitor* v) +{ + v->visit(this); +} + +std::string VInfoServer::name() +{ + return (server_)?(server_->name()):(std::string()); +} + +std::string VInfoServer::path() +{ + return name() + "://"; +} + +VItem* VInfoServer::item() const +{ + return node_; +} + +//========================================= +// +// VInfoNode +// +//========================================= + + +VInfoNode::VInfoNode(ServerHandler* server,VNode* node) : VInfo(server,node) +{ + if(node_) + { + assert(server_); + storedPath_=VItemPathParser::encodeWithServer(server_->name(),node_->absNodePath(),"node"); + } +} + +VInfo_ptr VInfoNode::create(VNode *node) +{ + ServerHandler* server=NULL; + if(node) + { + server=node->server(); + } + return VInfo_ptr(new VInfoNode(server,node)); +} + +bool VInfoNode::hasData() const +{ + return server_ != 0 && node_ != 0; +} + +void VInfoNode::accept(VInfoVisitor* v) +{ + v->visit(this); +} + +std::string VInfoNode::name() +{ + if(node_ && node_->node()) + return node_->strName(); + + return std::string(); +} + +std::string VInfoNode::path() +{ + std::string p; + if(server_) + p=server_->name(); + + if(node_ && node_->node()) + p+=":/" + node_->absNodePath(); + + return p; +} + +std::string VInfoNode::serverAlias() +{ + std::string p; + if(server_) + p = server_->name(); + return p; +} + +std::string VInfoNode::nodePath() +{ + std::string p; + if(node_ && node_->node()) + p = node_->absNodePath(); + return p; +} + +std::string VInfoNode::relativePath() +{ + std::string p; + if(node_ && node_->node()) + p = node_->absNodePath(); + return p; +} + +VItem* VInfoNode::item() const +{ + return node_; +} + +//========================================= +// +// VInfoAttribute +// +//========================================= + + +VInfoAttribute::VInfoAttribute(ServerHandler* server,VNode* node,VAttribute* attr) : + VInfo(server,node,attr) +{ + if(attr_) + { + assert(server_); + storedPath_=VItemPathParser::encodeWithServer(server_->name(), + attr_->fullPath(),attr_->typeName()); + } +} + +VInfoAttribute::~VInfoAttribute() +{ +} + +bool VInfoAttribute::hasData() const +{ + return server_ != 0 && node_ != 0 && attr_ != 0; +} + +void VInfoAttribute::accept(VInfoVisitor* v) +{ + v->visit(this); +} + +VInfo_ptr VInfoAttribute::create(VAttribute* att) +{ + ServerHandler* server=NULL; + VNode* node=att->parent(); + if(node) + { + server=node->server(); + } + + return VInfo_ptr(new VInfoAttribute(server,node,att)); +} + +std::string VInfoAttribute::path() +{ + std::string p; + if(server_) + p=server_->name(); + if(attr_) + p+=":/" + attr_->fullPath(); + + return p; +} + +std::string VInfoAttribute::nodePath() +{ + std::string p; + if(node_ && node_->node()) + p = node_->absNodePath(); + return p; +} + +std::string VInfoAttribute::name() +{ + return (attr_)?attr_->strName():std::string(); +} + +VItem* VInfoAttribute::item() const +{ + return attr_; +} + + + + + + + + + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VInfo.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VInfo.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VInfo.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VInfo.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,195 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VINFO_HPP_ +#define VINFO_HPP_ + +#include +#include +#include + +#include +#include + +#include "ServerObserver.hpp" +#include "Variable.hpp" + +class ServerHandler; +class VAttribute; +class VNode; +class VItem; + +class VInfoObserver; +class VInfoVisitor; + +class VInfo; +typedef boost::shared_ptr VInfo_ptr; + +//============================================================================== +// For each selected item in any of the views a new VInfo object is created. +// This class offers the same interface to access information about any selected +// items: servers, nodes, attributes. The concrete implementation of +// these access methods are done in subclasses derived from VInfo. +// +// VInfo is regarded as a temporary object. We only need it while the selection +// is used in breadcrumbs, info panels or other widgets outside the main views. +//============================================================================== + +class VInfo : public ServerObserver +{ +public: + virtual ~VInfo(); + + virtual bool isServer() {return false;} + virtual bool isNode() {return false;} + virtual bool isAttribute() {return false;} + virtual bool isEmpty() {return true;} + virtual bool hasData() const=0; + + ServerHandler* server() const {return server_;} + VNode* node() const {return node_;} + VAttribute* attribute() const {return attr_;} + virtual VItem* item() const=0; + + virtual std::string name()=0; + virtual std::string path()=0; + virtual std::string serverAlias() {return "";} + virtual std::string relativePath() {return "";} + virtual std::string nodePath()=0; + std::string storedNodePath() const; + const std::string& storedPath() const {return storedPath_;} + + virtual void accept(VInfoVisitor*)=0; + + void regainData(); + void addObserver(VInfoObserver*); + void removeObserver(VInfoObserver*); + + //Form ServerObserver + void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} + void notifyServerDelete(ServerHandler* server); + void notifyBeginServerClear(ServerHandler* server); + void notifyEndServerClear(ServerHandler* server); + void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} + void notifyEndServerScan(ServerHandler* server); + void notifyServerConnectState(ServerHandler* server) {} + void notifyServerActivityChanged(ServerHandler* server) {} + void notifyServerSuiteFilterChanged(ServerHandler* server) {} + + bool operator ==(const VInfo&); + + static VInfo_ptr createParent(VInfo_ptr); + static VInfo_ptr createFromPath(ServerHandler*,const std::string&); + static VInfo_ptr createFromPath(const std::string& path); + static VInfo_ptr createFromItem(VItem*); + +protected: + VInfo(ServerHandler* server,VNode* node,VAttribute* attr=0); + void dataLost(); + + mutable ServerHandler* server_; + mutable VNode* node_; + mutable VAttribute* attr_; + mutable std::string storedPath_; + + std::vector observers_; +}; + +// Implements the info object for server selections +class VInfoServer : public VInfo, public boost::enable_shared_from_this +{ +public: + bool isServer() {return true;} + bool isEmpty() {return false;} + bool hasData() const; + VItem* item() const; + + void accept(VInfoVisitor*); + std::string name(); + std::string path(); + std::string nodePath() {return "/";} + static VInfo_ptr create(ServerHandler*); + +protected: + explicit VInfoServer(ServerHandler*); +}; + + +// Implements the info object for node selections +class VInfoNode: public VInfo, public boost::enable_shared_from_this +{ +public: + bool isNode() {return true;} + bool isEmpty() {return false;} + bool hasData() const; + void accept(VInfoVisitor*); + std::string path(); + std::string name(); + std::string serverAlias(); + std::string nodePath(); + std::string relativePath(); + VItem* item() const; + + static VInfo_ptr create(VNode*); + +protected: + VInfoNode(ServerHandler*,VNode*); +}; + + +// Implements the info base class for attribute selections +class VInfoAttribute: public VInfo, public boost::enable_shared_from_this +{ +public: + ~VInfoAttribute(); + VAttribute* attribute() const {return attr_;} + bool isAttribute() {return true;} + bool isEmpty() {return false;} + bool hasData() const; + void accept(VInfoVisitor*); + std::string name(); + std::string path(); + std::string nodePath(); + VItem* item() const; + + static VInfo_ptr create(VAttribute*); + +protected: + VInfoAttribute(ServerHandler*,VNode*,VAttribute*); +}; + +typedef boost::shared_ptr VInfoServer_ptr; +typedef boost::shared_ptr VInfoNode_ptr; +typedef boost::shared_ptr VInfoAttribute_ptr; + +class VInfoVisitor +{ +public: + VInfoVisitor() {} + virtual ~VInfoVisitor() {} + + virtual void visit(VInfoServer*)=0; + virtual void visit(VInfoNode*)=0; + virtual void visit(VInfoAttribute*)=0; + +}; + +class VInfoObserver +{ +public: + VInfoObserver() {} + virtual ~VInfoObserver() {} + + virtual void notifyDataLost(VInfo*)=0; + virtual void notifyDelete(VInfo*)=0; +}; + +#endif + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VItem.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VItem.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VItem.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VItem.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,28 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VItem.hpp" +#include "VNode.hpp" + +bool VItem::isAncestor(const VItem* n) const +{ + if(n == this) + return true; + + VNode* nd=parent(); + while(nd) + { + if(const_cast(n) == nd) + return true; + + nd=nd->parent(); + } + return false; +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VItem.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VItem.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VItem.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VItem.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,74 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VITEM_HPP_ +#define VITEM_HPP_ + +#include +#include + +class ServerHandler; +class VNode; +class VServer; +class VSuiteNode; +class VFamilyNode; +class VAliasNode; +class VTaskNode; +class VAttribute; +class VItemVisitor; + +class VItem +{ +public: + VItem(VNode* parent) : parent_(parent) {} + virtual ~VItem() {} + + VNode* parent() const {return parent_;} + virtual VServer* isServer() const {return NULL;} + virtual VNode* isNode() const {return NULL;} + virtual VSuiteNode* isSuite() const {return NULL;} + virtual VFamilyNode* isFamily() const {return NULL;} + virtual VTaskNode* isTask() const {return NULL;} + virtual VAliasNode* isAlias() const {return NULL;} + virtual VAttribute* isAttribute() const {return NULL;} + + virtual ServerHandler* server() const=0; + virtual VServer* root() const=0; + virtual bool isTopLevel() const {return false;} + virtual std::string strName() const=0; + virtual QString name() const=0; + virtual const std::string& typeName() const=0; + virtual std::string fullPath() const=0; + virtual bool sameContents(VItem*) const=0; + virtual bool isAncestor(const VItem*) const; + virtual QString nodeMenuMode() const {return QString();} + virtual QString defStatusNodeMenuMode() const {return QString();} + +protected: + VNode* parent_; +}; + + +#if 0 +class VItemVisitor +{ +public: + VItemVisitor() {} + virtual ~VItemVisitor() {} + + virtual void visit(VServer*) {} + virtual void visit(VNode*) {} + virtual void visit(VAttribute*) {} +}; +#endif + + +#endif // VITEM_HPP_ + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VItemPathParser.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VItemPathParser.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VItemPathParser.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VItemPathParser.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,163 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VItemPathParser.hpp" +#include "VAttributeType.hpp" + +//format: +// [type]server://nodepath:atttibutename + +VItemPathParser::VItemPathParser(const std::string& path,PathFormat format) : itemType_(NoType) +{ + if(path.empty()) + return; + + size_t pos; + std::string p=path; + + if(format== DefaultFormat) + { + if(p.find("[") == 0) + { + pos=p.find("]"); + if(pos != std::string::npos) + { + type_=path.substr(1,pos-1); + p=path.substr(pos+1); + } + else + return; + } + + pos=p.find("://"); + + if(pos != std::string::npos) + { + server_=p.substr(0,pos); + p=p.substr(pos+2); + } + + if(p.size() == 1 && p.find("/") == 0) + { + itemType_=ServerType; + return; + } + + //Here we suppose that the node name cannot contain ":" + pos=p.find_first_of(":"); + if(pos != std::string::npos) + { + node_=p.substr(0,pos); + attribute_=p.substr(pos+1); + itemType_=AttributeType; + + if(type_ == "gen-variable") + type_="genvar"; + else if(type_ == "user-variable") + type_="var"; + + if(!VAttributeType::find(type_)) + { + itemType_=NoType; + type_.clear(); + } + } + else + { + node_=p; + itemType_=NodeType; + } + } + else if(format == DiagFormat) + { + pos=p.find(":/"); + if(pos != std::string::npos) + { + server_=p.substr(0,pos); + extractHostPort(server_); + if(p.length() > pos+1) + { + node_=p.substr(pos+1); + itemType_=NodeType; + } + else + { + itemType_=ServerType; + } + } + } +} + +std::string VItemPathParser::encode(const std::string& path,const std::string& type) +{ + if(type.empty()) + return path; + + return "[" + type + "]" + path; +} + +std::string VItemPathParser::encodeWithServer(const std::string& server,const std::string& path,const std::string& type) +{ + if(type.empty()) + return path; + + return "[" + type + "]" + server + ":/" + path; +} + +std::string VItemPathParser::encodeAttribute(const std::string& parentPath,const std::string& attrName,const std::string& attrType) +{ + if(attrType.empty()) + return std::string(); + + VItemPathParser parent(parentPath); + + return "[" + attrType + "]" + parent.server() + ":/" + parent.node() + ":" + attrName; +} + +std::string VItemPathParser::parent() const +{ + switch(itemType_) + { + case ServerType: + return std::string(); + break; + case NodeType: + { + std::size_t pos=node_.find_last_of("/"); + if(pos != std::string::npos) + { + std::string n=node_.substr(0,pos); + if(!n.empty()) + return encodeWithServer(server_,n,type_); + else + return encodeWithServer(server_,"/","server"); + } + } + break; + case AttributeType: + return encodeWithServer(server_,node_,"node"); + break; + default: + break; + } + + return std::string(); +} + +void VItemPathParser::extractHostPort(const std::string& server) +{ + size_t pos=server.find("@"); + if(pos != std::string::npos) + { + host_=server.substr(0,pos); + if(server.length() > pos +1) + port_=server.substr(pos+1); + } +} diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VItemPathParser.hpp ecflow-4.11.1/Viewer/ecflowUI/src/VItemPathParser.hpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VItemPathParser.hpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VItemPathParser.hpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,55 @@ +//============================================================================ +// Copyright 2009-2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#ifndef VITEMPATHPARSER_HPP +#define VITEMPATHPARSER_HPP + +#include + +class VItemPathParser +{ +public: + enum PathFormat {DefaultFormat, // [type]server://nodepath:atttibutename + DiagFormat //host@port:/nodepath + }; + + VItemPathParser(const std::string& path,PathFormat format=DefaultFormat); + + enum ItemType {ServerType,NodeType,AttributeType,NoType}; + + ItemType itemType() const {return itemType_;} + const std::string type() const {return type_;} + const std::string server() const {return server_;} + const std::string host() const {return host_;} + const std::string port() const {return port_;} + const std::string node() const {return node_;} + const std::string attribute() const {return attribute_;} + std::string parent() const; + + static std::string encode(const std::string& path,const std::string& type); + static std::string encodeWithServer(const std::string& server,const std::string& path, + const std::string& type); + static std::string encodeAttribute(const std::string& parentPath,const std::string& attrName,const std::string& attrType); + +protected: + void extractHostPort(const std::string& server); + + ItemType itemType_; + std::string type_; + std::string server_; + std::string host_; + std::string port_; + std::string node_; + std::string attribute_; +}; + + +#endif // VITEMPATHPARSER_HPP + diff -Nru ecflow-4.9.0/Viewer/ecflowUI/src/VLabelAttr.cpp ecflow-4.11.1/Viewer/ecflowUI/src/VLabelAttr.cpp --- ecflow-4.9.0/Viewer/ecflowUI/src/VLabelAttr.cpp 1970-01-01 00:00:00.000000000 +0000 +++ ecflow-4.11.1/Viewer/ecflowUI/src/VLabelAttr.cpp 2018-10-23 11:41:34.000000000 +0000 @@ -0,0 +1,139 @@ +//============================================================================ +// Copyright 2017 ECMWF. +// This software is licensed under the terms of the Apache Licence version 2.0 +// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. +// In applying this licence, ECMWF does not waive the privileges and immunities +// granted to it by virtue of its status as an intergovernmental organisation +// nor does it submit to any jurisdiction. +// +//============================================================================ + +#include "VLabelAttr.hpp" +#include "VAttributeType.hpp" +#include "VNode.hpp" + +#include "NodeAttr.hpp" + +//================================ +// VLabelAttrType +//================================ + +VLabelAttrType::VLabelAttrType() : VAttributeType("label") +{ + dataCount_=3; + searchKeyToData_["label_name"]=NameIndex; + searchKeyToData_["label_value"]=ValueIndex; + searchKeyToData_["name"]=NameIndex; + scanProc_=VLabelAttr::scan; +} + +QString VLabelAttrType::toolTip(QStringList d) const +{ + QString t="Type: Label
    "; + if(d.count() == dataCount_) + { + t+="Name: " + d[NameIndex] + "
    "; + t+="Value: " + d[ValueIndex]; + } + return t; +} + +QString VLabelAttrType::definition(QStringList d) const +{ + QString t="label"; + if(d.count() == dataCount_) + { + t+=" " + d[NameIndex] + " '" + d[ValueIndex] + "'"; + } + return t; +} + +void VLabelAttrType::encode(const Label& label,QStringList& data,bool firstLine) const +{ + std::string val=label.new_value(); + if(val.empty() || val == " ") + { + val=label.value(); + } + + if(firstLine) + { + std::size_t pos=val.find("\n"); + if(pos != std::string::npos) + { + val=val.substr(0,pos); + } + } + + data << qName_ << + QString::fromStdString(label.name()) << + QString::fromStdString(val); +} + +//===================================================== +// +// VLabelAttr +// +//===================================================== + +VLabelAttr::VLabelAttr(VNode *parent,const Label& label, int index) : VAttribute(parent,index) +{ + //name_=label.name(); +} + +int VLabelAttr::lineNum() const +{ + if(parent_->node_) + { + const std::vector
      ")) - msg+="

      "; - - msg+="warning: " + Viewer::formatText(warning,QColor(196,103,36)) + "
      "; - } - - if(!item->command().empty()) - { - QString cmdStr=QString::fromStdString(item->command()); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - cmdStr=cmdStr.toHtmlEscaped(); -#else - cmdStr=Qt::escape(cmdStr); -#endif - if(!warning.isEmpty()) - msg+="
      "; - else if(!msg.contains("
        ")) - msg+="

        "; - - msg+="command: " + Viewer::formatText(cmdStr,QColor(41,78,126)) + ""; - msg+="
        "; - } - - QMessageBox msgBox; - msgBox.setText(msg); - msgBox.setTextFormat(Qt::RichText); - msgBox.setIcon(QMessageBox::Question); - msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - if (msgBox.exec() == QMessageBox::Cancel) - { - ok=false; - } - } - - if(ok) - CommandHandler::run(filteredNodes,item->command()); - - if (customCommandDialog) - delete customCommandDialog; - } - } - -/* - QMenu *menu=new QMenu(parent_); - - QList acLst; - - QAction *ac=new QAction("Requeue",parent_); - acLst << ac; - - ac=new QAction("Submit",parent_); - acLst << ac; - - ac=new QAction("Set as root",parent_); - acLst << ac; - - if(QAction* res=QMenu::exec(acLst,pos,0,parent_)) - { - - if(res->iconText() == "Set as root") - { - emit viewCommand(filteredNodes,"set_as_root"); - } - else - ServerHandler::command(filteredNodes,res->iconText().toStdString()); - } - - delete menu; -*/ -} diff -Nru ecflow-4.9.0/Viewer/src/ActionHandler.hpp ecflow-4.11.1/Viewer/src/ActionHandler.hpp --- ecflow-4.9.0/Viewer/src/ActionHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ActionHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef ACTIONHANDLER_HPP_ -#define ACTIONHANDLER_HPP_ - -#include -#include - -#include - -#include "VInfo.hpp" - -class QWidget; -class Node; -class ServerHandler; - -class ActionHandler : public QObject -{ -Q_OBJECT -public: - explicit ActionHandler(QObject*,QWidget* menuParent); - - void contextMenu(std::vector,QPoint); - bool actionHandler(); - -Q_SIGNALS: - void viewCommand(VInfo_ptr,QString); - void infoPanelCommand(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - -protected: - QObject *actionSender_; - QWidget *menuParent_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/Animation.cpp ecflow-4.11.1/Viewer/src/Animation.cpp --- ecflow-4.9.0/Viewer/src/Animation.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Animation.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "Animation.hpp" -#include "ServerHandler.hpp" -#include "VNode.hpp" - -#include - -//===================================================== -// -// Animation -// -//===================================================== - -Animation::Animation(QWidget *view,Type type) : - view_(view), - type_(type) -{ - if(type_ == ServerLoadType) - setFileName(":/viewer/spinning_wheel.gif"); - - setScaledSize(QSize(12,12)); - - connect(this,SIGNAL(frameChanged(int)), - this,SLOT(renderFrame(int))); - - connect(this,SIGNAL(repaintRequest(Animation*)), - view_,SLOT(slotRepaint(Animation*))); -} - -void Animation::addTarget(VNode *n) -{ - Q_ASSERT(n); - if(!targets_.contains(n)) - targets_ << n; - - ServerHandler* s=n->server(); - Q_ASSERT(s); - s->addServerObserver(this); - - start(); -} - -void Animation::removeTarget(VNode* n) -{ - targets_.removeAll(n); - - if(targets_.isEmpty()) - QMovie::stop(); -} - -void Animation::renderFrame(int frame) -{ - if(targets_.isEmpty()) - QMovie::stop(); - - Q_EMIT(repaintRequest(this)); -} - -void Animation::notifyServerDelete(ServerHandler* server) -{ - removeTarget(server->vRoot()); - server->removeServerObserver(this); - //TODO: make it work for nodes as well -} - -void Animation::notifyBeginServerClear(ServerHandler* /*server*/) -{ - //TODO: make it work for nodes -} - -//===================================================== -// -// AnimationHandler -// -//===================================================== - -AnimationHandler::AnimationHandler(QWidget* view) : view_(view) -{ - -} - -AnimationHandler::~AnimationHandler() -{ - Q_FOREACH(Animation* an,items_) - { - delete an; - } -} - -Animation* AnimationHandler::find(Animation::Type type, bool makeIt) -{ - QMap::iterator it=items_.find(type); - if(it != items_.end()) - { - return it.value(); - } - else if(makeIt) - { - Animation* an=new Animation(view_,type); - items_[type]=an; - return an; - } - - return 0; -} diff -Nru ecflow-4.9.0/Viewer/src/Animation.hpp ecflow-4.11.1/Viewer/src/Animation.hpp --- ecflow-4.9.0/Viewer/src/Animation.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Animation.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef ANIMATION_HPP_ -#define ANIMATION_HPP_ - -#include -#include -#include - -#include "ServerObserver.hpp" -#include "VNode.hpp" - -class VNode; - -class Animation : public QMovie, public ServerObserver -{ -Q_OBJECT - -public: - enum Type {ServerLoadType}; - Animation(QWidget*,Type); - - void addTarget(VNode*); - void removeTarget(VNode*); - QList targets() const {return targets_;} - - void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - -Q_SIGNALS: - void repaintRequest(Animation*); - -protected Q_SLOTS: - void renderFrame(int); - -protected: - QWidget* view_; - QList targets_; - Type type_; -}; - -class AnimationHandler -{ -public: - explicit AnimationHandler(QWidget* view); - ~AnimationHandler(); - Animation* find(Animation::Type,bool makeIt); - -protected: - QWidget* view_; - QMap items_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/AstCollateVNodesVisitor.cpp ecflow-4.11.1/Viewer/src/AstCollateVNodesVisitor.cpp --- ecflow-4.9.0/Viewer/src/AstCollateVNodesVisitor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AstCollateVNodesVisitor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "AstCollateVNodesVisitor.hpp" - -#include - -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -static std::vector attrTypes; - -AstCollateVNodesVisitor::AstCollateVNodesVisitor(std::vector& s) : items_(s) -{ - if(attrTypes.empty()) - { - QStringList types; - types << "event" << "meter" << "var" << "genvar"; - Q_FOREACH(QString name,types) - { - VAttributeType *t=VAttributeType::find(name.toStdString()); - Q_ASSERT(t); - attrTypes.push_back(t); - - } - } -} - -AstCollateVNodesVisitor::~AstCollateVNodesVisitor() {} - -void AstCollateVNodesVisitor::visitEventState(AstEventState* astNode) -{ -} - -void AstCollateVNodesVisitor::visitNode(AstNode* astNode) -{ - if(Node* referencedNode = astNode->referencedNode()) - { - if(VNode* n=static_cast(referencedNode->graphic_ptr())) - { - items_.push_back(n); - } - } -} - -void AstCollateVNodesVisitor::visitVariable(AstVariable* astVar) -{ - if(Node* referencedNode = astVar->referencedNode()) - { - if(VNode* n=static_cast(referencedNode->graphic_ptr())) - { - std::size_t nType=attrTypes.size(); - std::size_t nItem=items_.size(); - for(std::size_t i=0; i < nType; i++) - { - if(VAttribute *a=n->findAttribute(attrTypes[i],astVar->name())) - { - for(std::size_t k=0; k < nItem; k++) - { - if(a == items_[k]) - return; - } - - items_.push_back(a); - return; - } - } - } - } -} - -void AstCollateVNodesVisitor::visitParentVariable(AstParentVariable* astVar) -{ - if(Node* referencedNode = astVar->referencedNode()) - { - if(VNode* n=static_cast(referencedNode->graphic_ptr())) - { - std::size_t nType=attrTypes.size(); - std::size_t nItem=items_.size(); - for(std::size_t i=0; i < nType; i++) - { - if(VAttribute *a=n->findAttribute(attrTypes[i],astVar->name())) - { - for(std::size_t k=0; k < nItem; k++) - { - if(a == items_[k]) - return; - } - - items_.push_back(a); - return; - } - } - } - } -} - -void AstCollateVNodesVisitor::visitFlag(AstFlag* astVar) -{ - // ??? -} - diff -Nru ecflow-4.9.0/Viewer/src/AstCollateVNodesVisitor.hpp ecflow-4.11.1/Viewer/src/AstCollateVNodesVisitor.hpp --- ecflow-4.9.0/Viewer/src/AstCollateVNodesVisitor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AstCollateVNodesVisitor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef ASTCOLLATEVNODESVISITOR_HPP -#define ASTCOLLATEVNODESVISITOR_HPP - -#include "ExprAstVisitor.hpp" - -#include - -class VItem; - -class AstCollateVNodesVisitor : public ecf::ExprAstVisitor -{ -public: - AstCollateVNodesVisitor(std::vector& ); - virtual ~AstCollateVNodesVisitor(); - - virtual void visitTop(AstTop*){} - virtual void visitRoot(AstRoot*){} - virtual void visitAnd(AstAnd*){} - virtual void visitNot(AstNot*){} - virtual void visitPlus(AstPlus*){} - virtual void visitMinus(AstMinus*){} - virtual void visitDivide(AstDivide*){} - virtual void visitMultiply(AstMultiply*){} - virtual void visitModulo(AstModulo*){} - virtual void visitOr(AstOr*){} - virtual void visitEqual(AstEqual*){} - virtual void visitNotEqual(AstNotEqual*){} - virtual void visitLessEqual(AstLessEqual*){} - virtual void visitGreaterEqual(AstGreaterEqual*){} - virtual void visitGreaterThan(AstGreaterThan*){} - virtual void visitLessThan(AstLessThan*){} - virtual void visitLeaf(AstLeaf*){} - virtual void visitInteger(AstInteger*){} - virtual void visitFunction(AstFunction*){} - virtual void visitNodeState(AstNodeState*){} - virtual void visitEventState(AstEventState*); - virtual void visitNode(AstNode*); - virtual void visitVariable(AstVariable*); - virtual void visitParentVariable(AstParentVariable*); - virtual void visitFlag(AstFlag*); - -private: - std::vector& items_; -}; - -#endif // ASTCOLLATEVNODESVISITOR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/AttributeEditor.cpp ecflow-4.11.1/Viewer/src/AttributeEditor.cpp --- ecflow-4.9.0/Viewer/src/AttributeEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,380 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "AttributeEditor.hpp" - -#include "AttributeEditorFactory.hpp" -#include "ConnectState.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "VConfig.hpp" -#include "VRepeatAttr.hpp" - -#include - -#define _USE_MODELESS_ATTRIBUTEDITOR -#define _UI_ATTRIBUTEDITOR_DEBUG - -#ifdef _USE_MODELESS_ATTRIBUTEDITOR -static QList editors; -#endif - -AttributeEditor::AttributeEditor(VInfo_ptr info,QString type,QWidget* parent) : QDialog(parent), info_(info), form_(0), type_(type) -{ - setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - -#ifdef _USE_MODELESS_ATTRIBUTEDITOR - setModal(false); -#endif - - Q_ASSERT(info_ && info_->isAttribute() && info_->attribute()); - messageLabel_->hide(); - - attrData_=info_->attribute()->data(); - - QString wt="Edit " + type; - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - attachInfo(); - - connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), - this,SLOT(slotButton(QAbstractButton*))); -} - -AttributeEditor::~AttributeEditor() -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG -#endif - detachInfo(); -#ifdef _USE_MODELESS_ATTRIBUTEDITOR - editors.removeOne(this); -#endif -} - -void AttributeEditor::edit(VInfo_ptr info,QWidget *parent) -{ - Q_ASSERT(info && info->isAttribute() && info->attribute()); - VAttribute* a=info->attribute(); - Q_ASSERT(a->type()); - -#ifdef _USE_MODELESS_ATTRIBUTEDITOR - Q_FOREACH(AttributeEditor* e,editors) - { - if((e->info_ && info) && - *(e->info_.get()) == *(info.get())) - { - e->raise(); - return; - } - } -#endif - - //For repeats we create an editor for each type - std::string typeStr=a->type()->strName(); - if(a->type() == VAttributeType::find("repeat")) - { - typeStr+="_" + a->subType(); - } - - //The edtior will be automatically deleted on close (Qt::WA_DeleteOnClose is set) - if(AttributeEditor* e=AttributeEditorFactory::create(typeStr,info,0)) - { -#ifdef _USE_MODELESS_ATTRIBUTEDITOR - editors << e; - e->show(); -#else - e->exec(); -#endif - } -} - -void AttributeEditor::addForm(QWidget* w) -{ - form_=w; - mainLayout_->insertWidget(3,w,1); -} - -void AttributeEditor::accept() -{ - apply(); - QDialog::accept(); -} - -void AttributeEditor::attachInfo() -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG -#endif - - if(info_) - { - if(info_->server()) - { - info_->server()->addNodeObserver(this); - info_->server()->addServerObserver(this); - } - - info_->addObserver(this); - } -} - -void AttributeEditor::detachInfo() -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG -#endif - if(info_) - { - if(info_->server()) - { -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " remove NodeObserver " << this; -#endif - info_->server()->removeNodeObserver(this); -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " remove ServerObserver " << this; -#endif - info_->server()->removeServerObserver(this); - } -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " remove InfoObserver"; -#endif - info_->removeObserver(this); - } - - messageLabel_->stopLoadLabel(); -} - -void AttributeEditor::slotButton(QAbstractButton* b) -{ - if(b && buttonBox_->buttonRole(b) == QDialogButtonBox::ResetRole) - resetValue(); -} - -void AttributeEditor::checkButtonStatus() -{ - setResetStatus(isValueChanged()); - setSaveStatus(isValueChanged()); -} - -void AttributeEditor::setResetStatus(bool st) -{ - QPushButton *resetpb=buttonBox_->button(QDialogButtonBox::Reset); - Q_ASSERT(resetpb); - resetpb->setEnabled(st); -} - -void AttributeEditor::setSaveStatus(bool st) -{ - QPushButton *savepb=buttonBox_->button(QDialogButtonBox::Save); - Q_ASSERT(savepb); - savepb->setEnabled(st); -} - -void AttributeEditor::setSuspended(bool st) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG - UiLog().dbg() << " status=" << st; -#endif - - Q_ASSERT(form_); - form_->setEnabled(!st); - - QPushButton *savePb=buttonBox_->button(QDialogButtonBox::Save); - Q_ASSERT(savePb); - savePb->setEnabled(!st); - - QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset); - Q_ASSERT(resetPb); - resetPb->setEnabled(!st); -} - -void AttributeEditor::notifyDataLost(VInfo* info) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG -#endif - if(info_ && info_.get() == info) - { - detachInfo(); - messageLabel_->showWarning("The parent node or the edited " + type_ + " is not available anymore! Please close the dialog!"); - setSuspended(true); - } -} - -void AttributeEditor::notifyBeginNodeChange(const VNode* vn, const std::vector& aspect,const VNodeChange&) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG -#endif - if(info_ && info_->node() && info_->node() == vn) - { - bool attrNumCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_ATTR) != aspect.end()); - if(attrNumCh) - { -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " ADD_REMOVE_ATTR"; -#endif - //try to regain the data - info_->regainData(); - - //If the attribute is not available dataLost() was already called. - if(!info_ || !info_->hasData()) - { -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " attribute does not exist"; -#endif - return; - } - - Q_ASSERT(info_->server() && info_->node()); - setSuspended(false); - -#if 0 - if(1) - { -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UiLog().dbg() << " attribute does not exist"; -#endif - detachInfo(); - messageLabel_->showWarning("The edited " + type_ + - " is not available any more! Please close the dialog!"); - setSuspended(true); - } -#endif - } - else - { - nodeChanged(aspect); - } - } -} - -void AttributeEditor::notifyDefsChanged(ServerHandler* server, const std::vector& a) -{ - if(info_ && info_->server() && info_->server() == server) - { - //TODO: implement - - } -} - -void AttributeEditor::notifyServerDelete(ServerHandler* server) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG_S(server) -#endif - if(info_ && info_->server() == server) - { - detachInfo(); - messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + - " was removed from ecFlowUI! The edited " + type_ + - " is not available anymore! Please close the dialog!"); - setSuspended(true); - } -} - -//This must be called at the beginning of a reset -void AttributeEditor::notifyBeginServerClear(ServerHandler* server) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG_S(server) -#endif - - if(info_) - { - if(info_->server() && info_->server() == server) - { - messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + " is being reloaded. \ - Until it is finished this " + type_ + " cannot be modified!"); - - messageLabel_->startLoadLabel(); - - setSuspended(true); - checkButtonStatus(); - } - } -} - -//This must be called at the end of a reset -void AttributeEditor::notifyEndServerScan(ServerHandler* server) -{ -#ifdef _UI_ATTRIBUTEDITOR_DEBUG - UI_FUNCTION_LOG_S(server) -#endif - - if(info_) - { - if(info_->server() && info_->server() == server) - { - messageLabel_->hide(); - messageLabel_->clear(); - - //We try to ressurect the info. We have to do it explicitly because it is not guaranteed - //that notifyEndServerScan() will be first called on other objects than AttributeEditor. So it - //is possible that the node exists but is still set to NULL in VInfo. - info_->regainData(); - - //If the node is not available dataLost() was already called. - if(!info_ || !info_->hasData()) - return; - - Q_ASSERT(info_->server() && info_->node()); - - setSuspended(false); - } - } -} - -void AttributeEditor::notifyServerConnectState(ServerHandler* server) -{ - Q_ASSERT(server); - if(info_->server() && info_->server() == server) - { - switch(server->connectState()->state()) - { - case ConnectState::Lost: - messageLabel_->showWarning("Connection lost to server " + QString::fromStdString(server->name()) + ". \ - Until it the connection regained this " + type_ + " cannot be modified!"); - messageLabel_->stopLoadLabel(); - setSuspended(true); - break; - case ConnectState::Normal: - messageLabel_->hide(); - messageLabel_->clear(); - messageLabel_->stopLoadLabel(); - setSuspended(false); - break; - default: - break; - } - } -} - -void AttributeEditor::doNotUseReset() -{ - if(QPushButton *resetPb=buttonBox_->button(QDialogButtonBox::Reset)) - { - resetPb->setEnabled(false); - resetPb->hide(); - } -} - -void AttributeEditor::disableCancel() -{ - if(QPushButton *cancelPb=buttonBox_->button(QDialogButtonBox::Cancel)) - cancelPb->hide(); -} diff -Nru ecflow-4.9.0/Viewer/src/AttributeEditorDialog.ui ecflow-4.11.1/Viewer/src/AttributeEditorDialog.ui --- ecflow-4.9.0/Viewer/src/AttributeEditorDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeEditorDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ - - - AttributeEditorDialog - - - - 0 - 0 - 281 - 171 - - - - Edit label - - - true - - - - 4 - - - - - TextLabel - - - Qt::NoTextInteraction - - - - - - - - - - Qt::Vertical - - - - 20 - 6 - - - - - - - - Qt::Vertical - - - - 20 - 6 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Reset|QDialogButtonBox::Save - - - - - - - - EditorInfoLabel - QLabel -
        EditorInfoLabel.hpp
        -
        - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        -
        - - - - buttonBox_ - accepted() - AttributeEditorDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox_ - rejected() - AttributeEditorDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/AttributeEditorFactory.cpp ecflow-4.11.1/Viewer/src/AttributeEditorFactory.cpp --- ecflow-4.9.0/Viewer/src/AttributeEditorFactory.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeEditorFactory.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "AttributeEditorFactory.hpp" - -#include - -static std::map* makers = 0; - -AttributeEditorFactory::AttributeEditorFactory(const std::string& type) -{ - if(makers == 0) - makers = new std::map; - - (*makers)[type] = this; -} - -AttributeEditorFactory::~AttributeEditorFactory() -{ - // Not called -} - -AttributeEditor* AttributeEditorFactory::create(const std::string& type,VInfo_ptr info,QWidget* parent) -{ - std::map::iterator j = makers->find(type); - if(j != makers->end()) - return (*j).second->make(info,parent); - - return 0; -} diff -Nru ecflow-4.9.0/Viewer/src/AttributeEditorFactory.hpp ecflow-4.11.1/Viewer/src/AttributeEditorFactory.hpp --- ecflow-4.9.0/Viewer/src/AttributeEditorFactory.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeEditorFactory.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef ATTRIBUTEEDITORFACTORY_HPP -#define ATTRIBUTEEDITORFACTORY_HPP - -#include -#include "VInfo.hpp" - -class AttributeEditor; -class QWidget; - -class AttributeEditorFactory -{ -public: - explicit AttributeEditorFactory(const std::string& type); - virtual ~AttributeEditorFactory(); - - virtual AttributeEditor* make(VInfo_ptr,QWidget*) = 0; - static AttributeEditor* create(const std::string&,VInfo_ptr,QWidget*); - -private: - explicit AttributeEditorFactory(const AttributeEditorFactory&); - AttributeEditorFactory& operator=(const AttributeEditorFactory&); -}; - -template -class AttributeEditorMaker : public AttributeEditorFactory -{ - AttributeEditor* make(VInfo_ptr info,QWidget* parent) { return new T(info,parent); } -public: - explicit AttributeEditorMaker(const std::string& t) : AttributeEditorFactory(t) {} -}; - -#endif // ATTRIBUTEEDITORFACTORY_HPP diff -Nru ecflow-4.9.0/Viewer/src/AttributeEditor.hpp ecflow-4.11.1/Viewer/src/AttributeEditor.hpp --- ecflow-4.9.0/Viewer/src/AttributeEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef ATTRIBUTEEDITOR_HPP -#define ATTRIBUTEEDITOR_HPP - -#include - -#include "NodeObserver.hpp" -#include "ServerObserver.hpp" -#include "VInfo.hpp" - -#include "ui_AttributeEditorDialog.h" - -class AttributeEditor : public QDialog, public ServerObserver, public NodeObserver, public VInfoObserver, protected Ui::AttributeEditorDialog -{ -Q_OBJECT - -public: - AttributeEditor(VInfo_ptr info,QString type,QWidget* parent); - virtual ~AttributeEditor(); - - //From VInfoObserver - void notifyDelete(VInfo*) {} - void notifyDataLost(VInfo*); - - //From NodeObserver - void notifyBeginNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&); - void notifyEndNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&) {} - - //From ServerObserver - void notifyDefsChanged(ServerHandler* server, const std::vector& a); - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - void notifyEndServerClear(ServerHandler* server) {} - void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - void notifyEndServerScan(ServerHandler* server); - void notifyServerConnectState(ServerHandler* server); - void notifyServerSuiteFilterChanged(ServerHandler* server) {} - void notifyServerSyncFinished(ServerHandler* server) {} - - static void edit(VInfo_ptr info,QWidget *parent); - -public Q_SLOTS: - void accept(); - void slotButton(QAbstractButton*); - -protected: - void attachInfo(); - void detachInfo(); - void checkButtonStatus(); - void setResetStatus(bool st); - void setSaveStatus(bool st); - void setSuspended(bool); - void addForm(QWidget* w); - void hideForm(); - void doNotUseReset(); - void disableCancel(); - virtual void apply()=0; - virtual void resetValue()=0; - virtual bool isValueChanged()=0; - virtual void nodeChanged(const std::vector& a) {} - - VInfo_ptr info_; - QStringList attrData_; - QWidget* form_; - QString type_; -}; - -#endif // ATTRIBUTEEDITOR_HPP diff -Nru ecflow-4.9.0/Viewer/src/AttributeSearchPanel.cpp ecflow-4.11.1/Viewer/src/AttributeSearchPanel.cpp --- ecflow-4.9.0/Viewer/src/AttributeSearchPanel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeSearchPanel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,219 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "AttributeSearchPanel.hpp" - -#include "CaseSensitiveButton.hpp" -#include "NodeQuery.hpp" -#include "NodeQueryOptionEdit.hpp" -#include "StringMatchCombo.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -//====================================================== -// -// AttributeLineDesc -// -//====================================================== - -class AttrLineDesc -{ -public: - AttrLineDesc(QString name,int row) : name_(name), row_(row) {} - virtual ~AttrLineDesc() {} - - virtual QString value()=0; - QString name() const {return name_;} - int row() const {return row_;} - -protected: - QString name_; - int row_; -}; - -class AttrLineStringDesc : public AttrLineDesc -{ -public: - AttrLineStringDesc(QString name,int row,QLineEdit *le) : AttrLineDesc(name,row), le_(le) {} - - QString value() {return le_->text();} - -protected: - QLineEdit* le_; -}; - - -//====================================================== -// -// AttributeGroupDesc -// -//====================================================== - -class AttrGroupDesc -{ -public: - AttrGroupDesc(QString name,QGridLayout* grid) : name_(name), grid_(grid) {} - -#if 0 - QString query() const; -#endif - void hide(); - void show(); - void add(AttrLineDesc* line) {lines_ << line;} - -protected: - QString name_; - QList lines_; - QGridLayout* grid_; -}; - -void AttrGroupDesc::hide() -{ - Q_FOREACH(AttrLineDesc* item,lines_) - { - int row=item->row(); - for(int i=0; i < grid_->columnCount(); i++) - { - if(QLayoutItem *li=grid_->itemAtPosition(row,i)) - { - if(li->widget()) - { - li->widget()->hide(); - } - } - } - } -} - -void AttrGroupDesc::show() -{ - Q_FOREACH(AttrLineDesc* item,lines_) - { - int row=item->row(); - for(int i=0; i < grid_->columnCount(); i++) - { - if(QLayoutItem *li=grid_->itemAtPosition(row,i)) - { - if(li->widget()) - { - li->widget()->show(); - } - } - } - } -} - -//====================================================== -// -// AttributeSearchPanel -// -//====================================================== - -AttributeSearchPanel::AttributeSearchPanel(QWidget* parent) : - QWidget(parent), - query_(NULL) -{ - //setupUi(this); -} - -AttributeSearchPanel::~AttributeSearchPanel() -{ -} - -void AttributeSearchPanel::setQuery(NodeQuery* query) -{ - query_=query; -} - -void AttributeSearchPanel::setSelection(QStringList lst) -{ - if(lst == selection_) - return; - - QMapIterator > it(groups_); - while (it.hasNext()) - { - it.next(); - bool inNew=lst.contains(it.key()); - bool inCurrent=selection_.contains(it.key()); - - bool st=false; - if(inNew && !inCurrent) - { - st=true; - } - else if(!inNew && inCurrent) - { - st=false; - } - - Q_FOREACH(NodeQueryOptionEdit* e,it.value()) - { - e->setVisible(st); - } - - } - - if(lst.isEmpty()) - hide(); - else - show(); - - selection_=lst; - - buildQuery(); -} - -void AttributeSearchPanel::clearSelection() -{ - setSelection(QStringList()); -} - -void AttributeSearchPanel::slotOptionEditChanged() -{ - -} - -void AttributeSearchPanel::buildQuery() -{ - /*query_.clear(); - - QMapIterator it(groups_); - while (it.hasNext()) - { - it.next(); - QString name=it.key(); - if(selection_.contains(name)) - { - if(!query_.isEmpty()) - query_+=" or "; - query_+=it.value()->query(); - } - } - - Q_EMIT queryChanged();*/ -} - -QStringList AttributeSearchPanel::groupNames() const -{ - return groups_.keys(); -} - -void AttributeSearchPanel::init() -{ -} - diff -Nru ecflow-4.9.0/Viewer/src/AttributeSearchPanel.hpp ecflow-4.11.1/Viewer/src/AttributeSearchPanel.hpp --- ecflow-4.9.0/Viewer/src/AttributeSearchPanel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeSearchPanel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ -#define VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ - -#include -#include - -class QGridLayout; -class QStandardItemModel; -class AttrGroupDesc; -class NodeQuery; -class NodeQueryOptionEdit; - - -class AttributeSearchPanel : public QWidget -{ - Q_OBJECT - -public: - explicit AttributeSearchPanel(QWidget *parent = 0); - ~AttributeSearchPanel(); - //QString query() const {return query_;} - QStringList groupNames() const; - void setQuery(NodeQuery*); - void init(); - -public Q_SLOTS: - void buildQuery(); - void setSelection(QStringList); - void clearSelection(); - -protected Q_SLOTS: - void slotOptionEditChanged(); - -// void slotTextEdited(QString); -// void slotMatchChanged(int); -#if 0 - void slotCaseChanged(bool); -#endif - -Q_SIGNALS: - void queryChanged(); - -private: - //void addStringLine(QString labelTxt,QString text,QString group); - - NodeQuery* query_; - QMap > groups_; - QGridLayout* grid_; - QStringList selection_; - -}; - -#endif /* VIEWER_SRC_ATTRIBUTESEARCHPANEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/AttributeView.hpp ecflow-4.11.1/Viewer/src/AttributeView.hpp --- ecflow-4.9.0/Viewer/src/AttributeView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/AttributeView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ diff -Nru ecflow-4.9.0/Viewer/src/CaseSensitiveButton.cpp ecflow-4.11.1/Viewer/src/CaseSensitiveButton.cpp --- ecflow-4.9.0/Viewer/src/CaseSensitiveButton.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CaseSensitiveButton.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CaseSensitiveButton.hpp" - -CaseSensitiveButton::CaseSensitiveButton(QWidget *parent) : QToolButton(parent) -{ - connect(this,SIGNAL(clicked(bool)), - this,SLOT(slotClicked(bool))); - - setCheckable(true); - setIcon(QPixmap(":/viewer/case_sensitive.svg")); - - tooltip_[true]=tr("Case sensitivity is on. Toggle to turn it off."); - tooltip_[false]=tr("Case sensitivity is off. Toggle to turn it on."); - - setToolTip(tooltip_[false]); -} - -void CaseSensitiveButton::slotClicked(bool b) -{ - setToolTip(tooltip_[b]); - Q_EMIT changed(b); -} - diff -Nru ecflow-4.9.0/Viewer/src/CaseSensitiveButton.hpp ecflow-4.11.1/Viewer/src/CaseSensitiveButton.hpp --- ecflow-4.9.0/Viewer/src/CaseSensitiveButton.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CaseSensitiveButton.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ -#define VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ - -#include - -#include - -class CaseSensitiveButton : public QToolButton -{ -Q_OBJECT - -public: - explicit CaseSensitiveButton(QWidget* parent=0); - -protected Q_SLOTS: - void slotClicked(bool); - -Q_SIGNALS: - void changed(bool); - -private: - std::map tooltip_; -}; - -#endif /* VIEWER_SRC_CASESENSITIVEBUTTON_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotify.cpp ecflow-4.11.1/Viewer/src/ChangeNotify.cpp --- ecflow-4.9.0/Viewer/src/ChangeNotify.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotify.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,493 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ChangeNotify.hpp" - -#include "ChangeNotifyDialog.hpp" -#include "ChangeNotifyModel.hpp" -#include "ChangeNotifyWidget.hpp" -#include "ServerHandler.hpp" -#include "Sound.hpp" -#include "UiLog.hpp" -#include "VConfig.hpp" -#include "VConfigLoader.hpp" -#include "VNode.hpp" -#include "VNodeList.hpp" -#include "VProperty.hpp" - -#include - -//#ifdef ECFLOW_QT5 -//#include -//#endif - -#include -#include - -#include - -static std::map items; - -static AbortedNotify abortedNotify("aborted"); -static ChangeNotify restaredtNotify("restarted"); -static ChangeNotify lateNotify("late"); -static ChangeNotify zombieNotify("zombie"); -static ChangeNotify aliasNotify("alias"); - -ChangeNotifyDialog* ChangeNotify::dialog_=0; - -//============================================== -// -// ChangeNotify -// -//============================================== - -ChangeNotify::ChangeNotify(const std::string& id) : - id_(id), - enabled_(false), - data_(0), - model_(0), - proxyModel_(0), - prop_(0), - propEnabled_(0) -{ - data_=new VNodeList(); - model_=new ChangeNotifyModel(); - model_->setData(data_); - - proxyModel_=new QSortFilterProxyModel(); - proxyModel_->setSourceModel(model_); - proxyModel_->setDynamicSortFilter(true); - - items[id] = this; -} - -ChangeNotifyModel* ChangeNotify::model() const -{ - return model_; //proxyModel_; -} - -void ChangeNotify::add(VNode *node,bool popup,bool sound) -{ - data_->add(node); - //proxyModel_->invalidate(); - - if(popup) - { - dialog()->setCurrent(this); - dialog()->show(); - dialog()->raise(); - } - else - { - if(!dialog()->isVisible()) - dialog()->setCurrent(this); - else - dialog()->raise(); - } - - if(sound) - { - bool sys=true; - std::string fName; - int loop=0; - if(VProperty* p=prop_->findChild("sound_file_type")) - { - sys=(p->value() == "system"); - } - - if(sys) - { - if(VProperty* p=prop_->findChild("sound_system_file")) - { - fName=p->valueAsStdString(); - } - } - - if(fName.empty()) - return; - - if(VProperty* p=prop_->findChild("sound_loop")) - { - loop=p->value().toInt(); - } - - if(sys) - { - Sound::instance()->playSystem(fName,loop); - } - - } -} - -void ChangeNotify::remove(VNode *node) -{ - data_->remove(node); -} - -void ChangeNotify::setEnabled(bool en) -{ - enabled_=en; - - if(!enabled_) - { - data_->clear(); - } - - proxyModel_->invalidate(); - - if(dialog_) - { - dialog()->setEnabled(this,en); - } - - ChangeNotifyWidget::setEnabled(id_,en); -} - -void ChangeNotify::setProperty(VProperty* prop) -{ - prop_=prop; - - if(VProperty* p=prop->findChild("use_status_colour")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("fill_colour")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("text_colour")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("count_fill_colour")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("count_text_colour")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("sound_file_type")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("sound_system_file")) - { - p->addObserver(this); - - QStringList lst; - const std::vector& vals=Sound::instance()->sysSounds(); - for(std::vector::const_iterator it=vals.begin(); it != vals.end(); ++it) - { - lst << QString::fromStdString(*it); - } - p->setParam("values",lst.join("/")); - p->setParam("values_label",lst.join("/")); - p->setParam("dir",QString::fromStdString(Sound::instance()->sysDir())); - p->addObserver(this); - } - - if(VProperty* p=prop->findChild("sound_user_file")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("sound_volume")) - p->addObserver(this); - - if(VProperty* p=prop->findChild("sound_loop")) - p->addObserver(this); -} - -void ChangeNotify::notifyChange(VProperty* prop) -{ - Q_ASSERT(prop); - - if(prop->name().contains("sound",Qt::CaseInsensitive)) - return; - - else if(prop->name() == "max_item_num") - { - data_->setMaxNum(prop->value().toInt()); - } - //The central settings changed - else if(prop == propEnabled_) - { - //Check if there is any loaded server with this - //notification enabled - bool hasEnabledServer=ServerHandler::checkNotificationState(id_); - updateNotificationState(hasEnabledServer); - } - - dialog()->updateSettings(this); - ChangeNotifyWidget::updateSettings(id_); -} - -void ChangeNotify::updateNotificationState(bool hasEnabledServer) -{ - if(propEnabled_) - setEnabled(propEnabled_->value().toBool() || hasEnabledServer); - else - setEnabled(hasEnabledServer); -} - - -void ChangeNotify::clearData() -{ - data_->clear(); - //proxyModel_->invalidate(); -} - -void ChangeNotify::showDialog(ChangeNotify* notifier) -{ - if(notifier) - dialog()->setCurrent(notifier); - - dialog()->show(); - dialog()->raise(); -} - -QColor ChangeNotify::fillColour() const -{ - if(prop_) - if(VProperty* p=prop_->findChild("fill_colour")) - return p->value().value(); - - return QColor(); -} - -QColor ChangeNotify::textColour() const -{ - if(prop_) - if(VProperty* p=prop_->findChild("text_colour")) - return p->value().value(); - - return QColor(); -} - -QColor ChangeNotify::countFillColour() const -{ - if(prop_) - if(VProperty* p=prop_->findChild("count_fill_colour")) - return p->value().value(); - - return QColor(); -} - -QColor ChangeNotify::countTextColour() const -{ - if(prop_) - if(VProperty* p=prop_->findChild("count_text_colour")) - return p->value().value(); - - return QColor(); -} - -QString ChangeNotify::toolTip() const -{ - return (prop_)?(prop_->param("tooltip")):QString(); -} - -QString ChangeNotify::widgetText() const -{ - return (prop_)?(prop_->param("widgetText")):QString(); -} - -//----------------------------------- -// -// Static methods -// -//----------------------------------- - -void ChangeNotify::add(const std::string& id,VNode *node,bool popup,bool sound) -{ - if(ChangeNotify* obj=ChangeNotify::find(id)) - { - obj->add(node,popup,sound); - } -} - -void ChangeNotify::remove(const std::string& id,VNode *node) -{ - if(ChangeNotify* obj=ChangeNotify::find(id)) - { - obj->remove(node); - } -} - -void ChangeNotify::updateNotificationStateFromServer(const std::string& id,bool en) -{ - if(ChangeNotify* obj=ChangeNotify::find(id)) - { - obj->updateNotificationState(en); - } -} - -ChangeNotify* ChangeNotify::find(const std::string& id) -{ - std::map::iterator it=items.find(id); - if(it != items.end()) - return it->second; - - return 0; -} - -void ChangeNotify::load(VProperty* group) -{ -UI_FUNCTION_LOG - - if(group->name() == "notification") - { - for(int i=0; i < group->children().size(); i++) - { - VProperty *p=group->children().at(i); - if(ChangeNotify* obj=ChangeNotify::find(p->strName())) - { - obj->setProperty(p); - } - } - - //This should be observed by each notification object - if(VProperty* p=group->find("notification.settings.max_item_num")) - { - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - p->addObserver(it->second); - it->second->data_->setMaxNum(p->value().toInt()); - } - } - } - else if(group->name() == "server") - { - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - it->second->loadServerSettings(); - } - } -#if 0 - else if(group->name() == "nstate") - { - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - it->second->loadNodeState(); - } - } -#endif -} - -//Called only once during init -void ChangeNotify::loadServerSettings() -{ -UI_FUNCTION_LOG - - UiLog().dbg() << " id=" << id_; - - std::string v("server.notification." + id_ + ".enabled"); - - UiLog().dbg() << " property=" << v; - - if(VProperty *p=VConfig::instance()->find(v)) - { - propEnabled_=p; - p->addObserver(this); - setEnabled(p->value().toBool()); - } - else - { - UiLog().err() << " Error! Unable to find property: " << v; - } -} - -ChangeNotifyDialog* ChangeNotify::dialog() -{ - if(!dialog_) - { - dialog_=new ChangeNotifyDialog(); - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - dialog_->add(it->second); - } - - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - dialog_->setEnabled(it->second,it->second->isEnabled()); - } - } - - return dialog_; -} - -/* -void ChangeNotify::showDialog(ChangeNotifyconst std::string& id) -{ - dialog()->setCurrentTab(id); - dialog()->show(); - dialog()->raise(); -} -*/ -/*void ChangeNotify::clearData(const std::string& id) -{ - if(ChangeNotify* obj=ChangeNotify::find(id)) - { - obj->data()->clear(); - } -}*/ - -void ChangeNotify::populate(ChangeNotifyWidget* w) -{ - for(std::map::iterator it=items.begin(); it != items.end(); ++it) - { - w->addTb(it->second); - } -} - -//================================================== -// -// AbortedNotify -// -//================================================== - -QColor AbortedNotify::fillColour() const -{ - bool useState=false; - if(prop_) - if(VProperty* p=prop_->findChild("use_status_colour")) - useState=p->value().toBool(); - - if(useState) - { - if(VProperty *nsp=VConfig::instance()->find("nstate.aborted")) - { - if(VProperty* master=nsp->findChild("fill_colour")) - { - return master->value().value(); - } - } - } - - return ChangeNotify::fillColour(); -} - -QColor AbortedNotify::textColour() const -{ - bool useState=false; - if(prop_) - if(VProperty* p=prop_->findChild("use_status_colour")) - useState=p->value().toBool(); - - if(useState) - { - if(VProperty *nsp=VConfig::instance()->find("nstate.aborted")) - { - if(VProperty* master=nsp->findChild("font_colour")) - { - return master->value().value(); - } - } - } - - return ChangeNotify::textColour(); -} - -static SimpleLoader loaderNotify("notification"); -static SimpleLoader loaderServerNotify("server"); diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.cpp ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.cpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,533 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ChangeNotifyDialog.hpp" - -#include "ChangeNotify.hpp" -#include "ChangeNotifyModel.hpp" -#include "MainWindow.hpp" -#include "SessionHandler.hpp" -#include "TreeView.hpp" -#include "UiLog.hpp" -#include "UIDebug.hpp" -#include "VConfig.hpp" -#include "VNodeList.hpp" -#include "VProperty.hpp" -#include "WidgetNameProvider.hpp" -#include "WmWorkspaceHandler.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -Q_DECLARE_METATYPE(QList) - -//=========================================================== -// -// ChangeNotifyDialogButton -// -//=========================================================== - -ChangeNotifyDialogButton::ChangeNotifyDialogButton(QWidget* parent) : - QToolButton(parent), - notifier_(0) -{ - setProperty("notify","1"); - setAutoRaise(true); - setIconSize(QSize(20,20)); - setCheckable(true); -} - -void ChangeNotifyDialogButton::setNotifier(ChangeNotify* notifier) -{ - notifier_=notifier; - - setText(notifier_->widgetText()); - setToolTip(notifier_->toolTip()); - - connect(notifier_->data(),SIGNAL(endAppendRow()), - this,SLOT(slotAppend())); - - connect(notifier_->data(),SIGNAL(endRemoveRow(int)), - this,SLOT(slotRemoveRow(int))); - - connect(notifier_->data(),SIGNAL(endReset()), - this,SLOT(slotReset())); - - updateSettings(); -} - -void ChangeNotifyDialogButton::slotAppend() -{ - updateSettings(); -} - -void ChangeNotifyDialogButton::slotRemoveRow(int) -{ - updateSettings(); -} - -void ChangeNotifyDialogButton::slotReset() -{ - updateSettings(); -} - -void ChangeNotifyDialogButton::updateSettings() -{ - setEnabled(notifier_->isEnabled()); - -#if 0 - - QString text; - QString numText; - - if(notifier_->prop()) - { - text=notifier_->prop()->param("widgetText"); - } - - int num=0; - if(notifier_->data()) - { - num=notifier_->data()->size(); - if(num > 0 && num < 10) - numText=QString::number(num); - else if(num > 10) - numText="9+"; - - } -#endif -} - -//=========================================================== -// -// ChangeNotifyDialogWidget -// -//=========================================================== - -ChangeNotifyDialogWidget::ChangeNotifyDialogWidget(QWidget *parent) : - QWidget(parent), - notifier_(0) -{ - setupUi(this); -} - -void ChangeNotifyDialogWidget::init(ChangeNotify* notifier) -{ - notifier_=notifier; - - tree_->setModel(notifier_->model()); - label_->setText(notifier_->widgetText()); - - -#if 0 - connect(notifier->data(),SIGNAL(endAppendRow()), - this,SLOT(slotAppend())); - - connect(notifier->data(),SIGNAL(endRemoveRow(int)), - this,SLOT(slotRemoveRow(int))); - - connect(notifier->data(),SIGNAL(endReset()), - this,SLOT(slotReset())); -#endif - - //Selection - connect(tree_,SIGNAL(clicked(const QModelIndex&)), - this,SLOT(slotSelectItem(const QModelIndex&))); - - connect(tree_,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClickItem(const QModelIndex&))); - - updateSettings(); -} - -#if 0 -void ChangeNotifyDialogWidget::slotAppend() -{ - Q_EMIT contentsChanged(); -} - -void ChangeNotifyDialogWidget::slotRemoveRow(int) -{ - Q_EMIT contentsChanged(); -} - -void ChangeNotifyDialogWidget::slotReset() -{ - Q_EMIT contentsChanged(); -} -#endif - -void ChangeNotifyDialogWidget::updateSettings() -{ - Q_ASSERT(notifier_); - QColor bgCol=notifier_->fillColour(); - QColor textCol=notifier_->textColour(); - QColor bgLight=bgCol.lighter(105); - - QString st="QLabel { \ - background: qlineargradient(x1 :0, y1: 0, x2: 0, y2: 1, \ - stop: 0 " + bgLight.name() + ", stop: 1 " + bgCol.name() + "); color: " + - textCol.name() + "; padding: 4px; border: 1px solid rgb(170,170,170);}"; - - UiLog().dbg() << bgCol << " " << textCol; - UiLog().dbg() << st; - - label_->setStyleSheet(st); -} - -void ChangeNotifyDialogWidget::slotSelectItem(const QModelIndex& idx) -{ - //QModelIndexList lst=tree_->selectedIndexes(); - //if(lst.count() > 0) - //{ - VInfo_ptr info=notifier_->model()->nodeInfo(idx); - if(info) - { - Q_EMIT selectionChanged(info); - } - //} -} - -void ChangeNotifyDialogWidget::slotDoubleClickItem(const QModelIndex&) -{ - -} - -void ChangeNotifyDialogWidget::writeSettings(QSettings& settings) -{ - QList wVec; - for(int i=0; i < tree_->model()->columnCount(QModelIndex()); i++) - { - wVec.push_back(tree_->columnWidth(i)); - } - settings.setValue("colWidth",QVariant::fromValue(wVec)); -} - -void ChangeNotifyDialogWidget::readSettings(const QSettings& settings) -{ - QList wVec; - if(settings.contains("colWidth")) - { - wVec=settings.value("colWidth").value >(); - if(wVec.count() >0 && wVec[0] < 1) - wVec.clear(); - } - - if(!wVec.isEmpty()) - { - for(int i=0; i < tree_->model()->columnCount(QModelIndex()); i++) - { - if(wVec.count() > i && wVec[i] > 0) - tree_->setColumnWidth(i,wVec[i]); - } - } - else - { - if(tree_->model()->columnCount(QModelIndex()) > 1) - { - QFont f; - QFontMetrics fm(f); - tree_->setColumnWidth(0,fm.width("serverserverserver")); - tree_->setColumnWidth(1,fm.width("/suite1/family1/family2/family3/family4/task")); - } - } -} - -//=========================================================== -// -// ChangeNotifyDialog -// -//=========================================================== - -ChangeNotifyDialog::ChangeNotifyDialog(QWidget *parent) : - QDialog(parent), - ignoreCurrentChange_(false), - switchWsProp_(0) -{ - setupUi(this); - - buttonHb_= new QHBoxLayout(buttonW_); - buttonHb_->setContentsMargins(0,0,0,0); - buttonHb_->setSpacing(2); - - buttonGroup_=new QButtonGroup(this); - connect(buttonGroup_,SIGNAL(buttonToggled(int,bool)), - this,SLOT(slotButtonToggled(int,bool))); - - clearOnCloseCb_->setChecked(true); - - connect(optionsTb_,SIGNAL(clicked()), - this,SLOT(slotOptions())); - - switchWsProp_=VConfig::instance()->find("notification.settings.switch_desktop"); - - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -ChangeNotifyDialog::~ChangeNotifyDialog() -{ - writeSettings(); -} - -void ChangeNotifyDialog::add(ChangeNotify* notifier) -{ - ChangeNotifyDialogWidget* w=new ChangeNotifyDialogWidget(this); - w->init(notifier); - - connect(w,SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChanged(VInfo_ptr))); - - ignoreCurrentChange_=true; - stacked_->addWidget(w); - ignoreCurrentChange_=false; - ntfWidgets_ << w; - - - - ChangeNotifyDialogButton *bw=new ChangeNotifyDialogButton(this); - bw->setNotifier(notifier); - buttonHb_->addWidget(bw); - int buttonId=buttonGroup_->buttons().count(); - buttonGroup_->addButton(bw,buttonId); - ntfButtons_ << bw; - - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - - readNtfWidgetSettings(stacked_->count()-1); -} - -void ChangeNotifyDialog::slotButtonToggled(int,bool) -{ - int idx=buttonGroup_->checkedId(); - if(idx != -1) - { - stacked_->setCurrentIndex(idx); - } -} - -void ChangeNotifyDialog::slotSelectionChanged(VInfo_ptr info) -{ - //Moves the dialogue to the virtual workspace of the first - //mainwindow and then switches the workspace - if(switchWsProp_ && switchWsProp_->value().toBool()) - { - if(WmWorkspaceHandler::switchTo(this,MainWindow::firstWindow())) - raise(); - } - - MainWindow::lookUpInTree(info); -} - -void ChangeNotifyDialog::slotOptions() -{ - QString op="notification"; - if(ChangeNotify* notifier=indexToNtf(stacked_->currentIndex())) - { - op+="." + QString::fromStdString(notifier->id()); - } - MainWindow::startPreferences(op); -} - -void ChangeNotifyDialog::setCurrent(ChangeNotify *ntf) -{ - int idx=ntfToIndex(ntf); - if(idx != -1) - { - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); - UI_ASSERT(idx >=0,"idx=" << idx); - - buttonGroup_->button(idx)->setChecked(true); - } -} - -void ChangeNotifyDialog::setEnabled(ChangeNotify* ntf,bool b) -{ - int idx=ntfToIndex(ntf); - if(idx != -1) - { - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); - UI_ASSERT(idx >=0,"idx=" << idx); - - buttonGroup_->button(idx)->setEnabled(b); - stacked_->widget(idx)->setEnabled(b); - } -} - -void ChangeNotifyDialog::clearCurrentData() -{ - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - - int idx=buttonGroup_->checkedId(); - if(idx != -1) - { - UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); - UI_ASSERT(idx >=0,"idx=" << idx); - if(ChangeNotify *ntf=indexToNtf(idx)) - ntf->clearData(); - } -} - -void ChangeNotifyDialog::on_closePb__clicked(bool) -{ - hide(); - - if(clearOnCloseCb_->isChecked()) - { - clearCurrentData(); - } - - writeSettings(); -} - -void ChangeNotifyDialog::on_clearPb__clicked(bool) -{ - clearCurrentData(); -} - -ChangeNotify* ChangeNotifyDialog::indexToNtf(int idx) -{ - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - - if(idx >=0 && idx < stacked_->count()) - { - UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); - UI_ASSERT(idx >=0,"idx=" << idx); - return ntfWidgets_[idx]->notifier(); - } - - return 0; -} - -int ChangeNotifyDialog::ntfToIndex(ChangeNotify* ntf) -{ - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - - for(int i=0; i < stacked_->count(); i++) - { - if(ntfWidgets_[i]->notifier() == ntf) - return i; - } - - return -1; -} - -void ChangeNotifyDialog::updateSettings(ChangeNotify* notifier) -{ - UI_ASSERT(stacked_->count() == buttonGroup_->buttons().count(), - "stacked_->count()=" << stacked_->count() << - " buttonGroup_->buttons().count()=" << buttonGroup_->buttons().count()); - - int idx=ntfToIndex(notifier); - if(idx != -1) - { - UI_ASSERT(idx < stacked_->count(),"idx=" << idx << " stacked_->count()=" << stacked_->count()); - UI_ASSERT(idx >=0,"idx=" << idx); - //if(stacked_->widget(idx)->isEnabled()) - //{ - ntfWidgets_[idx]->updateSettings(); - ntfButtons_[idx]->updateSettings(); - //} - } -} - -void ChangeNotifyDialog::closeEvent(QCloseEvent* e) -{ - if(clearOnCloseCb_->isChecked()) - { - clearCurrentData(); - } - writeSettings(); - e->accept(); -} - -void ChangeNotifyDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.setValue("clearOnClose",clearOnCloseCb_->isChecked()); - settings.endGroup(); - - for(int i=0; i < stacked_->count(); i++) - { - settings.beginGroup("tab_" + QString::number(i)); - ntfWidgets_[i]->writeSettings(settings); - settings.endGroup(); - } -} - -void ChangeNotifyDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(540,460)); - } - - if(settings.contains("clearOnClose")) - { - clearOnCloseCb_->setChecked(settings.value("clearOnClose").toBool()); - } - - settings.endGroup(); - - //The tab settings are read when the actual tabs are created later. -} - -void ChangeNotifyDialog::readNtfWidgetSettings(int idx) -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("ChangeNotifyDialog")), - QSettings::NativeFormat); - - settings.beginGroup("tab_" + QString::number(idx)); - UI_ASSERT(stacked_->count() > idx,"stacked_->count()=" << stacked_->count() << " idx=" << idx); - ntfWidgets_[idx]->readSettings(settings); - settings.endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.hpp ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.hpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,116 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef CHANGENOTIFYDIALOG_HPP_ -#define CHANGENOTIFYDIALOG_HPP_ - -#include -#include -#include -#include -#include - -#include "ui_ChangeNotifyDialog.h" -#include "ui_ChangeNotifyDialogWidget.h" - -#include "VInfo.hpp" - -class ChangeNotify; -class VProperty; - -class QButtonGroup; -class QHBoxLayout; -class QLabel; - -class ChangeNotifyDialogButton : public QToolButton -{ -Q_OBJECT - -public: - explicit ChangeNotifyDialogButton(QWidget* parent=0); - - void setNotifier(ChangeNotify*); - void updateSettings(); - -public Q_SLOTS: - void slotAppend(); - void slotRemoveRow(int); - void slotReset(); - -protected: - ChangeNotify* notifier_; -}; - -class ChangeNotifyDialogWidget : public QWidget, protected Ui::ChangeNotifyDialogWidget -{ - Q_OBJECT - -public: - explicit ChangeNotifyDialogWidget(QWidget* parent=0); - ~ChangeNotifyDialogWidget() {} - - void init(ChangeNotify*); - void updateSettings(); - ChangeNotify* notifier() const {return notifier_;} - void writeSettings(QSettings& settings); - void readSettings(const QSettings& settings); - -protected Q_SLOTS: - void slotSelectItem(const QModelIndex&); - void slotDoubleClickItem(const QModelIndex&); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - -protected: - ChangeNotify* notifier_; -}; - -class ChangeNotifyDialog : public QDialog, protected Ui::ChangeNotifyDialog -{ -Q_OBJECT - -public: - explicit ChangeNotifyDialog(QWidget *parent=0); - ~ChangeNotifyDialog(); - - void add(ChangeNotify*); - void setCurrent(ChangeNotify*); - void setEnabled(ChangeNotify*,bool b); - void updateSettings(ChangeNotify*); - -public Q_SLOTS: - void on_closePb__clicked(bool b); - void on_clearPb__clicked(bool b); - -protected Q_SLOTS: - void slotSelectionChanged(VInfo_ptr); - void slotOptions(); - void slotButtonToggled(int,bool); - -protected: - ChangeNotify* indexToNtf(int idx); - int ntfToIndex(ChangeNotify* ntf); - void closeEvent(QCloseEvent*); - void clearCurrentData(); - void writeSettings(); - void readSettings(); - void readNtfWidgetSettings(int tabIndex); - - QList ntfWidgets_; - QList ntfButtons_; - QButtonGroup* buttonGroup_; - bool ignoreCurrentChange_; - QLinearGradient grad_; - QHBoxLayout* buttonHb_; - VProperty* switchWsProp_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.ui ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.ui --- ecflow-4.9.0/Viewer/src/ChangeNotifyDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ - - - ChangeNotifyDialog - - - - 0 - 0 - 400 - 300 - - - - Change notification - - - - 2 - - - 0 - - - 2 - - - 0 - - - 2 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Configure notification options - - - &Preferences - - - - :/viewer/configure.svg:/viewer/configure.svg - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - - - - - - - C&lear current list on close - - - - - - - Qt::Horizontal - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Clea&r - - - - - - - &Close - - - - - - - clearOnCloseCb_ - line - holderW - optionsTb_ - stacked_ - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyDialogWidget.ui ecflow-4.11.1/Viewer/src/ChangeNotifyDialogWidget.ui --- ecflow-4.9.0/Viewer/src/ChangeNotifyDialogWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyDialogWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ - - - ChangeNotifyDialogWidget - - - - 0 - 0 - 502 - 508 - - - - Form - - - - 2 - - - 0 - - - - - true - - - - - - Qt::AlignCenter - - - - - - - false - - - true - - - true - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.cpp ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.cpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,368 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ChangeNotifyEditor.hpp" - -#include "PropertyLine.hpp" -#include "VConfig.hpp" -#include "VProperty.hpp" - -#include -#include - -#include - -ChangeNotifyEditor::ChangeNotifyEditor(QWidget* parent) : QWidget(parent) -{ - setupUi(this); - - model_=new ChangeNotifyEditorModel(this); - - tree_->setModel(model_); - - connect(tree_,SIGNAL(activated(QModelIndex)), - this,SLOT(slotRowSelected(QModelIndex))); - - connect(tree_,SIGNAL(clicked(QModelIndex)), - this,SLOT(slotRowSelected(QModelIndex))); - - assert(tab_->count()==0); - //assert(stacked_->count()==0); - - QFont f; - QFontMetrics fm(f); - //tree_->setFixedHeight((fm.height()+4)*5.5); - - tree_->setFixedWidth(fm.width("Restartedaaa")+30); - - //tab_->hide(); -} - -void ChangeNotifyEditor::addRow(QString label,QList lineLst,QWidget *stackContents) -{ - PropertyLine* enabledLine=0; - PropertyLine* popupLine=0; - PropertyLine* soundLine=0; - - QList propLst; - Q_FOREACH(PropertyLine* pl,lineLst) - { - if(pl) - { - propLst << pl->property(); - - if(pl->property()->name() == "enabled") - { - enabledLine=pl; - } - if(pl->property()->name() == "popup") - { - popupLine=pl; - } - if(pl->property()->name() == "sound") - { - soundLine=pl; - } - - } - } - - if(enabledLine) - { - connect(model_,SIGNAL(enabledChanged(VProperty*,QVariant)), - enabledLine,SLOT(slotReset(VProperty*,QVariant))); - - connect(enabledLine,SIGNAL(changed(QVariant)), - model_,SLOT(slotEnabledChanged(QVariant))); - - connect(enabledLine,SIGNAL(masterChanged(bool)), - model_,SLOT(slotEnabledMasterChanged(bool))); - - if(popupLine) - { - connect(enabledLine,SIGNAL(changed(QVariant)), - popupLine,SLOT(slotEnabled(QVariant))); - //init - popupLine->slotEnabled(enabledLine->property()->value()); - } - if(soundLine) - { - connect(enabledLine,SIGNAL(changed(QVariant)), - soundLine,SLOT(slotEnabled(QVariant))); - - //init - soundLine->slotEnabled(enabledLine->property()->value()); - } - } - - model_->add(label,propLst); - - QWidget* w=new QWidget(this); - QVBoxLayout* vb=new QVBoxLayout(); - vb->setContentsMargins(0,5,0,5); - w->setLayout(vb); - vb->addWidget(stackContents); - vb->addStretch(1); - - //stacked_->addWidget(w); - tab_->addTab(w,label); - - tree_->setCurrentIndex(model_->index(0,0)); - - for(int i=0; i < model_->columnCount()-1; i++) - { - tree_->resizeColumnToContents(i); - } -} - -void ChangeNotifyEditor::slotRowSelected(const QModelIndex& idx) -{ - //if(idx.row() == stacked_->currentIndex()) - if(idx.row() == tab_->currentIndex()) - return; - - if(idx.row() >= 0 && idx.row() < tab_->count()) - { - tab_->setCurrentIndex(idx.row()); - } - - /*if(idx.row() >= 0 && idx.row() < stacked_->count()) - { - stacked_->setCurrentIndex(idx.row()); - }*/ -} - -ChangeNotifyEditorModel::ChangeNotifyEditorModel(QObject *parent) : - QAbstractItemModel(parent) -{ - -} - -ChangeNotifyEditorModel::~ChangeNotifyEditorModel() -{ -} - -void ChangeNotifyEditorModel::add(QString label,QList propLst) -{ - beginResetModel(); - - ChangeNotifyEditorModelData d; - - d.label_=label; - - Q_FOREACH(VProperty* p,propLst) - { - if(p) - { - if(p->name() == "enabled") - { - d.enabled_=p; - d.enabledVal_=p->value().toBool(); - d.enabledMaster_=(p->master() && p->useMaster()); - - //Get the description - if(p->parent()) - { - if(VProperty* np=VConfig::instance()->find("notification."+ p->parent()->strName())) - d.desc_=np->param("description"); - } - } - } - } - - data_ << d; - - endResetModel(); -} - -int ChangeNotifyEditorModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 2; -} - -int ChangeNotifyEditorModel::rowCount( const QModelIndex& parent) const -{ - //Parent is the root: - if(!parent.isValid()) - { - return data_.count(); - } - - return 0; -} - -QVariant ChangeNotifyEditorModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid()) - { - return QVariant(); - } - - int row=index.row(); - if(row < 0 || row >= data_.count()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - switch(index.column()) - { - case 1: - return data_.at(row).label_; - case 2: - return data_.at(row).desc_; - default: - return QVariant(); - } - } - - else if(role == Qt::CheckStateRole) - { - switch(index.column()) - { - case 0: - return (data_.at(row).enabledVal_)?QVariant(Qt::Checked):QVariant(Qt::Unchecked); - default: - return QVariant(); - } - } - - return QVariant(); -} - -bool ChangeNotifyEditorModel::setData(const QModelIndex& index,const QVariant& value, int role) -{ - if(index.column() == 0) - { - int row=index.row(); - if(row <0 || row >= data_.count()) - return false; - - if(role == Qt::CheckStateRole) - { - bool checked=(value.toInt() == Qt::Checked)?true:false; - data_[row].enabledVal_=checked; - Q_EMIT dataChanged(index,index); - Q_EMIT enabledChanged(data_[row].enabled_,checked); - return true; - } - } - - return false; -} - - -QVariant ChangeNotifyEditorModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || role != Qt::DisplayRole) - return QAbstractItemModel::headerData( section, orient, role ); - - if(role == Qt::DisplayRole) - { - switch ( section ) - { - case 0: return tr("E"); - case 1: return tr("Title"); - case 2: return tr("Description"); - default: return QVariant(); - } - } - - return QVariant(); -} - -QModelIndex ChangeNotifyEditorModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex ChangeNotifyEditorModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - -Qt::ItemFlags ChangeNotifyEditorModel::flags( const QModelIndex & index) const -{ - int row=index.row(); - if(row >=0 && row <= data_.count() && - index.column() ==0 && data_.at(row).enabledMaster_) - { - return Qt::ItemIsUserCheckable| Qt::ItemIsSelectable; - } - - Qt::ItemFlags defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; - - if(index.isValid() && index.column() ==0) - defaultFlags=defaultFlags | Qt::ItemIsUserCheckable; - - return defaultFlags; -} - -int ChangeNotifyEditorModel::lineToRow(PropertyLine* line) const -{ - for(int i=0; i < data_.count(); i++) - { - if(data_.at(i).enabled_ == line->property()) - { - return i; - } - } - return -1; -} - - -void ChangeNotifyEditorModel::slotEnabledChanged(QVariant v) -{ - PropertyLine *line=static_cast(sender()); - assert(line); - - int row=lineToRow(line); - if(row != -1) - { - //We want to avoid circular dependencies (e.g. this function is triggered from - //setData. So we have to check if the value is different from the stored one! - if(data_.at(row).enabledVal_ != v.toBool()) - { - data_[row].enabledVal_=v.toBool(); - QModelIndex idx=index(row,0); - Q_EMIT dataChanged(idx,idx); - } - } -} - -void ChangeNotifyEditorModel::slotEnabledMasterChanged(bool b) -{ - PropertyLine *line=static_cast(sender()); - assert(line); - - int row=lineToRow(line); - if(row != -1) - { - QModelIndex idx=index(row,0); - data_[row].enabledMaster_=b; - if(b) - { - data_[row].enabledMaster_=b; - } - Q_EMIT dataChanged(idx,idx); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.hpp ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.hpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ -#define VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ - -#include -#include - -#include -#include "VProperty.hpp" - -#include "ui_ChangeNotifyEditor.h" - -class QGridlayout; - -class ChangeNotifyEditorModel; -class PropertyLine; - -class ChangeNotifyEditor : public QWidget, protected Ui::ChangeNotifyEditor -{ - Q_OBJECT - -public: - explicit ChangeNotifyEditor(QWidget* parent=0); - void addRow(QString,QList,QWidget*); - -protected Q_SLOTS: - void slotRowSelected(const QModelIndex& idx); - -private: - ChangeNotifyEditorModel* model_; -}; - - -class ChangeNotifyEditorModelData -{ -public: - ChangeNotifyEditorModelData() : enabled_(NULL), enabledMaster_(false), enabledVal_(false) {} - - QString label_; - QString desc_; - VProperty* enabled_; - bool enabledMaster_; - bool enabledVal_; -}; - - -class ChangeNotifyEditorModel : public QAbstractItemModel -{ - Q_OBJECT - -public: - explicit ChangeNotifyEditorModel(QObject *parent=0); - ~ChangeNotifyEditorModel(); - - void add(QString,QList); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - bool setData(const QModelIndex & index, const QVariant& value, int role = Qt::EditRole); - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - Qt::ItemFlags flags ( const QModelIndex & index) const; - -public Q_SLOTS: - void slotEnabledChanged(QVariant v); - void slotEnabledMasterChanged(bool b); - -Q_SIGNALS: - void enabledChanged(VProperty*,QVariant); - -protected: - int lineToRow(PropertyLine* line) const; - - QList data_; -}; - - -#endif /* VIEWER_SRC_CHANGENOTIFYEDITOR_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.ui ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.ui --- ecflow-4.9.0/Viewer/src/ChangeNotifyEditor.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyEditor.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ - - - ChangeNotifyEditor - - - - 0 - 0 - 502 - 508 - - - - Form - - - - - - false - - - true - - - true - - - - - - - -1 - - - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotify.hpp ecflow-4.11.1/Viewer/src/ChangeNotify.hpp --- ecflow-4.9.0/Viewer/src/ChangeNotify.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotify.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,91 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef CHANGENOTIFYHANDLER_HPP_ -#define CHANGENOTIFYHANDLER_HPP_ - -#include -#include - -#include "VProperty.hpp" - -class ChangeNotifyDialog; -class ChangeNotifyModel; -class ChangeNotifyWidget; -class VProperty; -class VNode; -class VNodeList; - -class QAbstractItemModel; -class QSortFilterProxyModel; - -class ChangeNotify : public VPropertyObserver -{ -public: - explicit ChangeNotify(const std::string& id); - - const std::string& id() const {return id_;} - VNodeList* data() const {return data_;} - VProperty* prop() const {return prop_;} - ChangeNotifyModel* model() const; - QSortFilterProxyModel* proxyModel() const {return proxyModel_;} - bool isEnabled() const {return enabled_;} - void clearData(); - - virtual QColor fillColour() const; - virtual QColor textColour() const; - QColor countFillColour() const; - QColor countTextColour() const; - QString toolTip() const; - QString widgetText() const; - - static void showDialog(ChangeNotify* notifier=0); - - //Form VPropertyObserver - void notifyChange(VProperty*); - - static void add(const std::string&,VNode*,bool,bool); - static void remove(const std::string&,VNode*); - static void populate(ChangeNotifyWidget* w); - static void updateNotificationStateFromServer(const std::string& id,bool hasEnabledServer); - - //Called from VConfigLoader - static void load(VProperty* group); - -protected: - void add(VNode*,bool,bool); - void remove(VNode*); - void setEnabled(bool); - void updateNotificationState(bool hasEnabledServer); - void setProperty(VProperty* prop); - void loadServerSettings(); - - static ChangeNotify* find(const std::string&); - static ChangeNotifyDialog* dialog(); - - std::string id_; - bool enabled_; - VNodeList* data_; - ChangeNotifyModel* model_; - QSortFilterProxyModel* proxyModel_; - VProperty* prop_; - VProperty* propEnabled_; //central settings in config GUI - static ChangeNotifyDialog* dialog_; -}; - -class AbortedNotify : public ChangeNotify -{ -public: - explicit AbortedNotify(const std::string& id) : ChangeNotify(id) {} - QColor fillColour() const; - QColor textColour() const; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyModel.cpp ecflow-4.11.1/Viewer/src/ChangeNotifyModel.cpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,241 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ChangeNotifyModel.hpp" - -#include "VNode.hpp" -#include "VNodeList.hpp" - -#include - -ChangeNotifyModel::ChangeNotifyModel(QObject *parent) : - QAbstractItemModel(parent), - data_(0) -{ -} - -ChangeNotifyModel::~ChangeNotifyModel() -{ -} - -VNodeList* ChangeNotifyModel::data() -{ - return data_; -} - -void ChangeNotifyModel::setData(VNodeList *data) -{ - beginResetModel(); - - data_=data; - - connect(data_,SIGNAL(beginAppendRow()), - this,SLOT(slotBeginAppendRow())); - - connect(data_,SIGNAL(endAppendRow()), - this,SLOT(slotEndAppendRow())); - - connect(data_,SIGNAL(beginRemoveRow(int)), - this,SLOT(slotBeginRemoveRow(int))); - - connect(data_,SIGNAL(endRemoveRow(int)), - this,SLOT(slotEndRemoveRow(int))); - - connect(data_,SIGNAL(beginRemoveRows(int,int)), - this,SLOT(slotBeginRemoveRows(int,int))); - - connect(data_,SIGNAL(endRemoveRows(int,int)), - this,SLOT(slotEndRemoveRows(int,int))); - - connect(data_,SIGNAL(beginReset()), - this,SLOT(slotBeginReset())); - - connect(data_,SIGNAL(endReset()), - this,SLOT(slotEndReset())); - - endResetModel(); -} - -bool ChangeNotifyModel::hasData() const -{ - return (data_ && data_->size() >0); -} - -int ChangeNotifyModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 3; -} - -int ChangeNotifyModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return data_->size(); - } - - return 0; -} - - -QVariant ChangeNotifyModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData()) - { - return QVariant(); - } - int row=index.row(); - if(row < 0 || row >= data_->size()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - VNodeListItem *item=data_->itemAt(row); - assert(item); - - switch(index.column()) - { - case 0: - return QString::fromStdString(item->server()); - break; - case 1: - return QString::fromStdString(item->path()); - break; - case 2: - return item->time(); - break; - default: - break; - } - } - - return QVariant(); -} - -QVariant ChangeNotifyModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) - return QAbstractItemModel::headerData( section, orient, role ); - - if(role == Qt::DisplayRole) - { - switch ( section ) - { - case 0: return tr("Server"); - case 1: return tr("Node"); - case 2: return tr("Time of change"); - default: return QVariant(); - } - } - else if(role== Qt::ToolTipRole) - { - switch ( section ) - { - case 0: return tr("Server"); - case 1: return tr("Node"); - case 2: return tr("Time of change"); - default: return QVariant(); - } - } - return QVariant(); -} - -QModelIndex ChangeNotifyModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex ChangeNotifyModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - -VInfo_ptr ChangeNotifyModel::nodeInfo(const QModelIndex& index) const -{ - VInfo_ptr res; - - if(!index.isValid() || !hasData()) - { - return res; - } - - int row=index.row(); - if(row < 0 || row >= data_->size()) - return res; - - VNodeListItem *item=data_->itemAt(row); - Q_ASSERT(item); - VNode* vnode=item->node(); - Q_ASSERT(vnode); - - if(vnode->isServer()) - return VInfoServer::create(vnode->server()); - else - return VInfoNode::create(vnode); - - return res; -} - -void ChangeNotifyModel::slotBeginAppendRow() -{ - beginInsertRows(QModelIndex(),data_->size(),data_->size()); -} - -void ChangeNotifyModel::slotEndAppendRow() -{ - endInsertRows(); -} - -void ChangeNotifyModel::slotBeginRemoveRow(int row) -{ - beginRemoveRows(QModelIndex(),row,row); -} - -void ChangeNotifyModel::slotEndRemoveRow(int row) -{ - endRemoveRows(); -} - -void ChangeNotifyModel::slotBeginRemoveRows(int rowStart,int rowEnd) -{ - beginRemoveRows(QModelIndex(),rowStart,rowEnd); -} - -void ChangeNotifyModel::slotEndRemoveRows(int,int) -{ - endRemoveRows(); -} - - -void ChangeNotifyModel::slotBeginReset() -{ - beginResetModel(); -} - -void ChangeNotifyModel::slotEndReset() -{ - endResetModel(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyModel.hpp ecflow-4.11.1/Viewer/src/ChangeNotifyModel.hpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef CHANGENOTIFYMODEL_HPP_ -#define CHANGENOTIFYMODEL_HPP_ - -#include - -#include - -#include "VInfo.hpp" - -class VNodeList; - -class ChangeNotifyModel : public QAbstractItemModel -{ - Q_OBJECT -public: - explicit ChangeNotifyModel(QObject *parent=0); - ~ChangeNotifyModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - void setData(VNodeList *); - bool hasData() const; - VNodeList* data(); - VInfo_ptr nodeInfo(const QModelIndex&) const; - -public Q_SLOTS: - void slotBeginAppendRow(); - void slotEndAppendRow(); - void slotBeginRemoveRow(int); - void slotEndRemoveRow(int); - void slotBeginRemoveRows(int,int); - void slotEndRemoveRows(int,int); - void slotBeginReset(); - void slotEndReset(); - -protected: - VNodeList* data_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyWidget.cpp ecflow-4.11.1/Viewer/src/ChangeNotifyWidget.cpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,252 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ChangeNotifyWidget.hpp" - -#include -#include -#include -#include -#include - -#include "ChangeNotify.hpp" -#include "VNodeList.hpp" -//#include "VProperty.hpp" - -#include - -std::vector ChangeNotifyWidget::widgets_; - -ChangeNotifyButton::ChangeNotifyButton(QWidget* parent) : - QToolButton(parent), - notifier_(0) -{ - setProperty("notify","1"); - setAutoRaise(true); - setIconSize(QSize(20,20)); - - grad_.setCoordinateMode(QGradient::ObjectBoundingMode); - grad_.setStart(0,0); - grad_.setFinalStop(0,1); -} - -void ChangeNotifyButton::setNotifier(ChangeNotify* notifier) -{ - notifier_=notifier; - - setToolTip(notifier_->toolTip()); - - connect(this,SIGNAL(clicked(bool)), - this,SLOT(slotClicked(bool))); - - connect(notifier_->data(),SIGNAL(endAppendRow()), - this,SLOT(slotAppend())); - - connect(notifier_->data(),SIGNAL(endRemoveRow(int)), - this,SLOT(slotRemoveRow(int))); - - connect(notifier_->data(),SIGNAL(endReset()), - this,SLOT(slotReset())); - - updateIcon(); -} - -void ChangeNotifyButton::slotAppend() -{ - updateIcon(); -} - -void ChangeNotifyButton::slotRemoveRow(int) -{ - updateIcon(); -} - -void ChangeNotifyButton::slotReset() -{ - updateIcon(); -} - -void ChangeNotifyButton::slotClicked(bool) -{ - ChangeNotify::showDialog(notifier_); -} - -void ChangeNotifyButton::updateIcon() -{ - QString text=notifier_->widgetText(); - QString numText; - - int num=0; - if(notifier_->data()) - { - num=notifier_->data()->size(); - if(num > 0 && num < 10) - numText=QString::number(num); - else if(num > 10) - numText="9+"; - - } - - QColor bgCol(198,198,199); - QColor fgCol(20,20,20); - QColor countBgCol(58,126,194); - QColor countFgCol(Qt::white); - - if(num > 0) - { - bgCol=notifier_->fillColour(); - fgCol=notifier_->textColour(); - countBgCol=notifier_->countFillColour(); - countFgCol=notifier_->countTextColour(); - } - - QFont f; - //f.setBold(true); - f.setPointSize(f.pointSize()); - QFontMetrics fm(f); - - QFont fNum; - fNum.setBold(true); - fNum.setPointSize(f.pointSize()-1); - QFontMetrics fmNum(fNum); - - int w; - if(!numText.isEmpty()) - w=fm.width(text) + 6 + fm.width(numText) + 2; - else - w=fm.width(text) + 6; - - int h=fm.height()+2; - - QPixmap pix(w,h); - pix.fill(QColor(255,255,255,0)); - QPainter painter(&pix); - painter.setRenderHint(QPainter::Antialiasing,true); - painter.setRenderHint(QPainter::TextAntialiasing,true); - - QRect textRect(0,0,fm.width(text)+6,h); - - QColor bgLight=bgCol.lighter(110); - grad_.setColorAt(0,bgLight); - grad_.setColorAt(1,bgCol); - - painter.setBrush(QBrush(grad_)); - //painter.setBrush(bgCol); - painter.setPen(bgCol.darker(170)); - painter.drawRoundedRect(textRect,2,2); - painter.setPen(fgCol); - painter.setFont(f); - painter.drawText(textRect,Qt::AlignHCenter|Qt::AlignVCenter,text); - - if(!numText.isEmpty()) - { - QRect numRect(textRect.right()-1,0,fmNum.width(numText)+4,fmNum.ascent()+2); - painter.setBrush(countBgCol); - painter.setPen(countFgCol); - painter.drawRoundedRect(numRect,4,4); - painter.setFont(fNum); - painter.drawText(numRect,Qt::AlignHCenter|Qt::AlignVCenter,numText); - } - - setIconSize(QSize(w,h)); - setIcon(pix); - -} - -ChangeNotifyWidget::ChangeNotifyWidget(QWidget *parent) : QWidget(parent) -{ - layout_=new QHBoxLayout(this); - layout_->setContentsMargins(0,0,0,0); - layout_->setSpacing(0); - - QLabel* label=new QLabel("Notifications: ",this); - label->setStyleSheet("QLabel{color: " + QColor(60,60,60).name() + ";}"); - //QFont f; - //f.setBold(true); - //f.setPointSize(f.pointSize()-1); - //label->setFont(f); - layout_->addWidget(label); - - ChangeNotify::populate(this); - - widgets_.push_back(this); -} - -ChangeNotifyWidget::~ChangeNotifyWidget() -{ - std::vector::iterator it=std::find(widgets_.begin(),widgets_.end(),this); - if(it != widgets_.end()) - widgets_.erase(it); -} - -ChangeNotifyButton* ChangeNotifyWidget::findButton(const std::string& id) -{ - std::map::const_iterator it=buttons_.find(id); - if(it != buttons_.end()) - return it->second; - - return 0; -} - -void ChangeNotifyWidget::addTb(ChangeNotify* notifier) -{ - ChangeNotifyButton *tb=new ChangeNotifyButton(this); - tb->setNotifier(notifier); - layout_->addWidget(tb); - if(!notifier->isEnabled()) - { - tb->setEnabled(false); - tb->hide(); - } - - buttons_[notifier->id()]=tb; - updateVisibility(); -} - -void ChangeNotifyWidget::setEnabled(const std::string& id,bool b) -{ - for(std::vector::iterator it=widgets_.begin(); it!= widgets_.end(); ++it) - { - if(ChangeNotifyButton* tb=(*it)->findButton(id)) - { - tb->setEnabled(b); - tb->setVisible(b); - (*it)->updateVisibility(); - } - - } -} - -void ChangeNotifyWidget::updateVisibility() -{ - setVisible(hasVisibleButton()); -} - -bool ChangeNotifyWidget::hasVisibleButton() const -{ - for(std::map::const_iterator it=buttons_.begin(); - it != buttons_.end(); ++it) - { - if(it->second->isEnabled()) - return true; - } - return false; -} - -void ChangeNotifyWidget::updateSettings(const std::string& id) -{ - for(std::vector::iterator it=widgets_.begin(); it!= widgets_.end(); ++it) - { - if(ChangeNotifyButton* tb=(*it)->findButton(id)) - { - tb->updateIcon(); - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/ChangeNotifyWidget.hpp ecflow-4.11.1/Viewer/src/ChangeNotifyWidget.hpp --- ecflow-4.9.0/Viewer/src/ChangeNotifyWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ChangeNotifyWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ -#define VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ - -#include -#include -#include - -#include -#include -#include - -class QHBoxLayout; -class QLabel; -class QSignalMapper; - -class ChangeNotify; -class VProperty; -class ChangeNotifyWidget; - -class ChangeNotifyButton : public QToolButton -{ -Q_OBJECT - -friend class ChangeNotifyWidget; - -public: - explicit ChangeNotifyButton(QWidget* parent=0); - - void setNotifier(ChangeNotify*); - -public Q_SLOTS: - void slotAppend(); - void slotRemoveRow(int); - void slotReset(); - void slotClicked(bool); - -protected: - void updateIcon(); - - ChangeNotify* notifier_; - QLinearGradient grad_; -}; - -class ChangeNotifyWidget : public QWidget -{ -friend class ChangeNotify; - -public: - explicit ChangeNotifyWidget(QWidget *parent=0); - ~ChangeNotifyWidget(); - - void updateVisibility(); - static void setEnabled(const std::string& id,bool b); - static void updateSettings(const std::string& id); - -protected: - void addTb(ChangeNotify*); - ChangeNotifyButton* findButton(const std::string& id); - bool hasVisibleButton() const; - - QHBoxLayout* layout_; - std::map buttons_; - static std::vector widgets_; -}; - -#endif /* VIEWER_SRC_CHANGENOTIFYWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/ClockWidget.cpp ecflow-4.11.1/Viewer/src/ClockWidget.cpp --- ecflow-4.9.0/Viewer/src/ClockWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ClockWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ClockWidget.hpp" -#include "PropertyMapper.hpp" -#include "VConfig.hpp" - -#include -#include - -#include -#include - -ClockWidget::ClockWidget(QWidget* parent) : QLabel(parent), showSec_(false) -{ - timer_=new QTimer(this); - connect(timer_,SIGNAL(timeout()), - this,SLOT(slotTimeOut())); - - QString sh="QLabel {color: rgb(34,107,138); margin-left: 5px; }"; - setStyleSheet(sh); - - std::vector propVec; - propVec.push_back("view.clock.showClock"); - propVec.push_back("view.clock.clockFormat"); - prop_=new PropertyMapper(propVec,this); - Q_ASSERT(prop_); - prop_->initObserver(this); - - adjustTimer(); -} - -ClockWidget::~ClockWidget() -{ - delete prop_; -} - -void ClockWidget::renderTime() -{ - if(isHidden()) - return; - - QTime t=QTime::currentTime(); - if(showSec_) - { - setText("" + t.toString(" HH:mm:ss ") + ""); - } - else - { - setText("" +t.toString(" HH:mm ") + ""); - } -} - -void ClockWidget::slotTimeOut() -{ - renderTime(); - if(!showSec_) - adjustTimer(); -} - -void ClockWidget::adjustTimer() -{ - if(isHidden()) - { - timer_->stop(); - } - else - { - if(showSec_) - { - timer_->start(1000); - } - else - { - int sec=QTime::currentTime().second(); - int interval=(sec <=1)?(60):(60-sec+1); - interval*=1000; - if(timer_->interval() != interval || !timer_->isActive()) - { - timer_->start(interval); - } - } - } -} - -void ClockWidget::notifyChange(VProperty* p) -{ - if(p->path() == "view.clock.showClock") - { - bool v=p->value().toBool(); - if(v!= isVisible()) - { - setVisible(v); - renderTime(); - adjustTimer(); - } - } - else if(p->path() == "view.clock.clockFormat") - { - QString v=p->valueAsString(); - bool showSec=false; - if(v=="hhmmss") - showSec=true; - - if(showSec != showSec_) - { - showSec_=showSec; - renderTime(); - adjustTimer(); - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/ClockWidget.hpp ecflow-4.11.1/Viewer/src/ClockWidget.hpp --- ecflow-4.9.0/Viewer/src/ClockWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ClockWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef CLOCKWIDGET_HPP -#define CLOCKWIDGET_HPP - -#include - -#include "VProperty.hpp" - -class QTimer; -class PropertyMapper; - -class ClockWidget : public QLabel, public VPropertyObserver -{ - Q_OBJECT -public: - ClockWidget(QWidget* parent=0); - ~ClockWidget(); - - void notifyChange(VProperty*); - -protected Q_SLOTS: - void slotTimeOut(); - -protected: - void renderTime(); - void adjustTimer(); - - PropertyMapper* prop_; - QTimer* timer_; - bool showSec_; -}; - -#endif // CLOCKWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/src/CMakeLists.txt ecflow-4.11.1/Viewer/src/CMakeLists.txt --- ecflow-4.9.0/Viewer/src/CMakeLists.txt 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CMakeLists.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,499 +0,0 @@ - -set(viewer_srcs - ViewerMain.cpp - AboutDialog.cpp - AbstractNodeModel.cpp - AbstractNodeView.cpp - AbstractSearchLine.cpp - AbstractTextEditSearchInterface.cpp - ActionHandler.cpp - Animation.cpp - AstCollateVNodesVisitor.cpp - AttributeEditor.cpp - AttributeEditorFactory.cpp - CaseSensitiveButton.cpp - ChangeNotify.cpp - ChangeNotifyDialog.cpp - ChangeNotifyEditor.cpp - ChangeNotifyModel.cpp - ChangeNotifyWidget.cpp - ClockWidget.cpp - CodeItemWidget.cpp - ComboMulti.cpp - CommandDesignerWidget.cpp - CommandHandler.cpp - CommandOutput.cpp - CommandOutputDialog.cpp - CommandOutputWidget.cpp - CompactView.cpp - ConfigListDelegate.cpp - ConnectState.cpp - CustomCommandDialog.cpp - CustomCommandHandler.cpp - CustomListWidget.cpp - CustomTabWidget.cpp - Dashboard.cpp - DashboardDialog.cpp - DashboardDock.cpp - DashboardTitle.cpp - DashboardWidget.cpp - DirectoryHandler.cpp - EditItemWidget.cpp - EditProvider.cpp - EditorInfoLabel.cpp - ExpandState.cpp - ExpandStateNode.cpp - FlagSet.hpp - FileInfoLabel.cpp - FileWatcher.cpp - FilterWidget.cpp - FontMetrics.cpp - GotoLineDialog.cpp - Highlighter.cpp - HistoryItemWidget.cpp - HtmlEdit.cpp - HtmlItemWidget.cpp - IconProvider.cpp - InfoPanel.cpp - InfoPanelItem.cpp - InfoPanelHandler.cpp - InfoProvider.cpp - InputEventLog.cpp - JobItemWidget.cpp - LabelEditor.cpp - LimitEditor.cpp - LineEdit.cpp - LogModel.cpp - LogProvider.cpp - LogTruncator.cpp - MainWindow.cpp - ManualItemWidget.cpp - MenuConfigDialog.cpp - MenuHandler.cpp - MessageLabel.cpp - MessageItemWidget.cpp - MeterEditor.cpp - ModelColumn.cpp - NodeExpression.cpp - NodeFilterDialog.cpp - NodePanel.cpp - NodePathWidget.cpp - NodeQuery.cpp - NodeQueryCombo.cpp - NodeQueryEditor.cpp - NodeQueryEngine.cpp - NodeQueryHandler.cpp - NodeQueryOption.cpp - NodeQueryOptionEdit.cpp - NodeQueryResult.cpp - NodeQueryResultModel.cpp - NodeQueryResultView.cpp - NodeQueryViewDelegate.cpp - NodeSearchDialog.cpp - NodeSearchWidget.cpp - NodeViewBase.cpp - NodeViewDelegate.cpp - NodeWidget.cpp - OneLineTextEdit.cpp - OutputBrowser.cpp - OutputCache.cpp - OutputClient.cpp - OutputFetchInfo.cpp - OutputFileClient.cpp - OutputDirClient.cpp - OutputModel.cpp - OutputItemWidget.cpp - OutputFileProvider.cpp - OutputDirProvider.cpp - OverviewItemWidget.cpp - OverviewProvider.cpp - Palette.cpp - PlainTextEdit.cpp - PlainTextSearchLine.cpp - PlainTextSearchInterface.cpp - PropertyDialog.cpp - PropertyEditor.cpp - PropertyLine.cpp - PropertyMapper.cpp - RectMetrics.cpp - RepeatEditor.cpp - RichTextEdit.cpp - RichTextSearchInterface.cpp - RichTextSearchLine.cpp - SaveSessionAsDialog.cpp - ScriptItemWidget.cpp - ServerComInfoWidget.cpp - ServerComQueue.cpp - ServerComThread.cpp - ServerDefsAccess.cpp - ServerHandler.cpp - ServerFilter.cpp - ServerItem.cpp - ServerList.cpp - ServerListDialog.cpp - ServerListSyncWidget.cpp - ServerSettingsItemWidget.cpp - SessionDialog.cpp - SessionRenameDialog.cpp - SessionHandler.cpp - ShellCommand.cpp - Sound.cpp - StandardView.cpp - StringMatchCombo.cpp - StringMatchMode.cpp - SuiteItemWidget.cpp - SuiteFilter.cpp - SuiteFilterObserver.hpp - SuiteModel.cpp - TabWidget.cpp - TableFilterWidget.cpp - TableNodeModel.cpp - TableNodeSortModel.cpp - TableNodeView.cpp - TableNodeViewDelegate.cpp - TableNodeWidget.cpp - TextEditSearchLine.cpp - TextFilterHandler.cpp - TextFilterHandlerDialog.cpp - TextFilterWidget.cpp - TextFormat.cpp - TimeItemWidget.cpp - TreeNodeModel.cpp - TreeNodeView.cpp - TreeNodeViewDelegate.cpp - TreeNodeWidget.cpp - TreeView.cpp - TriggerEditor.cpp - TriggerItemWidget.cpp - TriggerTextWidget.cpp - TriggerCollector.cpp - TriggerTableModel.cpp - TriggerTableView.cpp - TriggerTableWidget.cpp - TriggerViewDelegate.cpp - TriggeredScanner.cpp - UIDebug.cpp - UiLog.cpp - UpdateTimer.cpp - UserMessage.cpp - VAttribute.cpp - VAttributeType.cpp - VConfig.cpp - VConfigLoader.cpp - VDateAttr.cpp - VDir.cpp - VEventAttr.cpp - VFilter.cpp - VIcon.cpp - VInfo.cpp - VFile.cpp - VFileInfo.cpp - VItem.cpp - VItemPathParser.cpp - VLabelAttr.cpp - VLateAttr.cpp - VLimitAttr.cpp - VLimiterAttr.cpp - VMeterAttr.cpp - VModelData.cpp - VNode.cpp - VNodeList.cpp - VNodeMover.cpp - VNState.cpp - VParam.cpp - VProperty.cpp - VRepeatAttr.cpp - VReply.cpp - VServerSettings.cpp - VSettings.cpp - VSState.cpp - VTask.cpp - VTaskNode.cpp - VTimeAttr.cpp - VTree.cpp - VTriggerAttr.cpp - VGenVarAttr.cpp - VUserVarAttr.cpp - VariableEditor.cpp - VariableModel.cpp - VariableModelData.cpp - VariableItemWidget.cpp - VariableSearchLine.cpp - VariableView.cpp - ViewerUtil.cpp - WhyItemWidget.cpp - WidgetNameProvider.cpp - WmWorkspaceHandler.cpp - ZombieItemWidget.cpp - ZombieModel.cpp - TextPager/TextPagerCursor.cpp - TextPager/TextPagerDocument.cpp - TextPager/TextPagerEdit.cpp - TextPager/TextPagerLayout_p.cpp - TextPager/TextPagerSearchHighlighter.cpp - TextPager/TextPagerSearchInterface.cpp - TextPager/TextPagerSection.cpp - TextPager/TextPagerWidget.cpp - TextPager/syntaxhighlighter.cpp) - -if(ECFLOW_QT5) - include_directories(${ECFLOW_QT_INCLUDE_DIR}) -else() - include(${QT_USE_FILE}) - set(ECFLOW_QT_LIBRARIES ${QT_LIBRARIES}) -endif() - - -set(viewer_moc_files AbstractNodeModel.hpp - AbstractNodeView.hpp - AbstractSearchLine.hpp - ActionHandler.hpp - Animation.hpp - AttributeEditor.hpp - CaseSensitiveButton.hpp - ChangeNotifyDialog.hpp - ChangeNotifyEditor.hpp - ChangeNotifyModel.hpp - ChangeNotifyWidget.hpp - ClockWidget.hpp - CodeItemWidget.hpp - ComboMulti.hpp - CommandDesignerWidget.hpp - CommandOutput.hpp - CommandOutputDialog.hpp - CommandOutputWidget.hpp - CustomCommandDialog.hpp - CustomListWidget.hpp - Dashboard.hpp - DashboardDialog.hpp - DashboardDock.hpp - DashboardTitle.hpp - DashboardWidget.hpp - EditItemWidget.hpp - FileWatcher.hpp - FilterWidget.hpp - GotoLineDialog.hpp - HistoryItemWidget.hpp - HtmlItemWidget.hpp - InfoPanel.hpp - InputEventLog.hpp - LabelEditor.hpp - LimitEditor.hpp - LineEdit.hpp - LogProvider.hpp - LogTruncator.hpp - MainWindow.hpp - MenuConfigDialog.hpp - MessageItemWidget.hpp - MeterEditor.hpp - NodeFilterDialog.hpp - NodePanel.hpp - NodePathWidget.hpp - NodeQueryCombo.hpp - NodeQueryEditor.hpp - NodeQueryOptionEdit.hpp - NodeQueryEngine.hpp - NodeQueryResult.hpp - NodeQueryResultModel.hpp - NodeQueryResultView.hpp - NodeSearchDialog.hpp - NodeSearchWidget.hpp - NodeWidget.hpp - OneLineTextEdit.hpp - OutputBrowser.hpp - OutputCache.hpp - OutputClient.hpp - OutputFileClient.hpp - OutputDirClient.hpp - OutputItemWidget.hpp - OutputFileProvider.hpp - OutputDirProvider.hpp - PlainTextEdit.hpp - PropertyDialog.hpp - PropertyEditor.hpp - PropertyLine.hpp - SaveSessionAsDialog.hpp - RepeatEditor.hpp - RichTextEdit.hpp - ServerComInfoWidget.hpp - ServerComQueue.hpp - ServerComThread.hpp - ServerHandler.hpp - ServerListDialog.hpp - ServerListSyncWidget.hpp - ServerSettingsItemWidget.hpp - SessionDialog.hpp - SessionRenameDialog.hpp - ShellCommand.hpp - StringMatchCombo.hpp - SuiteItemWidget.hpp - TabWidget.hpp - TableFilterWidget.hpp - TableNodeModel.hpp - TableNodeView.hpp - TableNodeViewDelegate.hpp - TableNodeWidget.hpp - TextEditSearchLine.hpp - TextFilterHandlerDialog.hpp - TextFilterWidget.hpp - TreeNodeModel.hpp - TreeNodeView.hpp - TreeNodeViewDelegate.hpp - TreeNodeWidget.hpp - TriggerEditor.hpp - TriggerItemWidget.hpp - TriggerTableView.hpp - TriggerTableWidget.hpp - TriggeredScanner.hpp - VariableEditor.hpp - VariableItemWidget.hpp - VariableModel.hpp - VariableModelData.hpp - VariableSearchLine.hpp - VFilter.hpp - VModelData.hpp - VNodeList.hpp - WhyItemWidget.hpp - ZombieItemWidget.hpp - TextPager/TextPagerCursor_p.hpp - TextPager/TextPagerDocument.hpp - TextPager/TextPagerDocument_p.hpp - TextPager/TextPagerEdit.hpp - TextPager/TextPagerEdit_p.hpp - TextPager/TextPagerSection_p.hpp - TextPager/TextPagerWidget.hpp - TextPager/syntaxhighlighter.hpp -) - -set(viewer_wrap_ui_files - AboutDialog.ui - AttributeEditorDialog.ui - ChangeNotifyDialog.ui - ChangeNotifyDialogWidget.ui - ChangeNotifyEditor.ui - CommandDesignerWidget.ui - CommandOutputDialog.ui - CommandOutputWidget.ui - CustomCommandDialog.ui - CodeItemWidget.ui - DashboardDialog.ui - DashboardDockTitleWidget.ui - EditItemWidget.ui - GotoLineDialog.ui - HistoryItemWidget.ui - HtmlItemWidget.ui - InfoPanel.ui - LabelEditorWidget.ui - LimitEditorWidget.ui - MainWindow.ui - MessageItemWidget.ui - MenuConfigDialog.ui - MeterEditorWidget.ui - NodeFilterDialog.ui - NodeQueryEditor.ui - NodeQuerySaveDialog.ui - NodeSearchDialog.ui - NodeSearchWidget.ui - OutputItemWidget.ui - PropertyDialog.ui - PropertyEditor.ui - RepeatEditorWidget.ui - SaveSessionAsDialog.ui - SearchLineWidget.ui - ServerAddDialog.ui - ServerEditDialog.ui - ServerListDialog.ui - ServerListSyncWidget.ui - ServerSettingsItemWidget.ui - SessionDialog.ui - SessionRenameDialog.ui - SuiteItemWidget.ui - TableFilterWidget.ui - TableNodeWidget.ui - TextFilterAddDialog.ui - TextFilterHandlerDialog.ui - TextFilterWidget.ui - TimeItemWidget.ui - TreeNodeWidget.ui - TriggerEditorWidget.ui - TriggerItemWidget.ui - TriggerTableWidget.ui - VariableAddDialog.ui - VariableEditorWidget.ui - VariablePropDialog.ui - VariableItemWidget.ui - ZombieItemWidget.ui -) - -if(ECFLOW_QT5) - QT5_WRAP_CPP(VIEWER_MOC ${viewer_moc_files} ) - QT5_ADD_RESOURCES (VIEWER_RES viewer.qrc) - QT5_WRAP_UI (VIEWER_FORMS_HEADERS ${viewer_wrap_ui_files}) -else() - QT4_WRAP_CPP(VIEWER_MOC ${viewer_moc_files} ) - QT4_ADD_RESOURCES (VIEWER_RES viewer.qrc) - QT4_WRAP_UI (VIEWER_FORMS_HEADERS ${viewer_wrap_ui_files}) -endif() - - -# add all the images as dependencies of the resource file so that it is -# automatically recompiled when an image changes -file( GLOB image_files "${CMAKE_CURRENT_SOURCE_DIR}/../images/*.*" ) -ADD_CUSTOM_TARGET(Qt_resource_cpp DEPENDS ${VIEWER_RES}) -ADD_DEPENDENCIES(Qt_resource_cpp ${image_files}) - - - -add_definitions( -DECFLOW_SHARED_DIR="${CMAKE_INSTALL_PREFIX}/share/ecflow" ) - - -ecbuild_add_executable( TARGET ecflow_ui.x - SOURCES ${viewer_srcs} ${VIEWER_MOC} ${VIEWER_RES} ${VIEWER_FORMS_HEADERS} - INCLUDES . - ../../ACore/src - ../../ANattr/src - ../../ANode/src - ../../ANode/test - ../../Base/src - ../../Base/src/cts - ../../Base/src/stc - ../../Client/src - TextPager - ${Boost_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR} - LIBS core nodeattr node base libclient - pthread - m dl - ${ECFLOW_QT_LIBRARIES} - ${OPENSSL_LIBRARIES} -) - -# This ensures that for debug config, we only link with debug boost libs, for other configs, we link with optimised boost libs -target_link_libraries(ecflow_ui.x debug ${Boost_REGEX_LIBRARY_DEBUG} ${Boost_REGEX_LIBRARY_RELEASE} ) - - -set(build_ecflow_ui_log 0) - -if(${build_ecflow_ui_log}) - list(REMOVE_ITEM viewer_srcs ViewerMain.cpp) - ecbuild_add_executable( TARGET ecflow_ui_log - SOURCES LogEvent.cpp - ${viewer_srcs} ${VIEWER_MOC} ${VIEWER_RES} ${VIEWER_FORMS_HEADERS} - INCLUDES . - ../../ACore/src - ../../ANattr/src - ../../ANode/src - ../../ANode/test - ../../Base/src - ../../Base/src/cts - ../../Base/src/stc - ../../Client/src - ${Boost_INCLUDE_DIRS} - ${CMAKE_CURRENT_BINARY_DIR} - LIBS core nodeattr node base libclient - pthread - m - ${ECFLOW_QT_LIBRARIES} - DEFINITIONS MAIN_LOG - ) -endif() diff -Nru ecflow-4.9.0/Viewer/src/CodeItemWidget.cpp ecflow-4.11.1/Viewer/src/CodeItemWidget.cpp --- ecflow-4.9.0/Viewer/src/CodeItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CodeItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CodeItemWidget.hpp" - -#include -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#else -#include -#endif - -#include - -#include -#include - -CodeItemWidget::CodeItemWidget(QWidget *parent) : - QWidget(parent) -{ - setupUi(this); - - externalTb_->hide(); - - fileLabel_->setProperty("fileInfo","1"); - - searchLine_->setEditor(textEdit_); - searchLine_->setVisible(false); - - copyPathTb_->setEnabled(false); -} - -CodeItemWidget::~CodeItemWidget() -{ -} - -void CodeItemWidget::removeSpacer() -{ - //Remove the first spcer item!! - for(int i=0; horizontalLayout->count(); i++) - { - if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) - { - horizontalLayout->takeAt(i); - delete sp; - break; - } - } -} - -void CodeItemWidget::on_searchTb__clicked() -{ - searchLine_->setVisible(true); - searchLine_->setFocus(); - searchLine_->selectAll(); -} - -void CodeItemWidget::on_gotoLineTb__clicked() -{ - textEdit_->gotoLine(); -} - -void CodeItemWidget::on_fontSizeUpTb__clicked() -{ - //We need to call a custom slot here instead of "zoomIn"!!! - textEdit_->slotZoomIn(); -} - -void CodeItemWidget::on_fontSizeDownTb__clicked() -{ - //We need to call a custom slot here instead of "zoomOut"!!! - textEdit_->slotZoomOut(); -} - -void CodeItemWidget::on_reloadTb__clicked() -{ - reloadRequested(); -} - -//----------------------------------------- -// Copy file path -//----------------------------------------- - -void CodeItemWidget::on_copyPathTb__clicked() -{ - if(!currentFileName_.empty()) - { - QString txt=QString::fromStdString(currentFileName_); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QClipboard* cb=QGuiApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#else - QClipboard* cb=QApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#endif - } -} - -void CodeItemWidget::setCurrentFileName(const std::string& fname) -{ - currentFileName_=fname; - copyPathTb_->setEnabled(!currentFileName_.empty()); -} - -void CodeItemWidget::clearCurrentFileName() -{ - currentFileName_.clear(); - copyPathTb_->setEnabled(false); -} diff -Nru ecflow-4.9.0/Viewer/src/CodeItemWidget.hpp ecflow-4.11.1/Viewer/src/CodeItemWidget.hpp --- ecflow-4.9.0/Viewer/src/CodeItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CodeItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef CODEITEMWIDGET_HPP_ -#define CODEITEMWIDGET_HPP_ - -#include - -#include "ui_CodeItemWidget.h" - -class CodeItemWidget : public QWidget, protected Ui::CodeItemWidget -{ -Q_OBJECT - -public: - explicit CodeItemWidget(QWidget *parent=0); - ~CodeItemWidget(); - -protected Q_SLOTS: - void on_searchTb__clicked(); - void on_gotoLineTb__clicked(); - void on_fontSizeUpTb__clicked(); - void on_fontSizeDownTb__clicked(); - void on_reloadTb__clicked(); - void on_copyPathTb__clicked(); - -Q_SIGNALS: - void editorFontSizeChanged(); - -protected: - void removeSpacer(); - virtual void reloadRequested()=0; - void setCurrentFileName(const std::string&); - void clearCurrentFileName(); - -private: - std::string currentFileName_; - - -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/CodeItemWidget.ui ecflow-4.11.1/Viewer/src/CodeItemWidget.ui --- ecflow-4.9.0/Viewer/src/CodeItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CodeItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,247 +0,0 @@ - - - CodeItemWidget - - - - 0 - 0 - 510 - 465 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - false - - - QFrame::StyledPanel - - - - - - 2 - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Copy file path - - - Copy file path - - - - :/viewer/copy_path.svg:/viewer/copy_path.svg - - - true - - - - - - - Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg - - - Ctrl++ - - - true - - - - - - - Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg - - - Ctrl+- - - - true - - - - - - - ... - - - - - - - Show search bar (CTRL-F) - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - Goto line number (CTRL-L) - - - ... - - - - :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg - - - Ctrl+L - - - true - - - - - - - Reload - - - ... - - - - :/viewer/sync_black.svg:/viewer/sync_black.svg - - - true - - - - - - - - - - - - - 0 - 1 - - - - QPlainTextEdit::NoWrap - - - true - - - - - - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - FileInfoLabel - QLabel -
        FileInfoLabel.hpp
        -
        - - PlainTextSearchLine - QWidget -
        PlainTextSearchLine.hpp
        - 1 -
        - - PlainTextEdit - QPlainTextEdit -
        PlainTextEdit.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/ComboMulti.cpp ecflow-4.11.1/Viewer/src/ComboMulti.cpp --- ecflow-4.9.0/Viewer/src/ComboMulti.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ComboMulti.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,267 +0,0 @@ -#include "ComboMulti.hpp" - -#include -#include -#include -#include -#include -#include -#include - -//========================================================== -// -// ComboMulti -// -//========================================================== - -ComboMulti::ComboMulti(QWidget *widget) : - QComboBox(widget), - mode_(BasicMode), - dpyText_("") -{ - setSizeAdjustPolicy(QComboBox::AdjustToContents); - - ComboMultiDelegate* del=new ComboMultiDelegate(this); - - connect(del,SIGNAL(itemChecked()), - this,SLOT(slotChecked())); - - // set delegate items view - view()->setItemDelegate(del); - //view()->setStyleSheet(" padding: 15px; "); - // Enable editing on items view - view()->setEditTriggers(QAbstractItemView::CurrentChanged); - - // set "CheckBoxList::eventFilter" as event filter for items view - view()->viewport()->installEventFilter(this); - - // it just cool to have it as defualt ;) - view()->setAlternatingRowColors(true); -} - - -ComboMulti::~ComboMulti() -{ - -} - -bool ComboMulti::eventFilter(QObject *object, QEvent *event) -{ - // don't close items view after we release the mouse button - // by simple eating MouseButtonRelease in viewport of items view - if(event->type() == QEvent::MouseButtonRelease && object==view()->viewport()) - { - return true; - } - return QComboBox::eventFilter(object,event); -} - -void ComboMulti::paintEvent(QPaintEvent *) -{ - QStylePainter painter(this); - painter.setPen(palette().color(QPalette::Text)); - - // draw the combobox frame, focusrect and selected etc. - QStyleOptionComboBox opt; - initStyleOption(&opt); - - // if no display text been set , use "..." as default - if(dpyText_.isEmpty()) - if(mode_ == FilterMode) - opt.currentText = "ALL"; - else - opt.currentText="NONE"; - else - { - opt.currentText = dpyText_; - } - painter.drawComplexControl(QStyle::CC_ComboBox, opt); - - // draw the icon and text - painter.drawControl(QStyle::CE_ComboBoxLabel, opt); - -} - -void ComboMulti::slotChecked() -{ - QString s; - selection_.clear(); - - for(int i=0; i < model()->rowCount(); i++) - { - if(model()->data(model()->index(i,0),Qt::CheckStateRole).toBool()) - { - selection_ << model()->data(model()->index(i,0),Qt::DisplayRole).toString(); - } - } - - if(selection_.count() == 0) - s=""; - else - s=selection_.join(", "); - - setDisplayText(s); - - update(); - - Q_EMIT selectionChanged(); -} - -void ComboMulti::setSelection(QStringList lst) -{ - for(int i=0; i < count(); i++) - { - setItemData(i,false,Qt::CheckStateRole); - if(lst.contains(itemText(i))) - setItemData(i,true,Qt::CheckStateRole); - } - - slotChecked(); -} - -void ComboMulti::setSelectionByData(QStringList lst) -{ - for(int i=0; i < count(); i++) - { - setItemData(i,false,Qt::CheckStateRole); - if(lst.contains(itemData(i).toString())) - setItemData(i,true,Qt::CheckStateRole); - } - - slotChecked(); -} - -void ComboMulti::clearSelection() -{ - for(int i=0; i < count(); i++) - { - setItemData(i,false,Qt::CheckStateRole); - } - - slotChecked(); -} - -void ComboMulti::selectSoleItem() -{ - if(count() == 1) - { - setItemData(0,true,Qt::CheckStateRole); - slotChecked(); - } -} - -QStringList ComboMulti::selectionData() const -{ - QStringList lst; - for(int i=0; i < count(); i++) - { - if(itemData(i,Qt::CheckStateRole).toBool()) - lst << itemData(i,Qt::UserRole).toString(); - } - return lst; -} - -void ComboMulti::setDisplayText(QString text) -{ - dpyText_ = text; -} - -QString ComboMulti::displayText() const -{ - return dpyText_; -} - -QStringList ComboMulti::all() const -{ - QStringList lst; - for(int i=0; i < count(); i++) - lst << itemText(i); - - return lst; -} - -void ComboMulti::setMode(Mode mode) -{ - mode_=mode; -} - - -//========================================================== -// -// ComboMultiDelegate -// -//========================================================== - -ComboMultiDelegate::ComboMultiDelegate(QObject *parent) - : QItemDelegate(parent) -{ -} - -void ComboMultiDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const -{ - //Get item data - bool value = index.data(Qt::CheckStateRole).toBool(); - QString text = index.data(Qt::DisplayRole).toString(); - - // fill style options with item data - const QStyle *style = QApplication::style(); - QStyleOptionButton opt; - opt.state |= value ? QStyle::State_On : QStyle::State_Off; - opt.state |= QStyle::State_Enabled; - opt.text = text; - opt.rect = option.rect; - - style->drawControl(QStyle::CE_CheckBox,&opt,painter); -} - -QWidget* ComboMultiDelegate::createEditor(QWidget *parent, - const QStyleOptionViewItem & option , - const QModelIndex & index ) const -{ - QCheckBox *editor = new QCheckBox(parent); - return editor; -} - -void ComboMultiDelegate::setEditorData(QWidget *editor, - const QModelIndex &index) const -{ - //set editor data - QCheckBox *myEditor = static_cast(editor); - myEditor->setText(index.data(Qt::DisplayRole).toString()); - myEditor->setChecked(index.data(Qt::CheckStateRole).toBool()); - - connect(myEditor,SIGNAL(stateChanged(int)), - this,SLOT(slotEdited(int))); - -} - -void ComboMultiDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, - const QModelIndex &index) const -{ - //get the value from the editor (CheckBox) - QCheckBox *myEditor = static_cast(editor); - bool value = myEditor->isChecked(); - - if(model->data(index,Qt::CheckStateRole).toBool() != value) - { - model->setData(index,value,Qt::CheckStateRole); - Q_EMIT itemChecked(); - } - -} - -void ComboMultiDelegate::updateEditorGeometry(QWidget *editor, - const QStyleOptionViewItem &option, const QModelIndex &index ) const -{ - editor->setGeometry(option.rect); -} - -void ComboMultiDelegate::slotEdited(int) -{ - QCheckBox* cb = static_cast(sender()); - if(cb) - { - Q_EMIT commitData(cb); - } -} diff -Nru ecflow-4.9.0/Viewer/src/ComboMulti.hpp ecflow-4.11.1/Viewer/src/ComboMulti.hpp --- ecflow-4.9.0/Viewer/src/ComboMulti.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ComboMulti.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -#ifndef COMBOMULTI_HPP_ -#define COMBOMULTI_HPP_ - -#include -#include - -class ComboMulti: public QComboBox -{ -Q_OBJECT; - -public: - enum Mode {BasicMode,FilterMode}; - - enum CustomItemRole {SelectRole = Qt::UserRole+1}; - - explicit ComboMulti(QWidget *widget = 0); - virtual ~ComboMulti(); - bool eventFilter(QObject *object, QEvent *event); - virtual void paintEvent(QPaintEvent *); - void setDisplayText(QString text); - QString displayText() const; - bool hasSelection() const {return !selection_.isEmpty();} - QStringList selection() const {return selection_;} - QStringList all() const; - QStringList selectionData() const; - void selectSoleItem(); - void setSelection(QStringList); - void setSelectionByData(QStringList); - void setMode(Mode); - -public Q_SLOTS: - void slotChecked(); - void clearSelection(); - -Q_SIGNALS: - void selectionChanged(); - -private: - Mode mode_; - bool elide_; - QString dpyText_; - QStringList selection_; -}; - -class ComboMultiDelegate : public QItemDelegate -{ -Q_OBJECT - -public: - explicit ComboMultiDelegate(QObject *parent); - - void paint(QPainter *painter, const QStyleOptionViewItem &option, - const QModelIndex &index) const; - - QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem & option, - const QModelIndex & index ) const; - - void setEditorData(QWidget *editor,const QModelIndex &index) const; - void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const; - void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option, const QModelIndex &index ) const; - -protected Q_SLOTS: - void slotEdited(int); - -Q_SIGNALS: - void itemChecked() const; -}; - - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/CommandDesignerWidget.cpp ecflow-4.11.1/Viewer/src/CommandDesignerWidget.cpp --- ecflow-4.9.0/Viewer/src/CommandDesignerWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandDesignerWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,673 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - -#include - -#include "CommandDesignerWidget.hpp" -#include "CustomCommandHandler.hpp" -#include "Child.hpp" -#include "Str.hpp" -#include "NodeQueryResult.hpp" -#include "NodeExpression.hpp" - -using namespace boost; -namespace po = boost::program_options; - - -CommandDesignerWidget::CommandDesignerWidget(QWidget *parent) : QWidget(parent), menuItem_("") -{ - setupUi(this); - - - //at least for now, all commands will start with 'ecflow_client' and end with '' - commandLineEdit_->setText("ecflow_client "); - //commandLineEdit_->installEventFilter(this); - commandLineEdit_->setFocus(); - - haveSetUpDefaultCommandLine_ = false; - inCommandEditMode_ = false; - saveCommandsOnExit_ = false; - - addToContextMenuCb_->setChecked(true); // by default, suggest saving to context menu - - // ensure the Save button is in the right state - on_commandLineEdit__textChanged(); - - saveNameLineEdit_->setPlaceholderText(tr("Unnamed")); - - currentCommandSaved_ = false; - refreshSavedCommandList(); - - - setSaveOptionsState(false, true); - - - // ensure we start on the command-builder tab - changeToTab(TAB_BUILD); - on_tabWidget__currentChanged(TAB_BUILD); // trigger the callback to ensure the correct visibility of the save buttons - - // set up and populate our list of options to the ecflow client - // - this would be more efficient if we did it only once, in a singleton, but - // it seems pretty fast so we'll leave it like this for now - initialiseComponentListDetails(); - clientOptionsDescriptions_ = new po::options_description("help" , po::options_description::m_default_line_length + 80); - cmdRegistry_.addAllOptions(*clientOptionsDescriptions_); - addClientCommandsToComponentList(); - - -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - commandLineEdit_->setClearButtonEnabled(true); -#endif - - - infoLabel_->setShowTypeTitle(false); - infoLabel_->showInfo(tr("Click command for help, double-click to insert")); - - nodeSelectionView_->enableContextMenu(false); - - - nodeListLinkLabel_->setOpenExternalLinks(false); - - - connect(nodeSelectionView_, SIGNAL(selectionChanged()), this, SLOT(on_nodeSelectionChanged())); - - - setSavedCommandsButtonStatus(); - - - // temporary - //saveCommandGroupBox_->setVisible(false); - //tabWidget_->setTabEnabled(2, false); - //savedCommandsGroupBox_->setVisible(false); - -} - -CommandDesignerWidget::~CommandDesignerWidget() -{ - delete clientOptionsDescriptions_; - - if (saveCommandsOnExit_) - CustomSavedCommandHandler::instance()->writeSettings(); - - MenuHandler::refreshCustomMenuCommands(); -} - - -void CommandDesignerWidget::initialiseComponentListDetails() -{ - - // we don't want all the available commands to appear in the component list because they - // are not all relevant for one reason or another - - componentBlacklist_.clear(); - - // The following rely on standard out: - componentBlacklist_.push_back("help"); - componentBlacklist_.push_back("get"); - componentBlacklist_.push_back("get_state"); - componentBlacklist_.push_back("ch_suites"); - componentBlacklist_.push_back("migrate"); - componentBlacklist_.push_back("ping"); - componentBlacklist_.push_back("server_version"); - componentBlacklist_.push_back("stats"); - componentBlacklist_.push_back("status"); - componentBlacklist_.push_back("suites"); - componentBlacklist_.push_back("version"); - componentBlacklist_.push_back("zombie_get"); - - // The following only make sense for a persistent client. (like GUI, python): - componentBlacklist_.push_back("news"); - componentBlacklist_.push_back("sync"); - componentBlacklist_.push_back("sync_full"); - - // The following require a prompt: - componentBlacklist_.push_back("delete"); - componentBlacklist_.push_back("terminate"); - componentBlacklist_.push_back("halt"); - - // The following only make sense in a group: - componentBlacklist_.push_back("show"); - componentBlacklist_.push_back("why"); -} - - -void CommandDesignerWidget::initialiseCommandLine() -{ - if (!haveSetUpDefaultCommandLine_) - { - // put the cursor between the two pre-defined strings - this is where the command will go - commandLineEdit_->home(false); - commandLineEdit_->setCursorPosition(14); - commandLineEdit_->deselect(); - - haveSetUpDefaultCommandLine_ = true; - } -} - - -void CommandDesignerWidget::changeToTab(TabIndexes i) -{ - tabWidget_->setCurrentIndex(i); -} - - -void CommandDesignerWidget::setNodes(std::vector &nodes) -{ - nodes_ = nodes; - - - // populate the list of nodes - nodeSelectionView_->setSourceModel(&nodeModel_); - nodeModel_.slotBeginReset(); - nodeModel_.data()->add(nodes); - nodeModel_.slotEndReset(); - - // all should be selected at first - nodeSelectionView_->selectAll(); - - - on_nodeSelectionChanged(); // get the number of selected nodes and act accordingly -} - - - -// when the user clicks on the hyperlinked label which tells them how many nodes -// will be acted on, we want to switch to the Nodes tab -void CommandDesignerWidget::on_nodeListLinkLabel__linkActivated(const QString &link) -{ - if (link == "#nodes") - { - changeToTab(TAB_NODES); - } -} - - -void CommandDesignerWidget::setNodeNumberLinkText(int numNodes) -{ - QString s; - - s = (numNodes == 1) ? "" : "s"; - - nodeListLinkLabel_->setText(tr("Command will be run on %1 node%2: click here for list").arg(numNodes).arg(s)); -} - -// triggered when the user changes their node selection -void CommandDesignerWidget::on_nodeSelectionChanged() -{ - setNodeNumberLinkText(selectedNodes().size()); - on_commandLineEdit__textChanged(); // trigger the enabling/disabling of the Run button -} - - -std::vector& CommandDesignerWidget::selectedNodes() -{ - nodeSelectionView_->getListOfSelectedNodes(nodes_); - return nodes_; -} - -void CommandDesignerWidget::addClientCommandsToComponentList() -{ - // sort the commands into alphabetical order - std::vector< boost::shared_ptr > options = clientOptionsDescriptions_->options(); - - std::sort(options.begin(),options.end(), - boost::bind(std::less(), - boost::bind(&po::option_description::long_name,_1), - boost::bind(&po::option_description::long_name,_2))); - - // loop through the sorted commands and add them to the list - size_t numOptions = options.size(); - - for(size_t i = 0; i < numOptions; i++) - { - std::string longName(options[i]->long_name()); - if (!ecf::Child::valid_child_cmd(longName)) // do not show the 'child' options - { - if (std::find(componentBlacklist_.begin(), componentBlacklist_.end(), longName) == componentBlacklist_.end()) // not in blacklist? - { - componentsList_->addItem(QString("--") + QString::fromStdString(longName)); - QString statusTip(QString::fromStdString(longName)); - componentsList_->item(componentsList_->count()-1)->setStatusTip(statusTip); - } - } - } - - // ensure the itemEntered slot is triggered - componentsList_->setMouseTracking(true); - - // when the mouse hovers over an item, set the background colour of that item - componentsList_->setStyleSheet("QListWidget::item:hover {background-color:#FFFFDD;color:black}"); -} - - -void CommandDesignerWidget::showCommandHelp(QListWidgetItem *item, bool showFullHelp) -{ - // get the command name - QString qCommand(item->text()); - std::string command = qCommand.toStdString(); - ecf::Str::replace_all(command, "--", ""); // remove the "--" from the start - - - // try to find it in our list of commands - const po::option_description* od = clientOptionsDescriptions_->find_nothrow(command, - false, /*approx, will find nearest match*/ - false, /*long_ignore_case = false*/ - false /*short_ignore_case = false*/ - ); - - if (od) - { - // get the description, but only take the first line - std::vector< std::string > lines; - ecf::Str::split(od->description(),lines,"\n"); - if (!lines.empty()) - { - QString text = qCommand + QString(": "); - commandHelpLabel_->setText(text + QString::fromStdString(lines[0])); - } - - if (showFullHelp) - { - commandManPage_->setText(qCommand + "\n\n" + QString::fromStdString(od->description())); - } - } - else - { - // not a command that we have help text for - commandHelpLabel_->setText(""); - } -} - - -void CommandDesignerWidget::on_tabWidget__currentChanged(int index) -{ - //bool onSaveTab = (index == TAB_SAVE); - //saveCommandGroupBox_->setVisible(onSaveTab); - //saveOptionsButton_->setVisible(!onSaveTab); -} - - - -// when the mouse moves over a command, display the help text for it -void CommandDesignerWidget::on_componentsList__itemEntered(QListWidgetItem *item) -{ - showCommandHelp(item, false); - initialiseCommandLine(); -} - -// when the mouse is clicked on a command, display the help text for it -void CommandDesignerWidget::on_componentsList__itemClicked(QListWidgetItem *item) -{ - showCommandHelp(item, true); - commandLineEdit_->setFocus(); // to keep the text cursor visible -} - -// when the mouse is double-clicked on a command, insert it into the command line box -void CommandDesignerWidget::on_componentsList__itemDoubleClicked(QListWidgetItem *item) -{ - insertComponent(item); - commandLineEdit_->setFocus(); // to keep the text cursor visible -} - - -void CommandDesignerWidget::insertComponent(QListWidgetItem *item) -{ - commandLineEdit_->insert(item->text() + " "); -} - - - -void CommandDesignerWidget::on_commandLineEdit__textChanged() -{ - // only allow to run a non-empty command, and on 1 or more nodes - runButton_->setEnabled((!commandLineEdit_->text().isEmpty()) && nodes_.size() > 0); - - currentCommandSaved_ = false; - updateSaveButtonStatus(); -} - -void CommandDesignerWidget::on_saveNameLineEdit__textChanged() -{ - currentCommandSaved_ = false; - updateSaveButtonStatus(); -} - -void CommandDesignerWidget::on_addToContextMenuCb__stateChanged() -{ - currentCommandSaved_ = false; - updateSaveButtonStatus(); -} - - -void CommandDesignerWidget::updateSaveButtonStatus() -{ - // inCommandEditMode_ means we're editing a command from the saved list - if (inCommandEditMode_) - { - overwriteButton_->setEnabled(true); - saveAsNewButton_->setEnabled(false); - } - else - { - overwriteButton_->setEnabled(false); - saveAsNewButton_->setEnabled(true); - } - - // the cancel button is only available if we're in edit mode - cancelSaveButton_->setEnabled(inCommandEditMode_); -} - - -void CommandDesignerWidget::setSavedCommandsButtonStatus() -{ - int row = savedCommandsTable_->currentRow(); - bool isRowSelected = (row != -1); - deleteCommandButton_ ->setEnabled(isRowSelected); - editCommandButton_ ->setEnabled(isRowSelected); - duplicateCommandButton_->setEnabled(isRowSelected); - useCommandButton_ ->setEnabled(isRowSelected); - - upButton_ ->setEnabled(isRowSelected && row != 0); // not the first row - downButton_->setEnabled(isRowSelected && row != savedCommandsTable_->rowCount()-1); // not the last row -} - - -bool CommandDesignerWidget::validSaveName(const std::string &name) -{ - // name empty? - if (name.empty()) - { - QMessageBox::critical(0,QObject::tr("Custom command"), tr("Please enter a name for the command")); - return false; - } - - - // is there already a command with this name? - int commandWithThisName = CustomSavedCommandHandler::instance()->findIndexFromName(name); - bool nameUnique; - if (inCommandEditMode_) - nameUnique = (commandWithThisName == -1 || commandWithThisName == savedCommandsTable_->currentRow()); - else - nameUnique = (commandWithThisName == -1); - - if (!nameUnique) - { - QMessageBox::critical(0,QObject::tr("Custom command"), tr("A command with that name already exists - please choose another name")); - return false; - } - else - { - return true; - } -} - -void CommandDesignerWidget::selectRow(int row) -{ - savedCommandsTable_->setCurrentCell(row, 0); -} - - -void CommandDesignerWidget::selectLastSavedCommand() -{ - int lastRow = savedCommandsTable_->rowCount()-1; - selectRow(lastRow); -} - -// swap the commands in these two positions and select the one which will end up in the second position -void CommandDesignerWidget::swapSavedCommands(int i1, int i2) -{ - CustomSavedCommandHandler::instance()->swapCommandsByIndex(i1, i2); - refreshSavedCommandList(); - selectRow(i2); - setSavedCommandsButtonStatus(); - saveCommandsOnExit_ = true; // we won't save the commands yet, but mark for save on exit -} - - -void CommandDesignerWidget::on_saveAsNewButton__clicked() -{ - std::string name, command; - bool context; - - name = saveNameLineEdit_->text().toStdString(); - command = commandLineEdit_->text().toStdString(); - context = addToContextMenuCb_->isChecked(); - - if (validSaveName(name)) - { - CustomSavedCommandHandler::instance()->add(name, command, context, true); - refreshSavedCommandList(); - currentCommandSaved_ = true; - updateSaveButtonStatus(); - changeToTab(TAB_SAVE); - selectLastSavedCommand(); - setSavedCommandsButtonStatus(); - setSaveOptionsState(true, true); - } -} - -void CommandDesignerWidget::on_overwriteButton__clicked() -{ - std::string name, command; - bool context; - - name = saveNameLineEdit_->text().toStdString(); - command = commandLineEdit_->text().toStdString(); - context = addToContextMenuCb_->isChecked(); - - if (validSaveName(name)) - { - CustomSavedCommandHandler::instance()->replace(savedCommandsTable_->currentRow(), name, command, context); - savedCommandsTable_->setEnabled(true); // to show that we are no longer busy editing an entry - inCommandEditMode_ = false; - refreshSavedCommandList(); - currentCommandSaved_ = true; - updateSaveButtonStatus(); - setSaveOptionsState(true, true); - } -} - - -void CommandDesignerWidget::on_runButton__clicked() -{ - std::string command = commandLineEdit_->text().toStdString(); - - // save this in the command history - CustomCommandHistoryHandler::instance()->add(command, command, true, true); - - - // close the dialogue - the calling function will call the command() function - // to retrieve the user's command - //accept(); -} - -void CommandDesignerWidget::setSaveOptionsState(bool optionsVisible, bool saveOptionsButtonEnabled) -{ - // we just switch to the Saved Commands tab - //changeToTab(TAB_SAVE); - - saveCommandGroupBox_->setVisible(optionsVisible); - QString buttonText = (optionsVisible) ? "Save Options <<" : "Save Options >>"; - saveOptionsButton_->setText(buttonText); - saveOptionsVisible_ = optionsVisible; - saveOptionsButton_->setEnabled(saveOptionsButtonEnabled); -} - - -void CommandDesignerWidget::on_saveOptionsButton__clicked() -{ - setSaveOptionsState(!saveOptionsVisible_, true); - - // we just switch to the Saved Commands tab - //changeToTab(TAB_SAVE); -} - - -void CommandDesignerWidget::on_useCommandButton__clicked() -{ - // just put the command into the command edit box - int row = savedCommandsTable_->currentRow(); - QTableWidgetItem *commandItem = savedCommandsTable_->item(row, 2); - commandLineEdit_->setText(commandItem->text()); -} - - -void CommandDesignerWidget::on_editCommandButton__clicked() -{ - int row = savedCommandsTable_->currentRow(); - - // get the details of this command from the table - QTableWidgetItem *nameItem = savedCommandsTable_->item(row, 0); - QTableWidgetItem *contextItem = savedCommandsTable_->item(row, 1); - QTableWidgetItem *commandItem = savedCommandsTable_->item(row, 2); - - - inCommandEditMode_ = true; - - // insert the details into the edit boxes - commandLineEdit_->setText(commandItem->text()); - saveNameLineEdit_->setText(nameItem->text()); - std::string context = contextItem->text().toStdString(); - addToContextMenuCb_->setChecked(CustomSavedCommandHandler::instance()->stringToBool(context)); - - savedCommandsTable_ ->setEnabled(false); // to show that we are busy editing an entry - deleteCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry - editCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry - duplicateCommandButton_->setEnabled(false); // to show that we are busy editing an entry - useCommandButton_ ->setEnabled(false); // to show that we are busy editing an entry - upButton_ ->setEnabled(false); // to show that we are busy editing an entry - downButton_ ->setEnabled(false); // to show that we are busy editing an entry - - updateSaveButtonStatus(); - - // users should have the Save Options visible - setSaveOptionsState(true, false); -} - - -void CommandDesignerWidget::on_savedCommandsTable__cellDoubleClicked(int row, int column) -{ - on_editCommandButton__clicked(); // same as selecting a cell and clicking 'edit' -} - -void CommandDesignerWidget::on_duplicateCommandButton__clicked() -{ - CustomSavedCommandHandler::instance()->duplicate(savedCommandsTable_->currentRow()); - refreshSavedCommandList(); -} - - -void CommandDesignerWidget::on_deleteCommandButton__clicked() -{ - CustomSavedCommandHandler::instance()->remove(savedCommandsTable_->currentRow()); - refreshSavedCommandList(); -} - -void CommandDesignerWidget::on_upButton__clicked() -{ - int row = savedCommandsTable_->currentRow(); - swapSavedCommands(row, row-1); -} - -void CommandDesignerWidget::on_downButton__clicked() -{ - int row = savedCommandsTable_->currentRow(); - swapSavedCommands(row, row+1); -} - - -void CommandDesignerWidget::on_cancelSaveButton__clicked() -{ - savedCommandsTable_->setEnabled(true); // to show that we are no longer busy editing an entry - inCommandEditMode_ = false; - updateSaveButtonStatus(); - refreshSavedCommandList(); - setSaveOptionsState(true, true); -} - - -void CommandDesignerWidget::refreshSavedCommandList() -{ - int n = CustomSavedCommandHandler::instance()->numCommands(); - - savedCommandsTable_->clearContents(); - - for (int i = 0; i < n; i++) - { - CustomCommand *command = CustomSavedCommandHandler::instance()->commandFromIndex(i); - addCommandToSavedList(command, i); - } - savedCommandsTable_->setRowCount(n); - setSavedCommandsButtonStatus(); -} - - -void CommandDesignerWidget::addCommandToSavedList(CustomCommand *command, int row) -{ - QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(command->name())); - QTableWidgetItem *contextItem = new QTableWidgetItem(QString::fromStdString(command->contextString())); - QTableWidgetItem *commandItem = new QTableWidgetItem(QString::fromStdString(command->command())); - - // if the command already exists (by name) then we will replaced it; - // otherwise add a new row to the table - -// int thisRow = CustomCommandHandler::instance()->findIndex(command->name()); -// if (thisRow == -1) -// thisRow = savedCommandsTable_->rowCount(); - - //contextItem->setCheckable(); - - int lastRow = savedCommandsTable_->rowCount()-1; - - if (row > lastRow) - savedCommandsTable_->insertRow(row); - - savedCommandsTable_->setItem(row, 0, nameItem); - savedCommandsTable_->setItem(row, 1, contextItem); - savedCommandsTable_->setItem(row, 2, commandItem); -} - - -void CommandDesignerWidget::on_savedCommandsTable__cellClicked(int row, int column) -{ - setSavedCommandsButtonStatus(); -} - - -// the caller has asked for a 'fake' menu item from the dialogue, so we create it here -MenuItem &CommandDesignerWidget::menuItem() -{ - // put the right information into our menu item and return a reference to it - menuItem_.setCommand(commandLineEdit_->text().toStdString()); - menuItem_.setCustom(true); - - if (menuItem_.visibleCondition() == NULL) - { - BaseNodeCondition *trueCond = new TrueNodeCondition(); - menuItem_.setEnabledCondition(trueCond); - menuItem_.setVisibleCondition(trueCond); - menuItem_.setQuestionCondition(trueCond); - } - return menuItem_; -} - - -/* -bool CommandDesignerWidget::eventFilter(QObject* object, QEvent* event) -{ - if(object == commandLineEdit_ && event->type() == QEvent::FocusIn) - { - initialiseCommandLine(); - return false; - } - return false; -} -*/ diff -Nru ecflow-4.9.0/Viewer/src/CommandDesignerWidget.hpp ecflow-4.11.1/Viewer/src/CommandDesignerWidget.hpp --- ecflow-4.9.0/Viewer/src/CommandDesignerWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandDesignerWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,103 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - - -#ifndef COMMANDDESIGNERWIDGET_HPP_ -#define COMMANDDESIGNERWIDGET_HPP_ - -#include - -class CommandLineEdit; - -#include "ui_CommandDesignerWidget.h" -#include "CustomCommandHandler.hpp" -#include "NodeQueryResultModel.hpp" -#include "MenuHandler.hpp" - -#include "CtsCmdRegistry.hpp" - -class CommandDesignerWidget : public QWidget, private Ui::commandDesignerWidget -{ - Q_OBJECT - -public: - explicit CommandDesignerWidget(QWidget *parent = 0); - ~CommandDesignerWidget(); - - MenuItem &menuItem(); - void setNodes(std::vector &nodes); - std::vector& selectedNodes(); - -public Q_SLOTS: - void insertComponent(QListWidgetItem *); - void on_commandLineEdit__textChanged(); - void on_saveNameLineEdit__textChanged(); - void on_addToContextMenuCb__stateChanged(); - void on_overwriteButton__clicked(); - void on_saveAsNewButton__clicked(); - void on_runButton__clicked(); - void on_saveOptionsButton__clicked(); - void on_editCommandButton__clicked(); - void on_useCommandButton__clicked(); - void on_duplicateCommandButton__clicked(); - void on_deleteCommandButton__clicked(); - void on_upButton__clicked(); - void on_downButton__clicked(); - void on_cancelSaveButton__clicked(); - void on_savedCommandsTable__cellClicked(int row, int column); - void on_savedCommandsTable__cellDoubleClicked(int row, int column); - void on_componentsList__itemEntered(QListWidgetItem *item); - void on_componentsList__itemClicked(QListWidgetItem *item); - void on_componentsList__itemDoubleClicked(QListWidgetItem *item); - void on_nodeListLinkLabel__linkActivated(const QString &link); - void on_nodeSelectionChanged(); - void on_tabWidget__currentChanged(int index); - QPushButton *runButton() {return runButton_;} - - -private: - enum TabIndexes {TAB_BUILD, TAB_NODES, TAB_SAVE}; - - void initialiseComponentListDetails(); - void updateSaveButtonStatus(); - void addCommandToSavedList(CustomCommand *command, int row); - void refreshSavedCommandList(); - void addClientCommandsToComponentList(); - void showCommandHelp(QListWidgetItem *item, bool showFullHelp); - void initialiseCommandLine(); - void setNodeNumberLinkText(int numNodes); - void setSavedCommandsButtonStatus(); - bool validSaveName(const std::string &name); - void changeToTab(TabIndexes i); - void selectRow(int row); - void selectLastSavedCommand(); - void swapSavedCommands(int i1, int i2); - void setSaveOptionsState(bool optionsVisible, bool saveOptionsButtonEnabled); - - //bool eventFilter(QObject* object, QEvent* event); - - bool currentCommandSaved_; - bool haveSetUpDefaultCommandLine_; - bool inCommandEditMode_; - bool saveCommandsOnExit_; - bool saveOptionsVisible_; - std::vector componentBlacklist_; - MenuItem menuItem_; - - std::vector nodes_; - NodeQueryResultModel nodeModel_; - - CtsCmdRegistry cmdRegistry_; - boost::program_options::options_description* clientOptionsDescriptions_; -}; - - - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/CommandDesignerWidget.ui ecflow-4.11.1/Viewer/src/CommandDesignerWidget.ui --- ecflow-4.9.0/Viewer/src/CommandDesignerWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandDesignerWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,398 +0,0 @@ - - - commandDesignerWidget - - - - 0 - 0 - 940 - 579 - - - - Form - - - - - - - - Components - - - - - - true - - - 2 - - - - Build command - - - - - - - - - - 0 - 1 - - - - QFrame::NoFrame - - - QFrame::Raised - - - - - - - 0 - 0 - - - - - ecflow_client - - - - - <node_name> - - - - - <full_name> - - - - - - - - - Monospace - - - - - - - - - - - Description - - - - - - - - Selected Nodes - - - - - - - - - - Saved commands - - - - - - QAbstractItemView::NoEditTriggers - - - true - - - false - - - QAbstractItemView::NoDragDrop - - - Qt::IgnoreAction - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - true - - - false - - - true - - - - Name - - - - - Context menu - - - - - Command - - - - - - - - - - &Use - - - - - - - &Edit - - - - - - - Du&plicate - - - - - - - &Remove - - - - - - - - - - - :/viewer/images/arrow_up.svg:/viewer/images/arrow_up.svg - - - - - - - - - - - :/viewer/images/arrow_down.svg:/viewer/images/arrow_down.svg - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - - - - - - - Command - - - - - - - - - - Command: - - - - - - - - - - &Run - - - - - - - - 100 - 16777215 - - - - &Save ... - - - - - - - - - - 0 - 0 - - - - <html><head/><body><p><a href="#nodes"><span style=" text-decoration: underline; color:#0057ae;">Nodes selected</span></a></p></body></html> - - - - - - - - 0 - 0 - - - - - 500 - 16777215 - - - - - - - - - - - - Name: - - - - - - - - - - - - Add to context menu - - - - - - - - - &Save - - - - - - - Save As &New - - - - - - - &Cancel - - - - - - - - - - - - - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - NodeQueryResultView - QTreeView -
        NodeQueryResultView.hpp
        -
        -
        - - saveNameLineEdit_ - tabWidget_ - savedCommandsTable_ - addToContextMenuCb_ - saveAsNewButton_ - - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/CommandHandler.cpp ecflow-4.11.1/Viewer/src/CommandHandler.cpp --- ecflow-4.9.0/Viewer/src/CommandHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,233 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CommandHandler.hpp" - -//#include "File.hpp" -//#include "NodeFwd.hpp" -//#include "ArgvCreator.hpp" -#include "Str.hpp" - -#include "ServerHandler.hpp" -#include "ShellCommand.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "VNode.hpp" - -#include -#include - -#include - -//Send the same command for a list of objects (nodes/servers) specified in a VInfo vector. -//The command is specified as a string. - -void CommandHandler::run(std::vector info, const std::string& cmd) -{ - UI_FUNCTION_LOG - - std::string realCommand(cmd); - std::vector targetServers; - - if(realCommand.empty()) - { - //UiLog().err() << " command is not recognised. Check the menu definition."; - UserMessage::message(UserMessage::ERROR, true, "command " + cmd + - " is not recognised. Check the menu definition."); - return; - } - - UiLog().dbg() << "command=" << cmd; - - std::map targetNodeNames; - std::map targetNodeFullNames; - std::map targetParentFullNames; - - //Figure out what objects (node/server) the command should be applied to - for(std::size_t i=0; i < info.size(); i++) - { - std::string nodeFullName; - std::string nodeName; - std::string parentFullName; - - if(realCommand.find("") != std::string::npos) - { - nodeName=info[i]->name(); - } - - if(realCommand.find("") != std::string::npos) - { - if(info[i]->isNode()) - nodeFullName = info[i]->node()->absNodePath(); - else if(info[i]->isServer()) - info[i]->server()->longName(); - else if(info[i]->isAttribute()) - parentFullName = info[i]->node()->absNodePath(); - } - - if(realCommand.find("") != std::string::npos) - { - if(info[i]->isNode()) - { - if(VNode *p=info[i]->node()->parent()) - parentFullName = p->absNodePath(); - } - else if(info[i]->isAttribute()) - parentFullName = info[i]->node()->absNodePath(); - } - - //Store the names per target servers - targetNodeNames[info[i]->server()] += " " + nodeName; - targetNodeFullNames[info[i]->server()] += " " + nodeFullName; - targetParentFullNames[info[i]->server()] += " " + parentFullName; - - // add this to our list of target servers? - if(std::find(targetServers.begin(), targetServers.end(), info[i]->server()) == targetServers.end()) - { - targetServers.push_back(info[i]->server()); - } - } - - // for each target server, construct and send its command - for(size_t s = 0; s < targetServers.size(); s++) - { - ServerHandler* serverHandler = targetServers[s]; - - // replace placeholders with real node names - std::string placeholder(""); - ecf::Str::replace_all(realCommand, placeholder, targetNodeFullNames[serverHandler]); - - placeholder = ""; - ecf::Str::replace_all(realCommand, placeholder, targetNodeNames[serverHandler]); - - placeholder = ""; - ecf::Str::replace_all(realCommand, placeholder, targetParentFullNames[serverHandler]); - - //Shell command - if(realCommand.find("sh ") == 0) - { - substituteVariables(realCommand,info); - UiLog().dbg() << " final command: " << realCommand; - ShellCommand::run(realCommand,cmd); - return; - } - - UiLog().dbg() << " final command: " << realCommand; - - // get the command into the right format by first splitting into tokens - // and then converting to argc, argv format - std::vector strs; - std::string delimiters(" "); - ecf::Str::split(realCommand, strs, delimiters); - - // set up and run the thread for server communication - serverHandler->runCommand(strs); - } -} - -//Send a command to a server. The command is specified as a string vector, while the node or server for that -//the command will be applied is specified in a VInfo object. -void CommandHandler::run(VInfo_ptr info,const std::vector& cmd) -{ - UI_FUNCTION_LOG - - std::vector realCommand=cmd; - - if(realCommand.empty()) - { - //UiLog().err() << " command is not recognised!"; - UserMessage::message(UserMessage::ERROR, true, "command is not recognised."); - } - - UiLog().dbg() << "command: " << commandToString(realCommand); - - //Get the name of the object for that the command will be applied - std::string nodeFullName; - std::string nodeName; - ServerHandler* serverHandler = info->server(); - - if(info->isNode() || info->isAttribute()) - { - nodeFullName = info->node()->node()->absNodePath(); - nodeName = info->node()->node()->name(); - //UserMessage::message(UserMessage::DBG, false, std::string(" --> for node: ") + nodeFullName + " (server: " + info[i]->server()->longName() + ")"); - } - else if(info->isServer()) - { - nodeFullName = "/"; - nodeName = "/"; - //UserMessage::message(UserMessage::DBG, false, std::string(" --> for server: ") + nodeFullName); - } - - //Replace placeholders with real node names - for(std::size_t i=0; i < cmd.size(); i++) - { - if(realCommand[i]=="") - realCommand[i]=nodeFullName; - else if(realCommand[i]=="") - realCommand[i]=nodeName; - } - - UiLog().dbg() << " final command: " << commandToString(realCommand); - - // get the command into the right format by first splitting into tokens - // and then converting to argc, argv format - - //std::vector strs; - //std::string delimiters(" "); - //ecf::Str::split(realCommand, strs, delimiters); - - // set up and run the thread for server communication - serverHandler->runCommand(realCommand); -} - -void CommandHandler::run(VInfo_ptr info, const std::string& cmd) -{ - std::vector commands; - - ecf::Str::split(cmd, commands); - run(info, commands); -} - -std::string CommandHandler::commandToString(const std::vector& cmd) -{ - std::string s; - for(std::vector::const_iterator it=cmd.begin(); it != cmd.end(); ++it) - { - if(!s.empty()) s+=" "; - s+=*it; - } - return s; -} - -void CommandHandler::substituteVariables(std::string& cmd,const std::vector& info) -{ - if(info.size() > 0) - { - VNode *n=info[0]->node(); - if(!n || n->isAttribute()) - return; - - QString txt=QString::fromStdString(cmd); - QString txtRes=txt; - QRegExp rx("%(.*)%"); - rx.setMinimal(true); - int pos=0; - while ((pos = rx.indexIn(txt, pos)) != -1) - { - QString name=rx.cap(1); - pos += rx.matchedLength(); - std::string value=n->findInheritedVariable(name.toStdString(),true); - txtRes.replace("%" + name + "%",QString::fromStdString(value)); - } - - cmd = txtRes.toStdString(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/CommandHandler.hpp ecflow-4.11.1/Viewer/src/CommandHandler.hpp --- ecflow-4.9.0/Viewer/src/CommandHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef COMMANDHANDLER_HPP -#define COMMANDHANDLER_HPP - -#include "VInfo.hpp" - -//Class to interpret generic commands and send them -// 1. to ServerHandler if they are "ecflow_client" commands -// 2. to ShellCommand if they are shell commands - -class CommandHandler -{ -public: - static void run(std::vector,const std::string&); - static void run(VInfo_ptr,const std::vector&); - static void run(VInfo_ptr,const std::string&); - -protected: - static std::string commandToString(const std::vector& cmd); - static void substituteVariables(std::string& cmd,const std::vector& info); -}; - -#endif // COMMANDHANDLER_HPP diff -Nru ecflow-4.9.0/Viewer/src/CommandOutput.cpp ecflow-4.11.1/Viewer/src/CommandOutput.cpp --- ecflow-4.9.0/Viewer/src/CommandOutput.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutput.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,199 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CommandOutput.hpp" - -#include "CommandOutputDialog.hpp" - -CommandOutputHandler* CommandOutputHandler::instance_=0; - -//=============================================== -// -// CommandOutput -// -//============================================== - -CommandOutput::CommandOutput(QString cmd,QString cmdDef,QDateTime runTime) : - enabled_(true), command_(cmd), commandDef_(cmdDef), - runTime_(runTime), status_(RunningStatus) -{ - -} - -void CommandOutput::appendOutput(QString txt,int maxSize,bool& trimmed) -{ - output_+=txt; - trimmed=false; - if(output_.size() > maxSize) - { - output_=output_.right(maxSize-100); - trimmed=true; - } -} - -void CommandOutput::appendError(QString txt,int maxSize,bool& trimmed) -{ - error_+=txt; - trimmed=false; - if(error_.size() > maxSize) - { - error_=error_.right(maxSize-100); - trimmed=true; - } -} - -QString CommandOutput::statusStr() const -{ - static QString finishedStr("finished"); - static QString failedStr("failed"); - static QString runningStr("running"); - - switch(status_) - { - case FinishedStatus: - return finishedStr; - case FailedStatus: - return failedStr; - case RunningStatus: - return runningStr; - default: - return QString(); - } - - return QString(); -} - -QColor CommandOutput::statusColour() const -{ - static QColor redColour(255,0,0); - static QColor greenColour(9,160,63); - static QColor blackColour(0,0,0); - - switch(status_) - { - case CommandOutput::FinishedStatus: - return blackColour; - case CommandOutput::FailedStatus: - return redColour; - case CommandOutput::RunningStatus: - return greenColour; - default: - return blackColour; - } - - return blackColour; -} - -//=============================================== -// -// CommandOutputHandler -// -//=============================================== - -CommandOutputHandler::CommandOutputHandler(QObject* parent) : - QObject(parent), - maxNum_(25), - maxOutputSize_(1000000), - maxErrorSize_(30000) -{ - -} - -CommandOutputHandler* CommandOutputHandler::instance() -{ - if(!instance_) - instance_=new CommandOutputHandler(0); - - return instance_; -} - -int CommandOutputHandler::indexOfItem(CommandOutput_ptr item) const -{ - if(!item) - return -1; - - for(int i=0; i < items_.count(); i++) - { - if(item.get() == items_[i].get()) - return i; - } - - return -1; -} - -void CommandOutputHandler::appendOutput(CommandOutput_ptr item,QString txt) -{ - if(item) - { - bool trimmed=false; - item->appendOutput(txt,maxOutputSize_,trimmed); - CommandOutputDialog::showDialog(); - if(trimmed==false) - Q_EMIT itemOutputAppend(item,txt); - else - Q_EMIT itemOutputReload(item); - } -} - -void CommandOutputHandler::appendError(CommandOutput_ptr item,QString txt) -{ - if(item) - { - bool trimmed=false; - item->appendError(txt,maxErrorSize_,trimmed); - CommandOutputDialog::showDialog(); - if(trimmed==false) - Q_EMIT itemErrorAppend(item,txt); - else - Q_EMIT itemErrorReload(item); - } -} - -void CommandOutputHandler::finished(CommandOutput_ptr item) -{ - if(item) - { - item->setStatus(CommandOutput::FinishedStatus); - Q_EMIT itemStatusChanged(item); - } -} - -void CommandOutputHandler::failed(CommandOutput_ptr item) -{ - if(item) - { - item->setStatus(CommandOutput::FailedStatus); - Q_EMIT itemStatusChanged(item); - } -} - -CommandOutput_ptr CommandOutputHandler::addItem(QString cmd,QString cmdDef,QDateTime runTime) -{ - CommandOutputDialog::showDialog(); - CommandOutput_ptr item= - CommandOutput_ptr(new CommandOutput(cmd,cmdDef,runTime)); - Q_EMIT itemAddBegin(); - items_ << item; - checkItems(); - Q_EMIT itemAddEnd(); - return item; -} - -void CommandOutputHandler::checkItems() -{ - Q_ASSERT(maxNum_ >0); - while(items_.count() > maxNum_) - { - Q_ASSERT(items_.count() > 0); - CommandOutput_ptr item=items_.first(); - item->setEnabled(false); - items_.remove(0); - } -} diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputDialog.cpp ecflow-4.11.1/Viewer/src/CommandOutputDialog.cpp --- ecflow-4.9.0/Viewer/src/CommandOutputDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include - -#include "CommandOutputDialog.hpp" -#include "SessionHandler.hpp" -#include "VConfig.hpp" -#include "WidgetNameProvider.hpp" - -CommandOutputDialog* CommandOutputDialog::dialog_=0; - -CommandOutputDialog::CommandOutputDialog(QWidget *parent) : - QDialog(parent) -{ - setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose); - - QString wt=windowTitle(); - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - //connect(queryWidget_,SIGNAL(closeClicked()), - // this,SLOT(accept())); - - //Read the qt settings - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -CommandOutputDialog::~CommandOutputDialog() -{ -} - -void CommandOutputDialog::closeEvent(QCloseEvent * event) -{ - //queryWidget_->slotStop(); //The search thread might be running!! - dialog_=0; - event->accept(); - writeSettings(); -} - -void CommandOutputDialog::accept() -{ - dialog_=0; - writeSettings(); - QDialog::accept(); -} - -void CommandOutputDialog::reject() -{ - dialog_=0; - writeSettings(); - QDialog::reject(); -} - -void CommandOutputDialog::showDialog() -{ - if(!dialog_) - { - dialog_=new CommandOutputDialog(0); - dialog_->show(); - } - dialog_->raise(); -} - -//------------------------------------------ -// Settings read/write -//------------------------------------------ - -void CommandOutputDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("CommandOutputDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - widget_->writeSettings(settings); - settings.endGroup(); -} - -void CommandOutputDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("CommandOutputDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(550,540)); - } - - widget_->readSettings(settings); - - settings.endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputDialog.hpp ecflow-4.11.1/Viewer/src/CommandOutputDialog.hpp --- ecflow-4.9.0/Viewer/src/CommandOutputDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef COMMANDOUTPUTDIALOG_HPP -#define COMMANDOUTPUTDIALOG_HPP - -#include - -#include "ui_CommandOutputDialog.h" - -class ShellCommand; - -class CommandOutputDialog : public QDialog, protected Ui::CommandOutputDialog -{ - Q_OBJECT - -public: - static void showDialog(); - -protected Q_SLOTS: - void accept(); - void reject(); - -protected: - explicit CommandOutputDialog(QWidget *parent = 0); - ~CommandOutputDialog(); - - void closeEvent(QCloseEvent * event); - -private: - void readSettings(); - void writeSettings(); - - static CommandOutputDialog* dialog_; -}; - -#endif // COMMANDOUTPUTDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputDialog.ui ecflow-4.11.1/Viewer/src/CommandOutputDialog.ui --- ecflow-4.9.0/Viewer/src/CommandOutputDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ - - - CommandOutputDialog - - - - 0 - 0 - 400 - 300 - - - - Shell command output - - - - 5 - - - 4 - - - 2 - - - 4 - - - 4 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - CommandOutputWidget - QWidget -
        CommandOutputWidget.hpp
        - 1 -
        -
        - - - - buttonBox - accepted() - CommandOutputDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - CommandOutputDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/CommandOutput.hpp ecflow-4.11.1/Viewer/src/CommandOutput.hpp --- ecflow-4.9.0/Viewer/src/CommandOutput.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutput.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef COMMANDOUTPUT_HPP -#define COMMANDOUTPUT_HPP - -#include -#include -#include -#include -#include - -#include -#include - -class CommandOutput; -typedef boost::shared_ptr CommandOutput_ptr; - -class CommandOutputHandler; - -class CommandOutput -{ - friend class CommandOutputHandler; - -public: - enum Status {RunningStatus,FinishedStatus,FailedStatus}; - - QString command() const {return command_;} - QString commandDefinition() const {return commandDef_;} - QDateTime runTime() const {return runTime_;} - QString output() const {return output_;} - QString error() const {return error_;} - Status status() const {return status_;} - QString statusStr() const; - QColor statusColour() const; - bool isEnabled() const {return enabled_;} - -protected: - CommandOutput(QString cmd,QString cmdDef,QDateTime runTime); - - void appendOutput(QString,int,bool&); - void appendError(QString,int,bool&); - void setStatus(Status s) {status_=s;} - void setEnabled(bool b) {enabled_=b;} - - bool enabled_; - QString command_; - QString commandDef_; - QDateTime runTime_; - QString output_; - QString error_; - Status status_; -}; - -class CommandOutputHandler : public QObject -{ - Q_OBJECT -public: - static CommandOutputHandler* instance(); - - void appendOutput(CommandOutput_ptr,QString); - void appendError(CommandOutput_ptr,QString); - void finished(CommandOutput_ptr); - void failed(CommandOutput_ptr); - - CommandOutput_ptr addItem(QString cmd,QString cmdDef,QDateTime runTime); - QVector items() const {return items_;} - int itemCount() const {return items_.count();} - int indexOfItem(CommandOutput_ptr) const; - -Q_SIGNALS: - void itemAddBegin(); - void itemAddEnd(); - void itemOutputAppend(CommandOutput_ptr,QString); - void itemErrorAppend(CommandOutput_ptr,QString); - void itemOutputReload(CommandOutput_ptr); - void itemErrorReload(CommandOutput_ptr); - void itemStatusChanged(CommandOutput_ptr); - void itemsReloaded(); - -protected: - CommandOutputHandler(QObject*); - void checkItems(); - - static CommandOutputHandler* instance_; - int maxNum_; - int maxOutputSize_; - int maxErrorSize_; - QVector items_; -}; - - -#endif // COMMANDOUTPUT_HPP diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputWidget.cpp ecflow-4.11.1/Viewer/src/CommandOutputWidget.cpp --- ecflow-4.9.0/Viewer/src/CommandOutputWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,424 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CommandOutputWidget.hpp" - -#include -#include - -#include "ModelColumn.hpp" -#include "CommandOutput.hpp" -#include "TextFormat.hpp" -#include "ViewerUtil.hpp" - -CommandOutputModel::CommandOutputModel(QObject *parent) : - QAbstractItemModel(parent), - columns_(0) -{ - columns_=ModelColumn::def("output_columns"); - - assert(columns_); -} - -CommandOutputModel::~CommandOutputModel() -{ -} - -bool CommandOutputModel::hasData() const -{ - return CommandOutputHandler::instance()->itemCount() > 0; -} - -void CommandOutputModel::dataIsAboutToChange() -{ - beginResetModel(); -} - -void CommandOutputModel::dataChanged() -{ - endResetModel(); -} - -int CommandOutputModel::columnCount( const QModelIndex& /*parent */) const -{ - return columns_->count(); -} - -int CommandOutputModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return CommandOutputHandler::instance()->itemCount(); - } - - return 0; -} - -Qt::ItemFlags CommandOutputModel::flags ( const QModelIndex & index) const -{ - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -QVariant CommandOutputModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData()) - { - return QVariant(); - } - - int pos=CommandOutputHandler::instance()->itemCount()-index.row()-1; - if(pos < 0 || pos >= CommandOutputHandler::instance()->itemCount()) - return QVariant(); - - QString id=columns_->id(index.column()); - - CommandOutput_ptr item=CommandOutputHandler::instance()->items()[pos]; - if(!item) - return QVariant(); - - if(role == Qt::DisplayRole) - { - if(id == "command") - return item->command(); - else if(id == "status") - { - return item->statusStr(); - } - else if(id == "runtime") - return item->runTime().toString("yyyy-MM-dd hh:mm:ss"); - else - return QVariant(); - } - else if(role == Qt::ForegroundRole) - { - if(id == "status") - { - return item->statusColour(); - } - return QVariant(); - } - return QVariant(); -} - -QVariant CommandOutputModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::UserRole )) - return QAbstractItemModel::headerData( section, orient, role ); - - if(role == Qt::DisplayRole) - return columns_->label(section); - else if(role == Qt::UserRole) - return columns_->id(section); - - return QVariant(); -} - -QModelIndex CommandOutputModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex CommandOutputModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - - -CommandOutput_ptr CommandOutputModel::indexToItem(const QModelIndex& idx) const -{ - if(idx.isValid() && hasData()) - { - int pos=CommandOutputHandler::instance()->itemCount()-idx.row()-1; - if(pos >= 0 || pos < CommandOutputHandler::instance()->itemCount()) - return CommandOutputHandler::instance()->items()[pos]; - } - - CommandOutput_ptr r; - return r; -} - -QModelIndex CommandOutputModel::itemToStatusIndex(CommandOutput_ptr item) const -{ - if(item) - { - int pos=CommandOutputHandler::instance()->indexOfItem(item); - if(pos != -1) - { - int row=CommandOutputHandler::instance()->itemCount()-pos-1; - if(row >=0 && row < rowCount()) - return index(row,columns_->indexOf("status")); - } - } - - return QModelIndex(); -} - -//============================================== -// -// CommandOutputWidget -// -//============================================== - -CommandOutputWidget::CommandOutputWidget(QWidget *parent) : - QWidget(parent) -{ - setupUi(this); - - infoLabel_->setProperty("fileInfo","1"); - - messageLabel_->hide(); - - model_=new CommandOutputModel(this); - - tree_->setModel(model_); - tree_->setRootIsDecorated(false); - - //Adjust the tree columns - QFont f; - QFontMetrics fm(f); - tree_->setColumnWidth(0,fm.width(" sh ecflow_client --port %ECF_PORT% --host %ECF_HOST% --stats--port %ECF_PORT%")); - tree_->setColumnWidth(1,fm.width(" running ")); - - textEdit_->setShowLineNumbers(false); - - searchLine_->setEditor(textEdit_); - searchLine_->setVisible(false); - - CommandOutputHandler* handler=CommandOutputHandler::instance(); - Q_ASSERT(handler); - - connect(handler,SIGNAL(itemAddBegin()), - this,SLOT(slotItemAddBegin())); - - connect(handler,SIGNAL(itemAddEnd()), - this,SLOT(slotItemAddEnd())); - - connect(handler,SIGNAL(itemOutputAppend(CommandOutput_ptr,QString)), - this,SLOT(slotItemOutputAppend(CommandOutput_ptr,QString))); - - connect(handler,SIGNAL(itemErrorAppend(CommandOutput_ptr,QString)), - this,SLOT(slotItemErrorAppend(CommandOutput_ptr,QString))); - - connect(handler,SIGNAL(itemOutputReload(CommandOutput_ptr)), - this,SLOT(slotItemOutputReload(CommandOutput_ptr))); - - connect(handler,SIGNAL(itemErrorReload(CommandOutput_ptr)), - this,SLOT(slotItemErrorReload(CommandOutput_ptr))); - - connect(handler,SIGNAL(itemStatusChanged(CommandOutput_ptr)), - this,SLOT(slotItemStatusChanged(CommandOutput_ptr))); - - splitter_->setCollapsible(1,false); - - //The selection changes in the view - connect(tree_->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); - - if(model_->rowCount() > 0) - tree_->setCurrentIndex(model_->index(0,0)); -} - -CommandOutputWidget::~CommandOutputWidget() -{ -} - -bool CommandOutputWidget::isCurrent(CommandOutput_ptr item) -{ - return (item && item.get() == model_->indexToItem(tree_->currentIndex()).get()); -} - -void CommandOutputWidget::slotItemSelected(const QModelIndex&,const QModelIndex&) -{ - CommandOutput_ptr current=model_->indexToItem(tree_->currentIndex()); - loadItem(current); -} - -void CommandOutputWidget::slotItemAddBegin() -{ - Q_ASSERT(model_); - model_->dataIsAboutToChange(); -} - -void CommandOutputWidget::slotItemAddEnd() -{ - Q_ASSERT(model_); - model_->dataChanged(); - if(model_->rowCount() > 0) - { - tree_->setCurrentIndex(model_->index(0,0)); - } -} - -void CommandOutputWidget::slotItemOutputAppend(CommandOutput_ptr item,QString txt) -{ - if(isCurrent(item)) - { - textEdit_->appendPlainText(txt); - } -} - -void CommandOutputWidget::slotItemErrorAppend(CommandOutput_ptr item,QString txt) -{ - if(isCurrent(item)) - { - messageLabel_->appendError(txt); - } -} - -void CommandOutputWidget::slotItemOutputReload(CommandOutput_ptr item) -{ - if(isCurrent(item)) - { - textEdit_->setPlainText(item->output()); - } -} - -void CommandOutputWidget::slotItemErrorReload(CommandOutput_ptr item) -{ - if(isCurrent(item)) - { - messageLabel_->showError(item->error()); - } -} - -void CommandOutputWidget::slotItemStatusChanged(CommandOutput_ptr item) -{ - if(item) - { - QModelIndex idx=model_->itemToStatusIndex(item); - if(idx.isValid()) - { - tree_->update(idx); - } - if(item == model_->indexToItem(tree_->currentIndex())) - { - updateInfoLabel(item); - } - } -} - -void CommandOutputWidget::loadItem(CommandOutput_ptr item) -{ - if(item) - { - textEdit_->clear(); - messageLabel_->clear(); - messageLabel_->hide(); - updateInfoLabel(item); - - //Set output text - textEdit_->setPlainText(item->output()); - - //Set error text - QString err=item->error(); - if(!err.isEmpty()) - { - messageLabel_->showError(err); - } - //return true; - } -} - -void CommandOutputWidget::updateInfoLabel(CommandOutput_ptr item) -{ - if(!item) - { - infoLabel_->clear(); - return; - } - - QColor boldCol(39,49,101); - QColor defCol(90,90,90); - QString s=Viewer::formatBoldText("Command: ",boldCol) + item->command() + "
        " + - Viewer::formatBoldText("Definition: ",boldCol) + - Viewer::formatText(item->commandDefinition(),defCol) + "
        " + - Viewer::formatBoldText("Started at: ",boldCol) + - item->runTime().toString("yyyy-MM-dd hh:mm:ss") + - Viewer::formatBoldText("  Status: ", boldCol) + - Viewer::formatText(item->statusStr(),item->statusColour()); - - infoLabel_->setText(s); -} - -void CommandOutputWidget::removeSpacer() -{ - //Remove the first spacer item!! - for(int i=0; horizontalLayout->count(); i++) - { - if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) - { - horizontalLayout->takeAt(i); - delete sp; - break; - } - } -} - -void CommandOutputWidget::on_searchTb__clicked() -{ - searchLine_->setVisible(true); - searchLine_->setFocus(); - searchLine_->selectAll(); -} - -void CommandOutputWidget::on_gotoLineTb__clicked() -{ - textEdit_->gotoLine(); -} - -void CommandOutputWidget::on_fontSizeUpTb__clicked() -{ - //We need to call a custom slot here instead of "zoomIn"!!! - textEdit_->slotZoomIn(); -} - -void CommandOutputWidget::on_fontSizeDownTb__clicked() -{ - //We need to call a custom slot here instead of "zoomOut"!!! - textEdit_->slotZoomOut(); -} - -void CommandOutputWidget::writeSettings(QSettings& settings) -{ - settings.beginGroup("widget"); - settings.setValue("splitter",splitter_->saveState()); - ViewerUtil::saveTreeColumnWidth(settings,"treeColumnWidth",tree_); - settings.endGroup(); -} - -void CommandOutputWidget::readSettings(QSettings& settings) -{ - settings.beginGroup("widget"); - - ViewerUtil::initTreeColumnWidth(settings,"treeColumnWidth",tree_); - - if(settings.contains("splitter")) - { - splitter_->restoreState(settings.value("splitter").toByteArray()); - } - - settings.endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputWidget.hpp ecflow-4.11.1/Viewer/src/CommandOutputWidget.hpp --- ecflow-4.9.0/Viewer/src/CommandOutputWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef COMMANDOUTPUTWIDGET_HPP -#define COMMANDOUTPUTWIDGET_HPP - -#include -#include -#include - -#include "ui_CommandOutputWidget.h" - -#include "CommandOutput.hpp" - -class ModelColumn; - -class CommandOutputModel : public QAbstractItemModel -{ -public: - explicit CommandOutputModel(QObject *parent=0); - ~CommandOutputModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - void dataIsAboutToChange(); - void dataChanged(); - bool updateData(); - bool hasData() const; - CommandOutput_ptr indexToItem(const QModelIndex& idx) const; - QModelIndex itemToStatusIndex(CommandOutput_ptr item) const; - -protected: - ModelColumn* columns_; -}; - -class CommandOutputWidget : public QWidget, protected Ui::CommandOutputWidget -{ -Q_OBJECT - -public: - explicit CommandOutputWidget(QWidget *parent=0); - ~CommandOutputWidget(); - - void readSettings(QSettings&); - void writeSettings(QSettings&); - - -protected Q_SLOTS: - void slotItemSelected(const QModelIndex&,const QModelIndex&); - void slotItemAddBegin(); - void slotItemAddEnd(); - void slotItemOutputAppend(CommandOutput_ptr,QString); - void slotItemErrorAppend(CommandOutput_ptr,QString); - void slotItemOutputReload(CommandOutput_ptr); - void slotItemErrorReload(CommandOutput_ptr); - void slotItemStatusChanged(CommandOutput_ptr); - void on_searchTb__clicked(); - void on_gotoLineTb__clicked(); - void on_fontSizeUpTb__clicked(); - void on_fontSizeDownTb__clicked(); - -Q_SIGNALS: - void editorFontSizeChanged(); - -protected: - bool isCurrent(CommandOutput_ptr item); - void loadItem(CommandOutput_ptr); - void updateInfoLabel(CommandOutput_ptr); - void removeSpacer(); - - CommandOutputModel* model_; -}; - -#endif // COMMANDOUTPUTWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/src/CommandOutputWidget.ui ecflow-4.11.1/Viewer/src/CommandOutputWidget.ui --- ecflow-4.9.0/Viewer/src/CommandOutputWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CommandOutputWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,207 +0,0 @@ - - - CommandOutputWidget - - - - 0 - 0 - 557 - 671 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - false - - - QFrame::StyledPanel - - - - - - 2 - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg - - - Ctrl++ - - - true - - - - - - - Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg - - - Ctrl+- - - - true - - - - - - - Show search bar (CTRL-F) - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - Goto line number (CTRL-L) - - - ... - - - - :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg - - - Ctrl+L - - - true - - - - - - - - - - - - Qt::Vertical - - - - - 0 - 1 - - - - QPlainTextEdit::NoWrap - - - true - - - - - - - - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - PlainTextEdit - QPlainTextEdit -
        PlainTextEdit.hpp
        -
        - - PlainTextSearchLine - QWidget -
        PlainTextSearchLine.hpp
        - 1 -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/CompactView.cpp ecflow-4.11.1/Viewer/src/CompactView.cpp --- ecflow-4.9.0/Viewer/src/CompactView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CompactView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1045 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CompactView.hpp" - -#include "ExpandState.hpp" -#include "TreeNodeModel.hpp" -#include "TreeNodeViewDelegate.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -//#define _UI_COMPACTVIEW_DEBUG - -CompactView::CompactView(TreeNodeModel* model,QWidget* parent) : - AbstractNodeView(model,parent) -{ - //This is needed for making the context menu work - setProperty("view","tree"); - - //we cannot call it from the constructor of the base class - //because it calls a pure virtual method - reset(); -} - -CompactView::~CompactView() -{ - -} - -//Creates and initialize the viewItem structure of the children of the element -// parentId: the items whose children are to be expanded -// recursiveExpanding: all the children will be expanded -// afterIsUninitialized: when we recurse from layout(-1) it indicates -// the items after 'i' are not yet initialized and need not to be moved - -void CompactView::layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated) -{ - //This is the root item. - if(parentId == -1) - { - rowCount_=0; - maxRowWidth_=0; - } - - QModelIndex parentIndex = (parentId < 0) ? root_ : modelIndex(parentId); - - if(parentId >=0 && !parentIndex.isValid()) - { - //modelIndex() should never return something invalid for the real items. - //This can happen if columncount has been set to 0. - //To avoid infinite loop we stop here. - return; - } - - int count=model_->rowCount(parentIndex); - bool expanding=true; - - //This is the root item. viewItems must be empty at this point. - if(parentId == -1) - { - Q_ASSERT(viewItems_.empty()); - Q_ASSERT(preAllocated == false); - viewItems_.resize(count); - afterIsUninitialized = true; //It can only be true when we expand from the root! - } - //The count of the stored children does not match the actual count - else if(viewItems_[parentId].total != (uint)count) - { - //Expand - if(!afterIsUninitialized) - { - //We called expandall for a non-root item. All the new items need must be - //already instered at this point. This is the duty of the caller routine. - //const int itemsCount = viewItems_.size(); - //if(recursiveExpanding) - if(preAllocated) - { - //We called expandAll() for a non-root item. All the needed items need must already be - //inserted at this point. This is the duty of the caller routine! - //When layout() is finished we need to adjust the parent of all the items - //after the insertion position. This is the duty of the caller routine. We - //have chosen this solution for performance reasons! - } - else - { - insertViewItems(parentId + 1, count, TreeNodeViewItem()); - } - } - //ExpandAll from the root - else if(count > 0) - viewItems_.resize(viewItems_.size() + count); - } - else - { - expanding=false; - } - - int first = parentId + 1; - int last = 0; - int children = 0; - int level=(parentId >=0?viewItems_[parentId].level+1:0); - TreeNodeViewItem *item=0; - - std::vector itemWidthVec; - std::vector itemHeightVec; - int widest=0; - for(int i=first; i < first+count; i++) - { - int w,h; - QModelIndex currentIndex=model_->index(i-first,0,parentIndex); - delegate_->sizeHint(currentIndex,w,h); - itemWidthVec.push_back(w); - itemHeightVec.push_back(h); - - if(parentId >=0 && !model_->isAttribute(currentIndex)) - if(w > widest) widest=w; -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " item=" << currentIndex.data().toString() << " w=" << w; -#endif - } - -#ifdef _UI_COMPACTVIEW_DEBUG - if(parentId >=0) - UiLog().dbg() << "layout parent=" << viewItems_[parentId].index.data().toString() << - " widest child=" << widest; -#endif - - //Iterate through the direct children of parent item. At this point all the items - //needed in the loop below are pre-allocated but not yet initialised. - for(int i=first; i < first+count; i++) - { - QModelIndex currentIndex=model_->index(i-first,0,parentIndex); - - last = i + children; - item = &viewItems_[last]; - item->parentItem = parentId; - item->index=currentIndex; - item->hasMoreSiblings=(i < first+count-1); - item->level=level; - item->expanded = false; - item->total = 0; - item->widestInSiblings=widest; - - //We compute the size of the item. For attributes we delay the width computation until we - //actually paint them and we set their width to 300. - item->width=itemWidthVec[i-first]; - item->height=itemHeightVec[i-first]; - - int xp=leftMargin_; - if(parentId >=0) - { - item->widestInSiblings=widest; - xp=viewItems_[parentId].alignedRight()+itemGap_; - } - else - { - item->widestInSiblings=item->width; - } - - item->x=xp; - - if(item->alignedRight() > maxRowWidth_) - maxRowWidth_=item->alignedRight(); - - //We need to expand the item - if(recursiveExpanding || isIndexExpanded(currentIndex)) - { - if(recursiveExpanding) - expandedIndexes.insert(currentIndex); - - item->expanded = true; - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " before " << item->index.data().toString() << " total=" << item->total; -#endif - //Add the children to the layout - layout(last,recursiveExpanding,afterIsUninitialized,preAllocated); - - item = &viewItems_[last]; - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " after " << item->index.data().toString() << " total=" << item->total; -#endif - children+=item->total; - item->hasChildren = item->total > 0; - } - else - { - item->hasChildren = model_->hasChildren(currentIndex); - } - } - - if(!expanding) - return; // nothing changed - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " update parent total"; -#endif - - int pp=parentId; - while (pp > -1) - { - viewItems_[pp].total += count; - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " parent=" << viewItems_[pp].index.data().toString() << - " total=" << viewItems_[pp].total; -#endif - - pp = viewItems_[pp].parentItem; - } -} - -//Paint the rows intersecting with the given region -void CompactView::paint(QPainter *painter,const QRegion& region) -{ - //Even though the viewport palette is set correctly at the - //beginning something sets it to another value. Here we try - //to detect it and correct the palette with the right colour. - if(expectedBg_.isValid()) - { - QPalette p=viewport()->palette(); - if(p.color(QPalette::Window) != expectedBg_) - { - p.setColor(QPalette::Window,expectedBg_); - viewport()->setPalette(p); - viewport()->update(); - expectedBg_=QColor(); - return; - } - } - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "CompactView::paint -->"; - //UiLog().dbg() << "sizeof(TreeNodeViewItem)=" << sizeof(TreeNodeViewItem); - //UiLog().dbg() << "region=" << region; -#endif - - int firstVisibleOffset=0; - - //The first visible item at the top of the viewport - int firstVisible=firstVisibleItem(firstVisibleOffset); -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "firstVisible " << firstVisible; -#endif - - if(firstVisible<0) - return; - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "scrollX" << horizontalScrollBar()->value() << " " << viewport()->width(); -#endif - - int xOffset=0; - if(horizontalScrollBar()->value() > 0) - { - xOffset=horizontalScrollBar()->value(); - painter->translate(-xOffset,0); - } - - const int itemsCount = viewItems_.size(); - const int viewportWidth = viewport()->width(); - QVector rects = region.rects(); - QVector drawn; - bool multipleRects = (rects.size() > 1); - - //Iterate through the rectangles in the region - for(int a = 0; a < rects.size(); ++a) - { - const QRect area = (multipleRects - ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height()) - : rects.at(a)); -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " area=" << area; -#endif - - //Initialise indentVec. For each indentation level it tells us if - //a connector line is to be drawn. Here we scan up to the - //toplevel item in the firstVisible item's branch. - std::vector indentVec(1000,0); - if(firstVisible >0) - { - TreeNodeViewItem* item=&viewItems_[firstVisible]; - int level=item->level; - while(item->parentItem >= 0 && level >0) - { - TreeNodeViewItem* pt=&viewItems_[item->parentItem]; - if(item->hasMoreSiblings) - { - indentVec[item->level]=connectorPos(item,pt); - } - UI_ASSERT(pt->level == level-1, "item->parentItem=" << item->parentItem << - " pt->level=" << pt->level << " level=" << level); - item=pt; - level--; - } - } - - int i = firstVisible; // the first item at the top of the viewport - int y = firstVisibleOffset; // we may only see part of the first item - - //start at the top of the viewport and iterate down through the update area - int itemsInRow=1; - for (; i < itemsCount; i+=itemsInRow) - { - int itemHeight; - rowProperties(i,itemHeight,itemsInRow,indentVec); - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "row: " << i << " " << itemHeight << " " << itemsInRow; -#endif - //Try to find the first item int the current rect - if(y + itemHeight > area.top()) - break; - y += itemHeight; - } - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "y: " << y << " " << area.bottom(); -#endif - - //Paint the visible rows in the current rect - for (; i < itemsCount && y <= area.bottom(); i+=itemsInRow) - { - if(!multipleRects || !drawn.contains(i)) - { - //Draw a whole row. It will update y,itemsInRow and indentVec!! - drawRow(painter,i,xOffset,y,itemsInRow,indentVec); - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " row rendered - item=" << i << " y=" << y << " itemsInRow=" << itemsInRow; -#endif - } - else - { - int rh=rowHeight(i,1,itemsInRow); - y+=rh; -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " row skipped - item=" << i << " y=" << y << " itemsInRow=" << itemsInRow; -#endif - } - - if(multipleRects) - drawn.append(i); - } - } -} - -//Draw a whole row starting at item "start". -void CompactView::drawRow(QPainter* painter,int start,int xOffset,int& yp,int& itemsInRow,std::vector& indentVec) -{ - itemsInRow=0; - bool leaf=false; - const int itemsCount = static_cast(viewItems_.size()); - - //Get the rowheight - int iir=0; - int rh=rowHeight(start,1,iir); - - //See if there are no multiline items in this row - bool singleRow=delegate_->isSingleHeight(rh); - - int firstLevel=0; - const int viewportWidth = viewport()->width(); - - //We iterate through the items in the row - for(int i=start; i < itemsCount && !leaf; ++i ) - { - TreeNodeViewItem* item=&(viewItems_[i]); -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " item=" << i << " " << item->index.data().toString(); -#endif - leaf=(item->total == 0); - - //Find out the first indentation level in the row - if(firstLevel==0) - firstLevel=item->level; - - //Init style option - QStyleOptionViewItem opt; - if(selectionModel_->isSelected(item->index)) - opt.state |= QStyle::State_Selected; - - int optWidth=2000; - if(item->width > optWidth) - optWidth=item->width; - opt.rect=QRect(item->x,yp,optWidth,item->height); - - //We do not render the item if it is outisde the viewport and - //its parent's right is also outside the viewport. Here we considered that - //the connector line is always drawn from the child to the parent. - bool needToDraw=true; - if(item->parentItem >=0) - { - if(viewItems_[item->parentItem].right() >= translation() + viewportWidth) - needToDraw=false; - } - - if(needToDraw) - { - //For single rows we center items halfway through the rowHeight - if(singleRow) - { - if(item->height < rh) - { - opt.rect.moveTop(yp+(rh-item->height)/2); - } - } - - //QRect vr=visualRect(item->index); - //painter->fillRect(vr,QColor(120,120,120,120)); - -//#ifdef _UI_COMPACTVIEW_DEBUG -// UiLog().dbg() << " optRect=" << opt.rect << " visRect=" << vr; -//#endif - - //Draw the item with the delegate - QSize paintedSize; - delegate_->paint(painter,opt,item->index,paintedSize); - - //we have to know if the item width/height is the same that we expected. - //This can happen when: - // -we set a fixed initial width for the item (e.g. for an attribute) - // and now we got the real width - // -the number of icons or additional extra information - // changed for a node (so the width changed) - // -the number of lines changed in a multiline label (so the height changed) - bool wChanged=paintedSize.width() != item->width; - bool hChanged=paintedSize.height() != item->height; - bool wIncreased=paintedSize.width() > item->width; - - if(wChanged || hChanged) - { - //set new size - item->width=paintedSize.width(); - item->height=paintedSize.height(); - - if(item->right() > maxRowWidth_) - { - maxRowWidth_=item->right(); - doDelayedWidthAdjustment(); - } - else if(hChanged) - { - doDelayedWidthAdjustment(); - } - } - - //The width changed - if(wChanged) - { - bool sameAsWidest=(item->width == item->widestInSiblings); - item->width=paintedSize.width(); - - //servers - if(item->parentItem ==-1) - { - adjustWidthInParent(i); - doDelayedWidthAdjustment(); - } - //Nodes - else if(model_->isNode(item->index)) - { - //widestInSiblings has to be adjusted - if(sameAsWidest || paintedSize.width() > static_cast(item->widestInSiblings)) - { - adjustWidthInParent(i); - doDelayedWidthAdjustment(); - } - //we just need to update the item - else if( paintedSize.width() < static_cast(item->widestInSiblings)) - { - doDelayedWidthAdjustment(); - } - } - //Attributes - else - { - if(item->right() > maxRowWidth_) - { - maxRowWidth_=item->right(); - doDelayedWidthAdjustment(); - } - else if(wIncreased && !hChanged) - { - //we need to repaint the attribute - doDelayedWidthAdjustment(); - } - } - } - //the height changed (can only be a multiline label) - if(hChanged) - { - //set new size - item->height=paintedSize.height(); - doDelayedWidthAdjustment(); - } - - //QRect rr=opt.rect; - //rr.setWidth(item->width); - //painter->drawRect(rr); - - //UiLog().dbg() << i << " " << viewItems_[i]->index << " " << viewItems_[i]->index.data().toString() << " " - // << viewItems_[i]->x << " " << viewItems_[i]->height << " " << leaf; - - painter->setPen(connectorColour_); - - //If not a top level item (e.i. not a server) - if(item->parentItem >=0) - { - //The parent item. It is always a node. - TreeNodeViewItem* pt=&(viewItems_[item->parentItem]); - - //The horizontal line connecting the item to its parent - int lineX1=pt->right()+connectorGap_; - int lineX2=item->x-connectorGap_; - int lineX=(pt->right()+item->x)/2; - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " lineX=" << lineX << " " << item->x << " " << connectorPos(item,pt); -#endif - UI_ASSERT(lineX==connectorPos(item,pt),"lineX=" << lineX << " i=" << i << - " item->x=" << item->x << " connectorPos=" << connectorPos(item,pt)); - - //First child - in the same row as its parent - if(item->index.row() == 0) - { - int lineY=yp+pt->height/2; - - //horizontal line to the parent - painter->drawLine(lineX1,lineY,lineX2,lineY); - - //line towards the siblings - downwards - if(item->hasMoreSiblings) - { - //painter->drawLine(lineX,lineY,lineX,lineY+rh/2); - painter->drawLine(lineX,lineY,lineX,yp+rh); - indentVec[item->level]=lineX; - } - else - indentVec[item->level]=0; - } - //Child in the middle - has sibling both upwards and downwards - else if(item->hasMoreSiblings) - { - int lineY=yp+item->height/2; - - painter->drawLine(lineX,lineY,lineX2,lineY); - //painter->drawLine(lineX,lineY+rh/2,lineX,lineY-rh/2); - painter->drawLine(lineX,yp,lineX,yp+rh); - indentVec[item->level]=lineX; - } - - //The last child - has sibling only upwards - else - { - int lineY=yp+item->height/2; - painter->drawLine(lineX,lineY,lineX2,lineY); - //painter->drawLine(lineX,lineY,lineX,lineY-rh/2); - painter->drawLine(lineX,lineY,lineX,yp); - indentVec[item->level]=0; - } - } - - //indicate if a node is exandable - if(item->hasChildren && !item->expanded) - { - int lineY=yp+item->height/2; - int lineX=item->right()+connectorGap_; - QPen oriPen=painter->pen(); - painter->setPen(QPen(connectorColour_,1,Qt::DashLine)); - painter->drawLine(lineX,lineY,lineX+expandConnectorLenght_,lineY); - painter->setPen(oriPen); - } - } - - //When we reach a leaf item we move one row down. - if(leaf) - { - //Draw the vertical connector lines for all the levels - //preceding the first level in the row! - painter->setPen(connectorColour_); - for(int j=0; j < firstLevel; j++) - { - int xp=indentVec[j]; - if(xp != 0) - painter->drawLine(xp,yp,xp,yp+rh); - } - - yp+=rh; - rh=0; - firstLevel=0; - } - itemsInRow++; - } - - if(itemsInRow == 0) - itemsInRow=1; -} - -void CompactView::adjustWidthInParent(int start) -{ - //The parent index of the start item - int parentItem=viewItems_[start].parentItem; - - //The current max width in the start item's siblings - int prevWidest=viewItems_[start].widestInSiblings; - - //If the parent is not the root ie the start item is not a server - if(parentItem >=0) - { - int w=0, h=0, widest=0; - QModelIndex parentIndex=viewItems_[parentItem].index; - - //Determine the max width in the siblings of the start - //item, ie in the children of the parent item - int rowCount=model_->rowCount(parentIndex); - for(int i=0; i < rowCount; i++) - { - QModelIndex idx=model_->index(i,0,parentIndex); - if(model_->isNode(idx)) - { - delegate_->sizeHint(idx,w,h); - if(w >widest) widest=w; - } - } - - //If there is a new max width we need to adjust all the children of - //the parent item - int delta=widest-prevWidest; - if(delta != 0) - { - int n=parentItem+viewItems_[parentItem].total; - for(int i=parentItem+1; i <= n; i++) - { - //For a direct child of the parent item we just - //set the max width to its new value - if(viewItems_[i].parentItem == parentItem) - { - viewItems_[i].widestInSiblings = widest; - } - //The other items are shifted - else - { - viewItems_[i].x+=delta; - } - - //Check if the total width changed - if(viewItems_[i].right() > maxRowWidth_) - maxRowWidth_=viewItems_[i].right(); - } - } - } - - //If the parent is the root ie the start item is a server - else - { - //Determine the diff between the current and the previous width - int delta=viewItems_[start].width-prevWidest; - - //for server widestInSiblings is set to the width - viewItems_[start].widestInSiblings=viewItems_[start].width; - - //Shift all the children with the diff - if(delta != 0) - { - int n=start+viewItems_[start].total; - for(int i=start+1; i <= n; i++) - { - //shifted - viewItems_[i].x+=delta; - - //Check if the total width changed - if(viewItems_[i].right() > maxRowWidth_) - maxRowWidth_=viewItems_[i].right(); - } - } - } - -} - -int CompactView::connectorPos(TreeNodeViewItem* item, TreeNodeViewItem* parent) const -{ - return (parent->right()+item->x)/2; -} - - -//Get the rowheight. There are three kinds of row heights. -// 1. nodes (fixed height) -// 2. attributes (fixed height) -// 3. multiline label attributes (variable height!!!) -void CompactView::rowProperties(int start,int& rowHeight,int &itemsInRow,std::vector& indentVec) const -{ - rowHeight=0; - itemsInRow=0; - const int itemsCount = static_cast(viewItems_.size()); - - for(int i=start; i < itemsCount; i++) - { - TreeNodeViewItem* item=&(viewItems_[i]); - rowHeight=qMax(rowHeight,static_cast(item->height)); - itemsInRow++; - if(item->total == 0) - { - indentVec[item->level]=0; - break; - } - - if(item->parentItem >=0) - { - //The parent item. It is always a node. - TreeNodeViewItem* pt=&(viewItems_[item->parentItem]); - - if(item->hasMoreSiblings) - { - int lineX1=pt->right()+2; - int lineX2=item->x-2; - int lineX=(lineX1+lineX2)/2; - indentVec[item->level]=lineX; - } - else - { - indentVec[item->level]=0; - } - } - } - - UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); -} - -int CompactView::rowHeight(int start,int forward, int &itemsInRow) const -{ - uint rh=0; - itemsInRow=0; - const int itemsCount = static_cast(viewItems_.size()); - - if(forward == 1) - { - for(int i=start; i < itemsCount; i++) - { - rh=qMax(rh,viewItems_[i].height); - itemsInRow++; - if(viewItems_[i].total == 0) - break; - } - } - else - { - UI_ASSERT(start >= 0,"start=" << start << " total=" << viewItems_.size()); - UI_ASSERT(start < static_cast(viewItems_.size()),"start=" << start << " total=" << viewItems_.size()); - rh=qMax(rh,viewItems_[start].height); - itemsInRow++; - for(int i=start-1; i >= 0; i--) - { - if(viewItems_[i].total == 0) - break; - rh=qMax(rh,viewItems_[i].height); - itemsInRow++; - } - } - - UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); - return rh; -} - -int CompactView::itemCountInRow(int start) const -{ - const std::size_t itemsCount = viewItems_.size(); - int itemsInRow=0; - for(std::size_t i=start; i < itemsCount; i++) - { - itemsInRow++; - if(viewItems_[i].total == 0) - return itemsInRow; - } - - UI_ASSERT(itemsInRow > 0,"itemsInRow=" << itemsInRow); - return itemsInRow; -} - -int CompactView::itemRow(int item) const -{ - if(item < 0 || item >= static_cast(viewItems_.size())) - return -1; - - int row=-1; - int itemsInRow=0; - for(int i=0; i <= item; i+=itemsInRow) - { - row++; - itemsInRow=itemCountInRow(i); - } - - return row; -} - -int CompactView::firstVisibleItem(int &offset) const -{ - const int value = verticalScrollBar()->value(); -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "CompactNodeView::firstVisibleItem --> value=" << value; -#endif - - if (verticalScrollMode_ == ScrollPerItem) - { - offset = 0; - //value is the row number - - if(value <0 || value >= rowCount_) - return -1; - - int cnt=0; - int itemsInRow=0; - const std::size_t itemsCount=viewItems_.size(); - for (std::size_t i=0; i < itemsCount; i+=itemsInRow) - { - if(cnt == value) - { -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " i=" << i << " itemsInRow=" << itemsInRow; -#endif - return i; - } - itemsInRow=itemCountInRow(i); - cnt++; - } - //return (value < 0 || value >= viewItems_.count()) ? -1 : value; - } - - return -1; -} - - -//This has to be very quick. Called after each collapse/expand. -void CompactView::updateRowCount() -{ - rowCount_=0; - const int itemsCount = static_cast(viewItems_.size()); - for(int i=0; i < itemsCount; i++) - { - if(viewItems_[i].total == 0) - rowCount_++; - } - -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "CompactNodeView::updateRowCount --> " << rowCount_; -#endif -} - -void CompactView::updateScrollBars() -{ -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << "CompactNodeView::updateScrollBars -->"; -#endif - - QSize viewportSize = viewport()->size(); - if(!viewportSize.isValid()) - viewportSize = QSize(0, 0); - - if(viewItems_.empty()) - { - //doItemsLayout(); - } - - int itemsInViewport = 0; - - const int itemsCount = viewItems_.size(); - if(itemsCount ==0) - return; - - const int viewportHeight = viewportSize.height(); - int itemsInRow=1; - for(int height = 0, item = itemsCount - 1; item >= 0; item-=itemsInRow) - { - //UiLog().dbg() << "item=" << item; - height +=rowHeight(item,-1,itemsInRow); - if(height > viewportHeight) - break; - itemsInViewport++; - } -#ifdef _UI_COMPACTVIEW_DEBUG - UiLog().dbg() << " itemsCount=" << itemsCount << " rowCount=" << rowCount_; - UiLog().dbg() << " itemsInViewport " << itemsInViewport; -#endif - - if(verticalScrollMode_ == ScrollPerItem) - { - if(!viewItems_.empty()) - itemsInViewport = qMax(1, itemsInViewport); - - //verticalScrollBar()->setRange(0, itemsCount - itemsInViewport); - verticalScrollBar()->setRange(0, rowCount_ - itemsInViewport); - verticalScrollBar()->setPageStep(itemsInViewport); - verticalScrollBar()->setSingleStep(1); - } - else - { - // scroll per pixel - } - - //Horizontal scrollbar - if(viewportSize.width() < maxRowWidth_) - { - horizontalScrollBar()->setRange(0,maxRowWidth_+10-viewportSize.width()); - horizontalScrollBar()->setPageStep(viewportSize.width()); - horizontalScrollBar()->setSingleStep(1); - } - else - { - horizontalScrollBar()->setRange(0,0); - } -} - -/* - Returns the rectangle on the viewport occupied by the item at \a index. - If the index is not visible or explicitly hidden, the returned rectangle is invalid. -*/ -QRect CompactView::visualRect(const QModelIndex &index) const -{ - //if (!d->isIndexValid(index) || isIndexHidden(index)) - // return QRect(); - - //d->executePostedLayout(); - - int vi = viewIndex(index); - if (vi < 0) - return QRect(); - - int y = -1; - int rh=0; - coordinateForItem(vi,y,rh); - if(y >=0) - { - //return QRect(viewItems_[vi].x, y, viewItems_[vi].width,rh); //TODO: optimise it - return QRect(viewItems_[vi].x-1-translation(), y, viewItems_[vi].width+2,rh); - } - return QRect(); -} - -//Returns the viewport y coordinate for item. -void CompactView::coordinateForItem(int item,int& itemY,int& itemRowHeight) const -{ - itemY=-1; - if(verticalScrollMode_ == ScrollPerItem) - { - int offset = 0; - //firstVisibleItem must always start a row!!!! - int topViewItemIndex=firstVisibleItem(offset); - if (item >= topViewItemIndex) - { - // search in the visible area first and continue down - // ### slow if the item is not visible - - const int itemsCount = viewItems_.size(); - const int viewportHeight = viewport()->size().height(); - int itemsInRow=1; - - for(int height = 0, viewItemIndex = topViewItemIndex; - height <= viewportHeight && viewItemIndex < itemsCount; viewItemIndex+=itemsInRow) - { - int h=rowHeight(viewItemIndex,1,itemsInRow); - if(viewItemIndex <=item && item < viewItemIndex+itemsInRow) - { - itemY=height; - itemRowHeight=h; - return; - } - height +=h; - } - } - } -} - -//coordinate is in viewport coordinates -int CompactView::itemAtCoordinate(const QPoint& coordinate) const -{ - const std::size_t itemCount = viewItems_.size(); - if(itemCount == 0) - return -1; - - if(verticalScrollMode_ == ScrollPerItem) - { - //int topRow = verticalScrollBar()->value(); - - int offset = 0; - int topViewItemIndex=firstVisibleItem(offset); - - if(coordinate.y() >= 0) - { - // the coordinate is in or below the viewport - int viewItemCoordinate = 0; - int itemsInRow=0; - for(std::size_t viewItemIndex = topViewItemIndex; viewItemIndex < itemCount; viewItemIndex+=itemsInRow) - { - viewItemCoordinate += rowHeight(viewItemIndex,1,itemsInRow); - if (viewItemCoordinate > coordinate.y()) - { - viewItemIndex=itemAtRowCoordinate(viewItemIndex,itemsInRow,coordinate.x()+translation()); - return (viewItemIndex >= itemCount ? -1 : viewItemIndex); - } - } - } - } - - return -1; -} - -//return the item index at the absolute x coordinate (i.e. not viewport x coordinate) -int CompactView::itemAtRowCoordinate(int start,int count,int logicalXPos) const -{ - for(int i=start; i < start+count; i++) - { - int left=viewItems_[i].x-1; - int right=viewItems_[i].right()+2; - if(!viewItems_[i].expanded && viewItems_[i].hasChildren) - right=viewItems_[i].right()+connectorGap_+expandConnectorLenght_+3; - - if(left <= logicalXPos && right >= logicalXPos) - { - return i; - } - } - return -1; -} - -void CompactView::updateViewport(const QRect rect) -{ - viewport()->update(rect); -} diff -Nru ecflow-4.9.0/Viewer/src/CompactView.hpp ecflow-4.11.1/Viewer/src/CompactView.hpp --- ecflow-4.9.0/Viewer/src/CompactView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CompactView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef COMPACTVIEW_HPP -#define COMPACTVIEW_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AbstractNodeView.hpp" - -class TreeNodeModel; -class GraphNodeViewItem; -class QStyledItemDelegate; - -class CompactView : public AbstractNodeView -{ - -public: - explicit CompactView(TreeNodeModel* model,QWidget *parent=0); - ~CompactView(); - - QRect visualRect(const QModelIndex &index) const; - -protected: - void paint(QPainter *painter,const QRegion& region); - void drawRow(QPainter* painter,int start,int xOffset,int &yp,int &itemsInRow,std::vector&); - - void layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated); - - int itemRow(int item) const; - int itemCountInRow(int start) const; - void rowProperties(int start,int& rowHeight,int &itemsInRow,std::vector& indentVec) const; - int rowHeight(int start,int forward,int &itemsInRow) const; - void coordinateForItem(int item,int& itemY,int& itemRowHeight) const; - int itemAtCoordinate(const QPoint& coordinate) const; - int itemAtRowCoordinate(int start,int count,int xPos) const; - bool isPointInExpandIndicator(int,QPoint) const {return false;} - - int firstVisibleItem(int &offset) const; - void updateRowCount(); - void updateScrollBars(); - void updateViewport(const QRect rect); - - void adjustWidthInParent(int start); - -private: - int connectorPos(TreeNodeViewItem* item, TreeNodeViewItem* parent) const; -}; - -#endif // COMPACTVIEW_HPP - diff -Nru ecflow-4.9.0/Viewer/src/ConfigListDelegate.cpp ecflow-4.11.1/Viewer/src/ConfigListDelegate.cpp --- ecflow-4.9.0/Viewer/src/ConfigListDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ConfigListDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ConfigListDelegate.hpp" - -#include -#include -#include - -//======================================================== -// -// VariableViewDelegate -// -//======================================================== - -ConfigListDelegate::ConfigListDelegate(int iconSize,int maxWidth,QWidget *parent) : - QStyledItemDelegate(parent), - iconSize_(iconSize), - maxTextWidth_(maxWidth), - margin_(2), - gap_(5) -{ -} - -void ConfigListDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ - QStyleOptionViewItem vopt(option); - initStyleOption(&vopt, index); - - QPixmap pix=index.data(Qt::DecorationRole).value(); - - //Save painter state - painter->save(); - - //The background rect - QRect bgRect=option.rect.adjusted(0,1,0,-1); - - //Paint selection. This should be transparent. - if(option.state & QStyle::State_Selected) - { - painter->fillRect(bgRect,QColor(200,222,250)); - } - - //pixmap - QRect pixRect(bgRect.center().x()-iconSize_/2,bgRect.top()+margin_,iconSize_,iconSize_); - painter->drawPixmap(pixRect,pix); - - //text - QString text=index.data(Qt::DisplayRole).toString(); - QFont f; - f.setBold(true); - QFontMetrics fm(f); - int textW=fm.width(text); - QRect textRect(bgRect.center().x()-textW/2,pixRect.bottom()+gap_,textW,fm.height()); - - painter->drawText(textRect,Qt::AlignHCenter| Qt::AlignVCenter,text); - - //Restore painter state - painter->restore(); - - -} - -QSize ConfigListDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const -{ - QSize size=QStyledItemDelegate::sizeHint(option,index); - - int w=(maxTextWidth_ > iconSize_)?maxTextWidth_:iconSize_; - w+=4; - - QFont f; - QFontMetrics fm(f); - size=QSize(w,2*margin_+iconSize_+gap_+fm.height()+2); - - return size; -} - - diff -Nru ecflow-4.9.0/Viewer/src/ConfigListDelegate.hpp ecflow-4.11.1/Viewer/src/ConfigListDelegate.hpp --- ecflow-4.9.0/Viewer/src/ConfigListDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ConfigListDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ -#define VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ - -#include -#include -#include -#include - -#include "TreeView.hpp" - -#include - -class ConfigListDelegate : public QStyledItemDelegate -{ -public: - explicit ConfigListDelegate(int,int,QWidget *parent=0); - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - -protected: - int iconSize_; - int maxTextWidth_; - int margin_; - int gap_; -}; - -#endif /* VIEWER_SRC_CONFIGLISTDELEGATE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/ConnectState.cpp ecflow-4.11.1/Viewer/src/ConnectState.cpp --- ecflow-4.9.0/Viewer/src/ConnectState.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ConnectState.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,96 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ConnectState.hpp" - -#include - -static std::map descMap; - -ConnectState::ConnectState() : - state_(Undef), - lastConnect_(0), - lastFailed_(0), - lastDisconnect_(0) -{ - init(); -} - -void ConnectState::init() -{ - if(descMap.empty()) - { - descMap[Undef]=""; - descMap[Lost]="Connection to server lost"; - descMap[Disconnected]="Server is disconnected"; - descMap[Normal]="Server is connected"; - } -} - -const std::string& ConnectState::describe() const -{ - static std::string empty=""; - - std::map::const_iterator it=descMap.find(state_); - if(it != descMap.end()) - { - return it->second; - } - return empty; - -} -void ConnectState::state(State state) -{ - state_=state; - - switch(state_) - { - case Normal: - logConnect(); - break; - case Lost: - logFailed(); - break; - case Disconnected: - logDisconnect(); - break; - default: - break; - } -} - -void ConnectState::logConnect() -{ - lastConnect_=time(0); -} - -void ConnectState::logFailed() -{ - lastFailed_=time(0); -} - -void ConnectState::logDisconnect() -{ - lastDisconnect_=time(0); -} - -void ConnectState::errorMessage(const std::string& str) -{ - errMsg_=str; - - std::size_t pos = str.find("Client environment:"); - if(pos != std::string::npos) - { - shortErrMsg_=str.substr(0,pos); - } - else - { - shortErrMsg_=str; - } -} diff -Nru ecflow-4.9.0/Viewer/src/ConnectState.hpp ecflow-4.11.1/Viewer/src/ConnectState.hpp --- ecflow-4.9.0/Viewer/src/ConnectState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ConnectState.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef CONNECTSTATE_HPP_ -#define CONNECTSTATE_HPP_ - -#include -#include - -class ConnectState -{ -public: - ConnectState(); - - enum State {Undef,Normal,Disconnected,Lost}; - - void state(State state); - State state() const {return state_;} - const std::string& describe() const; - void errorMessage(const std::string&); - std::time_t lastConnectTime() const {return lastConnect_;} - std::time_t lastLostTime() const {return lastFailed_;} - std::time_t lastDisconnectTime() const {return lastDisconnect_;} - const std::string& errorMessage() const {return errMsg_;} - const std::string& shortErrorMessage() const {return shortErrMsg_;} - -protected: - static void init(); - void logConnect(); - void logFailed(); - void logDisconnect(); - - State state_; - std::time_t lastConnect_; - std::time_t lastFailed_; - std::time_t lastDisconnect_; - std::string errMsg_; - std::string shortErrMsg_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/CustomCommandDialog.cpp ecflow-4.11.1/Viewer/src/CustomCommandDialog.cpp --- ecflow-4.9.0/Viewer/src/CustomCommandDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomCommandDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "CustomCommandDialog.hpp" - - -CustomCommandDialog::CustomCommandDialog(QWidget *parent) -{ - setupUi(this); - - // when the user clicks the 'Run' button, we close the dialog with ACCEPT - connect(commandDesigner_->runButton(), SIGNAL(clicked()), this, SLOT(accept())); - -} diff -Nru ecflow-4.9.0/Viewer/src/CustomCommandDialog.hpp ecflow-4.11.1/Viewer/src/CustomCommandDialog.hpp --- ecflow-4.9.0/Viewer/src/CustomCommandDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomCommandDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - - -#ifndef CUSTOMCOMMANDDIALOG_HPP_ -#define CUSTOMCOMMANDDIALOG_HPP_ - -#include - -#include "ui_CustomCommandDialog.h" - -class CustomCommandDialog : public QDialog, private Ui::CustomCommandDialog -{ - Q_OBJECT - -public: - explicit CustomCommandDialog(QWidget *parent = 0); - ~CustomCommandDialog() {}; - - MenuItem &menuItem() {return commandDesigner_->menuItem();}; - void setNodes(std::vector &nodes) {commandDesigner_->setNodes(nodes);}; - std::vector &selectedNodes() {return commandDesigner_->selectedNodes();}; - - - -//public Q_SLOTS: -// void insertCurrentText(); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/CustomCommandDialog.ui ecflow-4.11.1/Viewer/src/CustomCommandDialog.ui --- ecflow-4.9.0/Viewer/src/CustomCommandDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomCommandDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ - - - CustomCommandDialog - - - - 0 - 0 - 985 - 512 - - - - - 369 - 0 - - - - User Command Editor - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - CommandDesignerWidget - QLineEdit -
        CommandDesignerWidget.hpp
        -
        -
        - - - - buttonBox_ - rejected() - CustomCommandDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - buttonBox_ - accepted() - CustomCommandDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/CustomCommandHandler.cpp ecflow-4.11.1/Viewer/src/CustomCommandHandler.cpp --- ecflow-4.9.0/Viewer/src/CustomCommandHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomCommandHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,320 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include - -#include "CustomCommandHandler.hpp" -#include "SessionHandler.hpp" - -#include "DirectoryHandler.hpp" -#include "File.hpp" -#include "VSettings.hpp" - - -CustomCommand::CustomCommand(const std::string &name, const std::string &command, bool context) : - name_(name), command_(command), inContextMenu_(context) -{ -} - - -void CustomCommand::set(const std::string &name, const std::string &command, bool context) -{ - name_ = name; - command_ = command; - inContextMenu_ = context; -} - - -void CustomCommand::save(VSettings *vs) const -{ - vs->put("name", name()); - vs->put("command", command()); - vs->put("context", contextString()); -} - -CustomCommandHandler::CustomCommandHandler() -{ -} - -void CustomCommandHandler::init() -{ - readSettings(); -} - -CustomCommand* CustomCommandHandler::replace(int index, const std::string& name, const std::string& command, bool context) -{ - assert(index >= 0); - assert(index < static_cast(items_.size())); - - CustomCommand *item; - - // already in the list - just update it - item = items_[index]; - item->set(name, command, context); - - writeSettings(); - - return item; -} - -CustomCommand* CustomCommandHandler::replace(int index, const CustomCommand &cmd) -{ - return replace(index, cmd.name(), cmd.command(), cmd.inContextMenu()); -} - - - -void CustomCommandHandler::remove(int index) -{ - assert(index >= 0); - assert(index < static_cast(items_.size())); - - items_.erase(items_.begin()+index); - - writeSettings(); -} - -CustomCommand* CustomCommandHandler::duplicate(int index) -{ - assert(index >= 0); - assert(index < static_cast(items_.size())); - - CustomCommand *item = items_[index]; - std::string postfix("_1"); - std::string newName = item->name() + postfix; - - // ensure we are creating a unique new name - if we find an existing item with the same name, add another postfix - while(find(newName) != NULL) - newName += postfix; - - CustomCommand*newCmd = add(newName, item->command(), item->inContextMenu(), false); - - writeSettings(); - - return newCmd; -} - -void CustomCommandHandler::swapCommandsByIndex(int i1, int i2) -{ - assert(i1 >= 0); - assert(i1 < static_cast(items_.size())); - assert(i2 >= 0); - assert(i2 < static_cast(items_.size())); - - CustomCommand *temp = items_[i2]; - items_[i2] = items_[i1]; - items_[i1] = temp; -} - - -CustomCommand* CustomCommandHandler::find(const std::string& name) const -{ - for(std::deque::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name) - return *it; - } - return NULL; -} - - -// find the index of the command which has the given name; -1 if not found -int CustomCommandHandler::findIndexFromName(const std::string& name) const -{ - int i = 0; - for(std::deque::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name) - return i; - i++; - } - return -1; // it was not found -} - - -void CustomCommandHandler::writeSettings() -{ - std::vector vsItems; - std::string dummyFileName="dummy"; - std::string key="commands"; - - std::string settingsFilePath = settingsFile(); - VSettings vs(settingsFilePath); - - for(int i = 0; i < numCommands(); i++) - { - VSettings vsThisItem(dummyFileName); - CustomCommand *cmd = commandFromIndex(i); - cmd->save(&vsThisItem); - vsItems.push_back(vsThisItem); - } - vs.put(key,vsItems); - vs.write(); -} - -void CustomCommandHandler::readSettings() -{ - std::string settingsFilePath = settingsFile(); - VSettings vs(settingsFilePath); - - bool ok = vs.read(false); // false means we don't abort if the file is not there - - if(ok) - { - std::vector commands; - vs.get("commands", commands); - - for (std::size_t i = 0; i < commands.size(); i++) - { - VSettings *vsCommand = &commands[i]; - std::string emptyDefault=""; - std::string name = vsCommand->get("name", emptyDefault); - std::string command = vsCommand->get("command", emptyDefault); - std::string context = vsCommand->get("context", emptyDefault); - add(name, command, stringToBool(context), false); // add it to our in-memory list - } - } -} - -bool CustomCommandHandler::stringToBool(std::string &str) -{ - bool result = (!str.empty() && str == "yes"); - return result; -} - - -// ------------------------- -// CustomSavedCommandHandler -// ------------------------- - -CustomSavedCommandHandler* CustomSavedCommandHandler::instance_=0; - - -CustomSavedCommandHandler* CustomSavedCommandHandler::instance() -{ - if(!instance_) - { - instance_=new CustomSavedCommandHandler(); - } - - return instance_; -} - -CustomCommand* CustomSavedCommandHandler::add(const std::string& name, const std::string& command, bool context, bool saveSettings) -{ - CustomCommand *item=new CustomCommand(name, command, context); - items_.push_back(item); - - if (saveSettings) - writeSettings(); - - return item; -} - -std::string CustomSavedCommandHandler::settingsFile() -{ - SessionItem* cs=SessionHandler::instance()->current(); - return cs->savedCustomCommandsFile(); -} - - -// --------------------------- -// CustomCommandHistoryHandler -// --------------------------- - - -CustomCommandHistoryHandler* CustomCommandHistoryHandler::instance_=0; - -CustomCommandHistoryHandler::CustomCommandHistoryHandler() -{ - maxCommands_ = 10; -} - - -CustomCommandHistoryHandler* CustomCommandHistoryHandler::instance() -{ - if(!instance_) - { - instance_=new CustomCommandHistoryHandler(); - } - - return instance_; -} - - -CustomCommand* CustomCommandHistoryHandler::add(const std::string& name, const std::string& command, bool context, bool saveSettings) -{ - int index = findIndexFromName(name); - - if (index == -1) // not already in the list - { - CustomCommand *item=new CustomCommand(name, command, context); - items_.push_front(item); // add it to the front - - if(static_cast(items_.size()) > maxCommands_) // too many commands? - { - items_.pop_back(); // remove the last item - } - - if (saveSettings) - writeSettings(); - - return item; - } - else - { - return commandFromIndex(index); - } - -} - -std::string CustomCommandHistoryHandler::settingsFile() -{ - SessionItem* cs=SessionHandler::instance()->current(); - return cs->recentCustomCommandsFile(); -} - - -/* -void NodeQueryHandler::add(NodeQuery* item,bool saveToFile) -{ - items_.push_back(item); - if(saveToFile) - save(item); -} - - -void NodeQueryHandler::remove(const std::string&) -{ -} - -void NodeQueryHandler::remove(NodeQuery*) -{ - -} - - -void NodeQueryHandler::save() -{ -} - -void NodeQueryHandler::save(NodeQuery *item) -{ - std::string f=DirectoryHandler::concatenate(dirPath_,item->name() + "." + suffix_); - VSettings vs(f); - item->save(&vs); - vs.write(); - -} - -*/ - - - diff -Nru ecflow-4.9.0/Viewer/src/CustomCommandHandler.hpp ecflow-4.11.1/Viewer/src/CustomCommandHandler.hpp --- ecflow-4.9.0/Viewer/src/CustomCommandHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomCommandHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ -#define VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ - -#include -#include - - -class VSettings; - -class CustomCommand -{ -public: - CustomCommand(const std::string &name, const std::string &command, bool context); - const std::string& name() const {return name_;} - const std::string& command() const {return command_;} - bool inContextMenu() const {return inContextMenu_;} - std::string contextString() const {return (inContextMenu_ ? "yes" : "no");} - void set(const std::string &name, const std::string &command, bool context); - void save(VSettings *vs) const; - - -private: - std::string name_; - std::string command_; - bool inContextMenu_; -}; - - -class CustomCommandHandler -{ -public: - CustomCommandHandler(); - - virtual CustomCommand* add(const std::string& name, const std::string& command, bool context, bool WriteSettings) = 0; - CustomCommand* replace(int index, const std::string& name, const std::string& command, bool context); - CustomCommand* replace(int index, const CustomCommand &cmd); - CustomCommand* duplicate(int index); - void remove(int index); - //void remove(const std::string& name); - //void remove(CustomCommand*); - //CustomCommand* find(const std::string& name) const; - - //void save(); - //void save(CustomCommand*); - void init(); - //const std::vector& items() const {return items_;} - CustomCommand* find(const std::string& name) const; - int findIndexFromName(const std::string& name) const; - int numCommands() const {return items_.size();} - CustomCommand *commandFromIndex(int i) {return items_[i];}; - bool stringToBool(std::string &str); - void swapCommandsByIndex(int i1, int i2); - void writeSettings(); - -protected: - void readSettings(); - virtual std::string settingsFile() = 0; - - const std::string suffix_; - std::deque items_; -}; - - - -// ---------------------------------------------------------------------------------------------- -// specialisation of CustomCommandHandler to handle the commands that the user has manually saved -// ---------------------------------------------------------------------------------------------- - -class CustomSavedCommandHandler : public CustomCommandHandler -{ -public: - CustomSavedCommandHandler() {}; - CustomCommand* add(const std::string& name, const std::string& command, bool context, bool saveSettings); - - static CustomSavedCommandHandler* instance(); - -protected: - static CustomSavedCommandHandler* instance_; - std::string settingsFile(); -}; - - - -// -------------------------------------------------------------------------------------------- -// specialisation of CustomCommandHandler to handle the commands that the user has recently run -// -------------------------------------------------------------------------------------------- - -class CustomCommandHistoryHandler : public CustomCommandHandler -{ -public: - CustomCommandHistoryHandler(); - CustomCommand* add(const std::string& name, const std::string& command, bool context, bool saveSettings); - static CustomCommandHistoryHandler* instance(); - -protected: - static CustomCommandHistoryHandler* instance_; - std::string settingsFile(); - int maxCommands_; -}; - - - - -#endif /* VIEWER_SRC_CUSTOMCOMMANDHANDLER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/CustomListWidget.cpp ecflow-4.11.1/Viewer/src/CustomListWidget.cpp --- ecflow-4.9.0/Viewer/src/CustomListWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomListWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CustomListWidget.hpp" - -#include -#include - -CustomListWidget::CustomListWidget(QWidget* parent) : QListWidget(parent) -{ - setAlternatingRowColors(true); - - connect(this,SIGNAL(itemChanged(QListWidgetItem*)), - this,SLOT(slotItemChanged(QListWidgetItem*))); -} - -void CustomListWidget::addItems(QStringList lst,bool checkState) -{ - QListWidget::addItems(lst); - for(int i=0; i < count(); i++) - { - item(i)->setCheckState((checkState)?Qt::Checked:Qt::Unchecked); - } -} - -void CustomListWidget::addItems(QStringList lst,bool checkState,QList colLst) -{ - addItems(lst,checkState); - - for(int i=0; i < count() && i < colLst.count(); i++) - { - QColor col=colLst[i]; - if(col.isValid()) - { - QPixmap pix(10,10); - QPainter painter(&pix); - pix.fill(col); - painter.setPen(Qt::black); - painter.drawRect(0,0,9,9); - item(i)->setIcon(pix); - } - } -} - -void CustomListWidget::slotItemChanged(QListWidgetItem*) -{ - Q_EMIT selectionChanged(); -} - -bool CustomListWidget::hasSelection() const -{ - for(int i=0; i < count(); i++) - { - if(item(i)->checkState() == Qt::Checked) - return true; - } - - return false; -} - - -QStringList CustomListWidget::selection() const -{ - QStringList lst; - for(int i=0; i < count(); i++) - { - if(item(i)->checkState() == Qt::Checked) - lst << item(i)->text(); - } - - return lst; -} - - -void CustomListWidget::clearSelection() -{ - for(int i=0; i < count(); i++) - { - item(i)->setCheckState(Qt::Unchecked); - } - Q_EMIT selectionChanged(); -} - -void CustomListWidget::setSelection(QStringList sel) -{ - for(int i=0; i < count(); i++) - { - item(i)->setCheckState(Qt::Unchecked); - if(sel.contains(item(i)->text())) - item(i)->setCheckState(Qt::Checked); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/CustomListWidget.hpp ecflow-4.11.1/Viewer/src/CustomListWidget.hpp --- ecflow-4.9.0/Viewer/src/CustomListWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomListWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ -#define VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ - -#include - -class CustomListWidget : public QListWidget -{ -Q_OBJECT - -public: - explicit CustomListWidget(QWidget* parent=0); - - void addItems(QStringList lst,bool checkState); - void addItems(QStringList lst,bool checkState,QList); - QStringList selection() const; - bool hasSelection() const; - void setSelection(QStringList sel); - -public Q_SLOTS: - void clearSelection(); - -protected Q_SLOTS: - void slotItemChanged(QListWidgetItem*); - -Q_SIGNALS: - void selectionChanged(); - - -}; - - -#endif /* VIEWER_SRC_CUSTOMLISTWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/CustomTabWidget.cpp ecflow-4.11.1/Viewer/src/CustomTabWidget.cpp --- ecflow-4.9.0/Viewer/src/CustomTabWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomTabWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "CustomTabWidget.hpp" - -#include - - -CustomTabWidget::CustomTabWidget(QWidget* parent) : QTabWidget(parent) -{ - setProperty("change","1"); -} - -void CustomTabWidget::setCustomIcon(int index, QPixmap pix) -{ - if (index >= 0 && index < count()) - { - QSize maxSize=maxIconSize(); - - if(maxSize.width() < pix.width()) - maxSize.setWidth(pix.width()); - - if(maxSize.height() < pix.height()) - maxSize.setHeight(pix.height()); - - if(maxSize != iconSize()) - setIconSize(maxSize); - - setTabIcon(index, QIcon(pix)); - } -} - -QSize CustomTabWidget::maxIconSize() const -{ - QSize maxSize(0,0); - for(int i=0; i < count(); i++) - { - if(tabIcon(i).availableSizes().count() > 0) - { - QSize avs=tabIcon(i).availableSizes().front(); - if(maxSize.width() < avs.width()) - maxSize.setWidth(avs.width()); - - if(maxSize.height() < avs.height()) - maxSize.setHeight(avs.height()); - } - } - return maxSize; -} - diff -Nru ecflow-4.9.0/Viewer/src/CustomTabWidget.hpp ecflow-4.11.1/Viewer/src/CustomTabWidget.hpp --- ecflow-4.9.0/Viewer/src/CustomTabWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/CustomTabWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_CUSTOMTABWIDGET_HPP_ -#define VIEWER_SRC_CUSTOMTABWIDGET_HPP_ - -#include - -class CustomTabWidget : public QTabWidget -{ -public: - explicit CustomTabWidget(QWidget* parent=0); - - void setCustomIcon(int index, QPixmap pix); - -protected: - QSize maxIconSize() const; -}; - - -#endif /* VIEWER_SRC_CUSTOMTABWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/Dashboard.cpp ecflow-4.11.1/Viewer/src/Dashboard.cpp --- ecflow-4.9.0/Viewer/src/Dashboard.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Dashboard.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,685 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "Dashboard.hpp" - -#include "DashboardDialog.hpp" -#include "DashboardDock.hpp" -#include "DashboardTitle.hpp" -#include "InfoPanel.hpp" -#include "NodeWidget.hpp" -#include "ServerHandler.hpp" -#include "ServerFilter.hpp" -#include "TableNodeWidget.hpp" -#include "TreeNodeWidget.hpp" -#include "UiLog.hpp" -#include "VFilter.hpp" -#include "VSettings.hpp" -#include "WidgetNameProvider.hpp" - -#include -#include -#include -#include -#include -#include -#include "NodeSearchDialog.hpp" -#include "NodeSearchWidget.hpp" - -int Dashboard::maxWidgetNum_=20; - -Dashboard::Dashboard(QString rootNode,QWidget *parent) : - QMainWindow(parent), - settingsAreRead_(false) -{ - //We use the mainwindow as a widget. Its task is - //to dock all the component widgets! - setWindowFlags(Qt::Widget); - - setObjectName("db"); - - //The serverfilter. It holds the list of servers displayed by this dashboard. - serverFilter_=new ServerFilter(); - serverFilter_->addObserver(this); - - titleHandler_=new DashboardTitle(serverFilter_,this); - - //Central widget - we need to create it but we do not - //use it. So we can hide it! - QWidget *w=new QLabel("centre",this); - w->setObjectName("dbc"); - setCentralWidget(w); - w->hide(); - - layout()->setContentsMargins(0,0,0,0); - - setDockOptions(QMainWindow::AnimatedDocks|QMainWindow::AllowTabbedDocks|QMainWindow::AllowNestedDocks); -} - -Dashboard::~Dashboard() -{ - widgets_.clear(); - popupWidgets_.clear(); - - Q_EMIT aboutToDelete(); - - serverFilter_->removeObserver(this); - delete serverFilter_; -} - - -DashboardWidget* Dashboard::addWidgetCore(const std::string& type) -{ - DashboardWidget *w=0; - - //Create a dashboard widget - if(type == "tree") - { - NodeWidget* ctl=new TreeNodeWidget(serverFilter_,this); - - connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), - this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); - - connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SLOT(slotCommand(VInfo_ptr,QString))); - - w=ctl; - } - else if(type == "table") - { - NodeWidget* ctl=new TableNodeWidget(serverFilter_,this); - - connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), - this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); - - connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SLOT(slotCommand(VInfo_ptr,QString))); - - w=ctl; - } - else if(type == "info") - { - InfoPanel* ctl=new InfoPanel(this); - - connect(ctl,SIGNAL(popInfoPanel(VInfo_ptr,QString)), - this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); - - connect(ctl,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SLOT(slotCommand(VInfo_ptr,QString))); - - w=ctl; - } - - if(w) - { - connect(w,SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChanged(VInfo_ptr))); - - connect(w,SIGNAL(maximisedChanged(DashboardWidget*)), - this,SLOT(slotMaximisedChanged(DashboardWidget*))); - } - - return w; -} - - -void Dashboard::slotSelectionChanged(VInfo_ptr info) -{ - DashboardWidget* s=static_cast(sender()); - - Q_FOREACH(DashboardWidget* dw,widgets_) - { - if(dw != s) - dw->setCurrentSelection(info); - } - Q_FOREACH(DashboardWidget* dw,popupWidgets_) - { - if(dw != s) - dw->setCurrentSelection(info); - } - - Q_EMIT selectionChanged(info); -} - - -DashboardWidget* Dashboard::addWidget(const std::string& type) -{ - //Get a unique dockId stored as objectName - QString dockId=uniqueDockId(); - - if(hasMaximised()) - { - resetMaximised(); - } - - DashboardWidget* w=addWidget(type,dockId.toStdString()); - - //At this point the widgets can be inactive. Reload will make them active!!! - w->reload(); - - if(type == "info") - { - VInfo_ptr info=currentSelectionInView(); - if(info && info.get()) - { - if(InfoPanel* ip=static_cast(w)) - { - ip->slotReload(info); - } - } - } - - return w; -} - -DashboardWidget* Dashboard::addWidget(const std::string& type,const std::string& dockId) -{ - DashboardWidget *w=Dashboard::addWidgetCore(type); - - //If the db-widget creation fails we should do something!!! - if(!w) - return 0; - - //Store dockId in the db-widget - w->id(dockId); - - widgets_ << w; - - //Create a dockwidget - DashboardDock *dw = new DashboardDock(w, this); - - dw->setAllowedAreas(Qt::RightDockWidgetArea); - - //Store the dockId in the dockwidget (as objectName) - dw->setObjectName(QString::fromStdString(dockId)); - - //Add the dockwidget to the dashboard - addDockWidget(Qt::RightDockWidgetArea, dw); - - connect(dw,SIGNAL(closeRequested()), - this,SLOT(slotDockClose())); - - checkMaximisedState(); - - return w; -} - -DashboardWidget* Dashboard::addDialog(const std::string& type) -{ - DashboardWidget *w=Dashboard::addWidgetCore(type); - - //If the db-widget creation fails we should do something!!! - if(!w) - return 0; - - //The DashBoard or any of its children cannot be the parent of the - //dialog because in this case it would be always on top its parent. This is - //the behaviour when the dialog's parent is QMainWindow. - DashboardDialog* dia=new DashboardDialog(0); - - //So the parent is 0 and we will emit a signal from the Dashboard - //destructor to notify the dialog about the deletion. Then we can be - //sure that the dialog deletes itself when the Dashboard gets deleted. - connect(this,SIGNAL(aboutToDelete()), - dia,SLOT(slotOwnerDelete())); - - connect(dia,SIGNAL(finished(int)), - this,SLOT(slotDialogFinished(int))); - - connect(dia,SIGNAL(aboutToClose()), - this,SLOT(slotDialogClosed())); - - //The dialog will reparent the widget - dia->add(w); - dia->show(); - - return w; -} - -void Dashboard::addSearchDialog() -{ - //It will delete itself on close!! - //The parent is 0, for the reason see the comment in addDialog() - NodeSearchDialog* d=new NodeSearchDialog(0); - d->queryWidget()->setServerFilter(serverFilter_); - - connect(d->queryWidget(),SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChanged(VInfo_ptr))); - - connect(d->queryWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); - - //The dashboard signals the dialog on deletion - connect(this,SIGNAL(aboutToDelete()), - d,SLOT(slotOwnerDelete())); - - d->show(); -} - -void Dashboard::addSearchDialog(VInfo_ptr info) -{ - //It will delete itself on close!! - //The parent is 0, for the reason see the comment in addDialog() - NodeSearchDialog* d=new NodeSearchDialog(0); - d->queryWidget()->setServerFilter(serverFilter_); - d->queryWidget()->setRootNode(info); - - connect(d->queryWidget(),SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChanged(VInfo_ptr))); - - connect(d->queryWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SLOT(slotPopInfoPanel(VInfo_ptr,QString))); - - //The dashboard signals the dialog on deletion - connect(this,SIGNAL(aboutToDelete()), - d,SLOT(slotOwnerDelete())); - - d->show(); -} - -void Dashboard::slotDockClose() -{ - if(DashboardDock *dock=static_cast(sender())) - { - if(DashboardWidget* dw=static_cast(dock->widget())) - { - widgets_.removeOne(dw); - disconnect(this,0,dw,0); - - if(dw->isMaximised()) - resetMaximised(); - - checkMaximisedState(); - - dw->deleteLater(); - } - dock->deleteLater(); - } -} - -VInfo_ptr Dashboard::currentSelection() -{ - return currentSelectionInView(); -} - -void Dashboard::currentSelection(VInfo_ptr n) -{ - //if(NodeWidget *ctl=handler_->currentControl()) - // ctl->currentSelection(n); -} - -void Dashboard::slotPopInfoPanel(QString name) -{ - if(DashboardWidget *dw=addDialog("info")) - { - if(InfoPanel* ip=static_cast(dw)) - { - ip->setDetached(true); - ip->setCurrent(name.toStdString()); - } - - popupWidgets_ << dw; - } -} - -void Dashboard::slotPopInfoPanel(VInfo_ptr info,QString name) -{ - if(DashboardWidget *dw=addDialog("info")) - { - if(InfoPanel* ip=static_cast(dw)) - { - //ip->setDetached(true); - ip->slotReload(info); - ip->setCurrent(name.toStdString()); - } - - popupWidgets_ << dw; - } -} - -void Dashboard::slotCommand(VInfo_ptr info,QString cmd) -{ - if(!info || !info.get() ) - return; - - if(cmd == "search") - { - addSearchDialog(info); - } -} - -//------------------------------- -// Maximise panel -//------------------------------- - -void Dashboard::slotMaximisedChanged(DashboardWidget* w) -{ - QList dLst=findChildren(QString()); - - //maximise the given panel - if(w->isMaximised()) - { - bool hasMaxApp=hasMaximisedApplied(); - //nothing can be maximised in practice at this point - Q_ASSERT(hasMaxApp == false); - savedDockState_=saveState(); - - Q_FOREACH(DashboardDock* d,dLst) - { - if(d->widget() == w) - d->setVisible(true); - else - { - DashboardWidget* dw=static_cast(d->widget()); - Q_ASSERT(dw); - dw->resetMaximised(); - d->setVisible(false); - } - } - } - //restore the previous state - else - { - resetMaximised(); - } -} - -bool Dashboard::hasMaximised() const -{ - for(int i=0; i < widgets_.count(); i++) - { - if(widgets_[i]->isMaximised()) - return true; - } - return false; -} - -bool Dashboard::hasMaximisedApplied() const -{ - QList dLst=findChildren(QString()); - Q_FOREACH(DashboardDock* d,dLst) - { - if(d->isVisible() == false) - return true; - } - return false; -} - -void Dashboard::resetMaximised() -{ - QList dLst=findChildren(QString()); - Q_FOREACH(DashboardDock* d,dLst) - { - DashboardWidget* dw=static_cast(d->widget()); - Q_ASSERT(dw); - dw->resetMaximised(); - d->setVisible(true); - } - - if(!savedDockState_.isEmpty()) - { - restoreState(savedDockState_); - savedDockState_.clear(); - } -} - -void Dashboard::checkMaximisedState() -{ - bool st=(widgets_.count() > 1); - for(int i=0; i < widgets_.count(); i++) - widgets_.at(i)->setEnableMaximised(st); -} - -//------------------------ -// Dialogs -//------------------------ - -void Dashboard::slotDialogFinished(int) -{ - if(DashboardDialog* dia=static_cast(sender())) - { - disconnect(this,0,dia->dashboardWidget(),0); - popupWidgets_.removeOne(dia->dashboardWidget()); - } -} - -void Dashboard::slotDialogClosed() -{ - if(DashboardDialog* dia=static_cast(sender())) - { - disconnect(this,0,dia->dashboardWidget(),0); - popupWidgets_.removeOne(dia->dashboardWidget()); - } -} - -//------------------------ -// Rescan -//------------------------ - -void Dashboard::reload() -{ - for(int i=0; i < widgets_.count(); i++) - widgets_.at(i)->reload(); -} - -void Dashboard::rerender() -{ - for(int i=0; i < widgets_.count(); i++) - widgets_.at(i)->rerender(); - - titleHandler_->updateTitle(); -} - -//------------------------ -// ViewMode -//------------------------ - -Viewer::ViewMode Dashboard::viewMode() -{ - //return handler_->currentMode(); - return Viewer::TreeViewMode; -} - -void Dashboard::setViewMode(Viewer::ViewMode mode) -{ - //handler_->setCurrentMode(mode); -} - -//---------------------------------- -// Save and load settings!! -//---------------------------------- - -void Dashboard::writeSettings(VComboSettings* vs) -{ - serverFilter_->writeSettings(vs); - - //Qt settings - if(savedDockState_.isEmpty() == false) - vs->putQs("state",savedDockState_); - else - vs->putQs("state",saveState()); - - //Other setting - vs->put("widgetCount",findChildren().count()); - - for(int i=0; i < widgets_.count(); i++) - { - std::string id=Dashboard::widgetSettingsId(i); - vs->beginGroup(id); - widgets_.at(i)->writeSettings(vs); - vs->endGroup(); - } -} - -void Dashboard::readSettings(VComboSettings* vs) -{ - settingsAreRead_=true; - - //This will create the ServerHandler objects - serverFilter_->readSettings(vs); - - //At this point each ServerHandler is running its reset()! - - Q_FOREACH(QWidget* w,findChildren()) - { - UiLog().dbg() << "DashBoard::readSettings() dock: " << w->objectName(); - } - - //Read the information about the dashboard widgets. - int cnt=vs->get("widgetCount",0); - for(int i=0; i < cnt; i++) - { - std::string id=Dashboard::widgetSettingsId(i); - if(vs->contains(id)) - { - vs->beginGroup(id); - std::string type=vs->get("type",""); - std::string dockId=vs->get("dockId",""); - - //Create a dashboard widget - if(DashboardWidget *dw=addWidget(type,dockId)) - { - //This will make the widgets active!!! The ServerHandler reset() can - //be still running at this point! - dw->readSettings(vs); - } - vs->endGroup(); - } - } - - //Restore state of the dockwidgets. It will not create the dockwidgets - //themselves but set their states and geometry. This is based on - //the the dockwidgets's objectname, so that has to be unique. We need to call - //it when the dockwidgets have already been created. - if(vs->containsQs("state")) - { - restoreState(vs->getQs("state").toByteArray()); - } - - initialSelectionInView(); - - settingsAreRead_=false; -} - -#if 0 -void Dashboard::slotInfoPanelSelection(VInfo_ptr info) -{ - selectInTreeView(info); -} -#endif - - -void Dashboard::initialSelectionInView() -{ - Q_FOREACH(DashboardWidget* w,widgets_) - { - if(w->initialSelectionInView()) - { - return; - } - } -} - -bool Dashboard::selectInTreeView(VInfo_ptr info) -{ - if(!info) - return false; - - Q_FOREACH(DashboardWidget* w,widgets_) - { - if(w->type() == "tree") - { - w->setCurrentSelection(info); - return serverFilter_->isFiltered(info->server()); - } - } - - return false; -} - -VInfo_ptr Dashboard::currentSelectionInView() -{ - Q_FOREACH(DashboardWidget* w,widgets_) - { - VInfo_ptr info=w->currentSelection(); - if(info && info.get()) - return info; - } - - return VInfo_ptr(); -} - -//Find an unique id for a new dockwidget -QString Dashboard::uniqueDockId() -{ - QSet ids; - Q_FOREACH(QDockWidget* dw, findChildren()) - { - ids << dw->objectName(); - } - - for(unsigned i=0; i < 1000; i++) - { - QString uid="dock_" + QString::number(i); - if(!ids.contains(uid)) - { - return uid; - } - } - - //We should handle this situation!! - assert(0); - - return "dock_1000"; -} - -std::string Dashboard::widgetSettingsId(int i) -{ - return "widget_" + boost::lexical_cast(i); -} - -void Dashboard::notifyServerFilterAdded(ServerItem* item) -{ - if(!settingsAreRead_) - { - //If there are no views we automatically add a tree view - if(widgets_.count() == 0) - addWidget("tree"); - - Q_EMIT contentsChanged(); - } -} - -void Dashboard::notifyServerFilterRemoved(ServerItem* item) -{ - if(!settingsAreRead_) - Q_EMIT contentsChanged(); -} - -void Dashboard::notifyServerFilterChanged(ServerItem*) -{ - if(!settingsAreRead_) - Q_EMIT contentsChanged(); -} - -void Dashboard::notifyServerFilterDelete() -{ - //if(!settingsAreRead_) - // Q_EMIT contentsChanged(); -} - -//We need to do this in order to prevent the dock window context menu from popping up. -//See ECFLOW-894 -void Dashboard::contextMenuEvent(QContextMenuEvent * e) -{ - e->accept(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/DashboardDialog.cpp ecflow-4.11.1/Viewer/src/DashboardDialog.cpp --- ecflow-4.9.0/Viewer/src/DashboardDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "DashboardDialog.hpp" - -#include "DashboardWidget.hpp" -#include "SessionHandler.hpp" -#include "WidgetNameProvider.hpp" - -#include -#include -#include -#include - -DashboardDialog::DashboardDialog(QWidget *parent) : - QDialog(parent), - dw_(0) -{ - setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose); - - //Disable the default button - Q_FOREACH(QAbstractButton* b,buttonBox_->buttons()) - { - if(QPushButton* pb=buttonBox_->button(buttonBox_->standardButton(b)) ) - pb->setAutoDefault(false); - } - - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -DashboardDialog::~DashboardDialog() -{ - //dw_->deleteLater(); -} - -void DashboardDialog::add(DashboardWidget* dw) -{ - dw_=dw; - - //The dialog takes ownership of the widget - dw_->setParent(this); - - layout_->insertWidget(0,dw_,1); - - connect(dw_,SIGNAL(titleUpdated(QString,QString)), - this,SLOT(slotUpdateTitle(QString,QString))); - - dw_->populateDialog(); - dw_->readSettingsForDialog(); -} - -void DashboardDialog::reject() -{ - if(dw_) - dw_->writeSettingsForDialog(); - - writeSettings(); - QDialog::reject(); -} - -void DashboardDialog::closeEvent(QCloseEvent * event) -{ - if(dw_) - dw_->writeSettingsForDialog(); - - Q_EMIT aboutToClose(); - event->accept(); - writeSettings(); -} - -void DashboardDialog::slotUpdateTitle(QString txt,QString /*type*/) -{ - setWindowTitle(txt.remove("").remove("")); -} - -void DashboardDialog::slotOwnerDelete() -{ - this->deleteLater(); -} - -void DashboardDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("DashboardDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void DashboardDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("DashboardDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(520,500)); - } - - settings.endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/DashboardDialog.hpp ecflow-4.11.1/Viewer/src/DashboardDialog.hpp --- ecflow-4.9.0/Viewer/src/DashboardDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef DASHBOARDDIALOG_HPP_ -#define DASHBOARDDIALOG_HPP_ - -#include - -#include "ui_DashboardDialog.h" - -class DashboardWidget; - -class DashboardDialog : public QDialog, protected Ui::DashboardDialog -{ -Q_OBJECT - -public: - explicit DashboardDialog(QWidget *parent=0); - ~DashboardDialog(); - - void add(DashboardWidget*); - DashboardWidget* dashboardWidget() const {return dw_;} - -public Q_SLOTS: - void reject(); - void slotUpdateTitle(QString,QString); - void slotOwnerDelete(); - -Q_SIGNALS: - void aboutToClose(); - -protected: - void closeEvent(QCloseEvent * event); - void readSettings(); - void writeSettings(); - - DashboardWidget* dw_; - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/DashboardDialog.ui ecflow-4.11.1/Viewer/src/DashboardDialog.ui --- ecflow-4.9.0/Viewer/src/DashboardDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ - - - DashboardDialog - - - - 0 - 0 - 538 - 251 - - - - - 0 - 0 - - - - About EcflowUI - - - false - - - - 4 - - - 2 - - - 4 - - - 4 - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - - buttonBox_ - accepted() - DashboardDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox_ - rejected() - DashboardDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/DashboardDock.cpp ecflow-4.11.1/Viewer/src/DashboardDock.cpp --- ecflow-4.9.0/Viewer/src/DashboardDock.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDock.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,238 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "DashboardDock.hpp" - -#include -#include - -#include "DashboardWidget.hpp" -#include "IconProvider.hpp" - -DashboardDockTitleWidget::DashboardDockTitleWidget(QWidget *parent) : - QWidget(parent), - titleBc_(0) -{ - setupUi(this); - - setProperty("dockTitle","1"); - - //We define the style here because it did not work from the qss file - QPalette p=palette(); - QLinearGradient gr(0,0,0,1); - gr.setCoordinateMode(QGradient::ObjectBoundingMode); - gr.setColorAt(0,QColor(126,126,126)); - gr.setColorAt(0.4,QColor(105,105,105)); - gr.setColorAt(0.41,QColor(97,97,97)); - gr.setColorAt(1,QColor(70,70,70)); - p.setBrush(QPalette::Window,gr); - setPalette(p); - - //Add warning icon - IconProvider::add(":viewer/warning.svg","warning"); - - //Title icon + text - p=titleLabel_->palette(); - p.setColor(QPalette::WindowText,Qt::white); - titleLabel_->setPalette(p); - titleLabel_->hide(); - titleIconLabel_->hide(); - - detachedTb_->setProperty("docktitle","1"); - maxTb_->setProperty("docktitle","1"); - optionsTb_->setProperty("docktitle","1"); - closeTb_->setProperty("docktitle","1"); -} - -void DashboardDockTitleWidget::setBcWidget(QWidget *w) -{ - Q_ASSERT(mainLayout->count() > 1); - Q_ASSERT(w); - titleBc_=w; - mainLayout->insertWidget(1,titleBc_,1); -#if 0 - for(int i=0; i < mainLayout->count(); i++) - { - if(QLayoutItem* item=mainLayout->itemAt(i)) - { - if(item->widget() == titleIconLabel_) - { - titleBc_=w; - mainLayout->insertWidget(i,titleBc_,1); - return; - } - } - } -#endif - - Q_ASSERT(titleBc_); -} - -void DashboardDockTitleWidget::setDetachedAction(QAction *ac) -{ - detachedTb_->setDefaultAction(ac); -} - -void DashboardDockTitleWidget::setMaximisedAction(QAction *ac) -{ - maxTb_->setDefaultAction(ac); -} - -void DashboardDockTitleWidget::addActions(QList lst) -{ - Q_FOREACH(QAction* ac,lst) - { - QToolButton *tb=new QToolButton(this); - tb->setProperty("docktitle","1"); - tb->setDefaultAction(ac); - tb->setAutoRaise(true); - tb->setPopupMode(QToolButton::InstantPopup); - //tb->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - - QPalette p=tb->palette(); - p.setColor(QPalette::ButtonText,Qt::white); - tb->setPalette(p); - - actionLayout_->addWidget(tb); - - if(!ac->isEnabled()) - tb->hide(); - - actionTbList_ << tb; - - connect(ac,SIGNAL(changed()), - this,SLOT(slotActionChanged())); - } - - //Hide the separator line if no buttons were added - //if(actionTbList_.isEmpty()) - // line_->hide(); -} - -void DashboardDockTitleWidget::slotActionChanged() -{ - Q_FOREACH(QToolButton *tb, actionTbList_) - { - if(!tb->isEnabled()) - { - tb->hide(); - } - else - { - tb->show(); - } - } -} - - -QSize DashboardDockTitleWidget::sizeHint() const -{ - QSize s=QWidget::sizeHint(); - //s.setHeight(); - return s; -} - -QSize DashboardDockTitleWidget::minimumSizeHint() const -{ - QSize s=QWidget::minimumSizeHint(); - //s.setHeight(16); - return s; -} - -QToolButton* DashboardDockTitleWidget::optionsTb() const -{ - return optionsTb_; -} - -#if 0 -void DashboardDockTitleWidget::on_floatTb__clicked(bool) -{ - if(QDockWidget *dw = qobject_cast(parentWidget())) - { - if(dw->features().testFlag(QDockWidget::DockWidgetFloatable)) - { - bool current=dw->isFloating(); - dw->setFloating(!current); - } - } -} -#endif - -void DashboardDockTitleWidget::on_closeTb__clicked(bool) -{ - if(QDockWidget *dw = qobject_cast(parentWidget())) - { - dw->close(); - } -} - -void DashboardDockTitleWidget::slotUpdateTitle(QString txt,QString type) -{ - if(txt.isEmpty()) - { - titleLabel_->hide(); - titleIconLabel_->hide(); - } - else - { - if(type == "warning") - { - titleIconLabel_->setPixmap(IconProvider::pixmap("warning",16)); - titleIconLabel_->show(); - txt.prepend(" "); - txt.append(" "); - } - else - { - titleIconLabel_->show(); - } - - titleLabel_->show(); - titleLabel_->setText(txt); - } -} - -//============================================================ -// -// DashBoardDock -// -//============================================================ - -DashboardDock::DashboardDock(DashboardWidget *dw,QWidget * parent) : - QDockWidget(parent) -{ - setFeatures(QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetMovable); - - DashboardDockTitleWidget *dt=new DashboardDockTitleWidget(this); - - setTitleBarWidget(dt); - - dt->setDetachedAction(dw->detachedAction()); - dt->setMaximisedAction(dw->maximisedAction()); - dt->addActions(dw->dockTitleActions()); - - connect(dw,SIGNAL(titleUpdated(QString,QString)), - dt,SLOT(slotUpdateTitle(QString,QString))); - - dw->populateDockTitleBar(dt); - - setWidget(dw); -} - -void DashboardDock::showEvent(QShowEvent* event) -{ - QWidget::showEvent(event); -} - -void DashboardDock::closeEvent (QCloseEvent *event) -{ - QWidget::closeEvent(event); - Q_EMIT closeRequested(); -} diff -Nru ecflow-4.9.0/Viewer/src/DashboardDock.hpp ecflow-4.11.1/Viewer/src/DashboardDock.hpp --- ecflow-4.9.0/Viewer/src/DashboardDock.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDock.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef DASHBOARDDOCK_HPP_ -#define DASHBOARDDOCK_HPP_ - -#include - -#include "ui_DashboardDockTitleWidget.h" - -class DashboardWidget; -class QToolButton; - -class DashboardDockTitleWidget : public QWidget, protected Ui::DashboardDockTitleWidget -{ -Q_OBJECT - -public: - explicit DashboardDockTitleWidget(QWidget *parent=0); - - void addInfoPanelActions(); - QSize sizeHint() const; - QSize minimumSizeHint() const; - QToolButton* optionsTb() const; - void setBcWidget(QWidget *w); - void addActions(QList lst); - void setDetachedAction(QAction *ac); - void setMaximisedAction(QAction *ac); - -public Q_SLOTS: - void slotUpdateTitle(QString txt,QString type); - -protected Q_SLOTS: -#if 0 - void on_floatTb__clicked(bool); -#endif - void on_closeTb__clicked(bool); - void slotActionChanged(); - -Q_SIGNALS: - void detachedChanged(bool); - -protected: - QList actionTbList_; - QWidget* titleBc_; - QPixmap warnPix_; -}; - -class DashboardDock : public QDockWidget -{ -Q_OBJECT - -public: - explicit DashboardDock(DashboardWidget* dw,QWidget * parent=0); - -Q_SIGNALS: - void closeRequested(); - -protected: - void showEvent(QShowEvent* event); - void closeEvent (QCloseEvent *event); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/DashboardDockTitleWidget.ui ecflow-4.11.1/Viewer/src/DashboardDockTitleWidget.ui --- ecflow-4.9.0/Viewer/src/DashboardDockTitleWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardDockTitleWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,221 +0,0 @@ - - - DashboardDockTitleWidget - - - - 0 - 0 - 711 - 140 - - - - - 0 - 0 - - - - Form - - - - - - true - - - - 2 - - - QLayout::SetMinAndMaxSize - - - 0 - - - 2 - - - 0 - - - 2 - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 2 - 20 - - - - - - - - Qt::Horizontal - - - - 15 - 20 - - - - - - - - TextLabel - - - - - - - - 0 - 0 - - - - - 8 - - - - Title - - - Qt::AlignCenter - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - - 0 - - - - - - - Detach from selection in other panels - - - - - - - :/viewer/images/dock_float.svg:/viewer/images/dock_float.svg - - - false - - - false - - - - - - - Options - - - - - - - :/viewer/dock_config.svg:/viewer/dock_config.svg - - - false - - - QToolButton::InstantPopup - - - true - - - - - - - ... - - - - :/viewer/dock_max.svg:/viewer/dock_max.svg - - - - 16 - 16 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - Close panel - - - - - - - :/viewer/images/dock_close.svg:/viewer/images/dock_close.svg - - - - 16 - 16 - - - - true - - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/Dashboard.hpp ecflow-4.11.1/Viewer/src/Dashboard.hpp --- ecflow-4.9.0/Viewer/src/Dashboard.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Dashboard.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef DASHBOARD_HPP_ -#define DASHBOARD_HPP_ - -#include "Viewer.hpp" - -#include - -#include "ServerFilter.hpp" -#include "VInfo.hpp" -#include "VSettings.hpp" - -class DashboardTitle; -class DashboardWidget; -class ServerFilter; -class ServerItem; -class VComboSettings; - -class Dashboard : public QMainWindow, public ServerFilterObserver -{ - Q_OBJECT - -public: - Dashboard(QString,QWidget* parent=0); - ~Dashboard(); - - void reload(); - void rerender(); - - DashboardWidget* addWidget(const std::string& type); - DashboardWidget* addDialog(const std::string& type); - Viewer::ViewMode viewMode(); - void setViewMode(Viewer::ViewMode); - ServerFilter* serverFilter() const {return serverFilter_;} - VInfo_ptr currentSelection(); - void currentSelection(VInfo_ptr n); - bool selectInTreeView(VInfo_ptr); - void addSearchDialog(); - DashboardTitle* titleHandler() const {return titleHandler_;} - bool hasMaximised() const; - bool hasMaximisedApplied() const; - - void notifyServerFilterAdded(ServerItem* item); - void notifyServerFilterRemoved(ServerItem* item); - void notifyServerFilterChanged(ServerItem*); - void notifyServerFilterDelete(); - - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - void contentsChanged(); - void aboutToDelete(); - -public Q_SLOTS: - void slotPopInfoPanel(VInfo_ptr,QString); - void slotCommand(VInfo_ptr,QString); - -protected Q_SLOTS: - void slotDockClose(); - void slotDialogFinished(int); - void slotDialogClosed(); - void slotPopInfoPanel(QString); - void slotSelectionChanged(VInfo_ptr info); - void slotMaximisedChanged(DashboardWidget* w); - -protected: - void contextMenuEvent(QContextMenuEvent* e); - -private: - DashboardWidget* addWidgetCore(const std::string& type); - DashboardWidget* addWidget(const std::string& type,const std::string& dockId); - QString uniqueDockId(); - static std::string widgetSettingsId(int i); - void initialSelectionInView(); - VInfo_ptr currentSelectionInView(); - void addSearchDialog(VInfo_ptr); - void resetMaximised(); - void checkMaximisedState(); - - ServerFilter* serverFilter_; - DashboardTitle* titleHandler_; - QList widgets_; - QList popupWidgets_; - QByteArray savedDockState_; - bool settingsAreRead_; - static int maxWidgetNum_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/DashboardTitle.cpp ecflow-4.11.1/Viewer/src/DashboardTitle.cpp --- ecflow-4.9.0/Viewer/src/DashboardTitle.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardTitle.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,385 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "DashboardTitle.hpp" - -#include "Dashboard.hpp" -#include "ServerFilter.hpp" -#include "ServerHandler.hpp" -#include "VNode.hpp" -#include "UiLog.hpp" - -#include -#include - -int DashboardTitle::lighter_=150; - -//#define _UI_DASHBOARDTITLE_DEBUG - -DashboardTitle::DashboardTitle(ServerFilter* filter,Dashboard *parent) : - QObject(parent), - dashboard_(parent), - filter_(filter), - maxPixWidth_(0), - current_(false) -{ - filter_->addObserver(this); -} - -DashboardTitle::~DashboardTitle() -{ - clear(); -} - -void DashboardTitle::clear() -{ - if(!filter_) - return; - - filter_->removeObserver(this); - for(std::vector::const_iterator it=filter_->items().begin(); it !=filter_->items().end(); ++it) - { - ServerHandler* s=(*it)->serverHandler(); - s->removeServerObserver(this); - } - - filter_=0; -} - - -void DashboardTitle::notifyServerFilterAdded(ServerItem* item) -{ - ServerHandler* s=item->serverHandler(); - s->addServerObserver(this); - updateTitle(); -} - -void DashboardTitle::notifyServerFilterRemoved(ServerItem* item) -{ - ServerHandler* s=item->serverHandler(); - s->removeServerObserver(this); - updateTitle(); -} - -void DashboardTitle::notifyServerFilterChanged(ServerItem*) -{ - updateTitle(); -} - -void DashboardTitle::notifyServerFilterDelete() -{ - clear(); -} - -void DashboardTitle::notifyDefsChanged(ServerHandler* server, const std::vector& a) -{ - updateTitle(); -} - -void DashboardTitle::notifyServerDelete(ServerHandler* server) -{ - //server->removeServerObserver(this); -} - -void DashboardTitle::notifyEndServerClear(ServerHandler* server) -{ - updateTitle(); -} - -void DashboardTitle::notifyEndServerScan(ServerHandler* server) -{ - updateTitle(); -} - -void DashboardTitle::notifyServerConnectState(ServerHandler* server) -{ - updateTitle(); -} - -void DashboardTitle::notifyServerActivityChanged(ServerHandler* server) -{ - updateTitle(); -} - -void DashboardTitle::setMaxPixWidth(int w) -{ - maxPixWidth_=w; - updateTitle(); -} - -void DashboardTitle::setCurrent(bool b) -{ - if(b != current_) - { - current_=b; - updateTitle(); - } -} - -int DashboardTitle::fullWidth() const -{ - QFont f; - QFontMetrics fm(f); - const int gap=1; - const int padding=10; - int w=0; - for(size_t i=0; i < filter_->items().size(); i++) - { - QString str=QString::fromStdString(filter_->items()[i]->name()); - int tw=fm.width(str); - if(tw > w) w=tw; - } - - return 10+filter_->items().size()*(w+padding)+(filter_->items().size()-1)*gap; -} - -void DashboardTitle::updateTitle() -{ - pix_=QPixmap(); - tooltip_.clear(); - title_.clear(); - desc_.clear(); - descPix_=QPixmap(); - - if(!filter_ || filter_->itemCount()==0) - { - tooltip_=tr("No server is loaded in this this tab"); - desc_=tr("No server loaded in tab"); - Q_EMIT changed(this); - return; - } - - if(maxPixWidth_ == 0) - return; - - QStringList texts; - QList fillColors; - QList textColors; - - const std::vector& items=filter_->items(); - for(std::vector::const_iterator it=items.begin(); it != items.end(); ++it) - { - //Get text - QString str=QString::fromStdString((*it)->name()); - QString host=QString::fromStdString((*it)->host()); - QString port=QString::fromStdString((*it)->port()); - - //Get server status - ServerHandler* server=(*it)->serverHandler(); - fillColors << server->vRoot()->stateColour(); - textColors << server->vRoot()->stateFontColour(); - - texts << str; - - //Description for tab list menu - if(!desc_.isEmpty()) - { - desc_+=", "; - } - desc_+=str; - - //Tooltip - if(!tooltip_.isEmpty()) - { - tooltip_+="
        -------------------
        "; - } - tooltip_+=server->vRoot()->toolTip(); - } - - int num=texts.count(); - Q_ASSERT(num>0); - - { - const int marginX=0; - const int gap=1; - - int maxBandH=2; - int bandH=(current_)?maxBandH:2; - - QFont f; - QFontMetrics fm(f); - - QList textRects; - QList fillRects; - - //Compute the pixmap size - int h=fm.height()+bandH+1; - //int w=fm.width("ABCD...")+(num-1); - int w=maxPixWidth_-10; - int dw=w/num; - - int xp=0; - int yp=0; - bool noText=false; - - for(int i=0; i < texts.count(); i++) - { -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg() << i << texts[i] << dw << fm.width(texts[0]); -#endif - QString txt=fm.elidedText(texts[i],Qt::ElideRight,dw); -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg() << " " << txt << fm.width(txt); -#endif - QString ellips(0x2026); //horizontal ellipsis - - if(txt.startsWith(ellips)) - { - txt=texts[i].left(2); - txt=fm.elidedText(txt,Qt::ElideRight,dw); - } -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg() << " " << txt << fm.width(txt); -#endif - if(txt.startsWith(ellips)) - { - txt=texts[i].left(1); - txt=fm.elidedText(txt,Qt::ElideRight,dw); - } -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg()<< " " << txt << fm.width(txt); -#endif - if(txt.isEmpty()) - { - txt=texts[i].left(1); - } -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg() << " " << txt << fm.width(txt); -#endif - if(fm.width(txt) > dw) - { - texts[i]=""; - noText=true; - } - else - { - texts[i]=txt; - } -#ifdef _UI_DASHBOARDTITLE_DEBUG - UiLog().dbg() << " " << texts[i] << fm.width(texts[i]); -#endif - textRects << QRect(xp,yp,dw,fm.height()); - fillRects << QRect(xp,yp+fm.height(),dw,bandH); - xp=fillRects.back().right()+gap; - } - - w=xp-gap+marginX+2; - - //Render the pixmap - pix_=QPixmap(w,h); - pix_.fill(Qt::transparent); - QPainter painter(&pix_); - - for(int i=0; i < texts.count(); i++) - { - QColor bg=fillColors[i]; - - if(noText) - { - QRect r=fillRects[i]; - r.setTop(0); - r.setBottom(h-2); - fillRects[i]=r; - - QColor bgLight; - if(bg.value() < 235) - bgLight=bg.lighter(130); - else - bgLight=bg.lighter(lighter_); - - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(0,1); - - grad.setColorAt(0,bgLight); - grad.setColorAt(1,bg); - QBrush bgBrush(grad); - - QColor borderCol=bg.darker(150); - painter.setPen(borderCol); - painter.setBrush(bgBrush); - painter.drawRect(fillRects[i]); - } - else - { - QColor fg=QColor(0,0,0); - painter.setPen(fg); - painter.drawText(textRects[i],Qt::AlignCenter,texts[i]); - - QColor borderCol=bg.darker(140); - painter.setPen(borderCol); - painter.setBrush(bg); - painter.drawRect(fillRects[i]); - - if( i >0) - { - painter.setPen(QColor(140,140,140)); - painter.drawLine(fillRects[i].left(),fillRects[i].bottom(), - fillRects[i].left(),textRects[i].center().y()); - } - } - } - } - - //Desc pix - { - QFont f; - QFontMetrics fm(f);//Compute the pixmap size - int h=fm.height()+2; - //int w=fm.width("ABCD...")+(num-1); - int w=h*5/4; - int dw=w/num; - - descPix_=QPixmap(w+1,h+1); - descPix_.fill(Qt::transparent); - QPainter painter(&descPix_); - - int xp=0; - for(int i=0; i < fillColors.count(); i++) - { - QRect r(xp,0,dw,h); - QColor bg=fillColors[i]; - QColor bgLight; - if(bg.value() < 235) - bgLight=bg.lighter(130); - else - bgLight=bg.lighter(lighter_); - - QColor borderCol=bg.darker(150); - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(0,1); - - grad.setColorAt(0,bgLight); - grad.setColorAt(1,bg); - QBrush bgBrush(grad); - - painter.setPen(borderCol); - painter.setBrush(bgBrush); - painter.drawRect(r); - xp+=dw; - } - - } - - if(tooltip_.isEmpty()) - tooltip_=tr("No server is loaded in this this tab"); - - if(desc_.isEmpty()) - desc_=tr("No server loaded in tab"); - - Q_EMIT changed(this); -} - - - - - diff -Nru ecflow-4.9.0/Viewer/src/DashboardTitle.hpp ecflow-4.11.1/Viewer/src/DashboardTitle.hpp --- ecflow-4.9.0/Viewer/src/DashboardTitle.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardTitle.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef DASHBOARDTITLE_HPP_ -#define DASHBOARDTITLE_HPP_ - -#include -#include - -#include "ServerFilter.hpp" -#include "ServerObserver.hpp" - -class Dashboard; -class ServerItem; - -class DashboardTitle : public QObject, public ServerFilterObserver, public ServerObserver -{ -Q_OBJECT - -friend class Dashboard; - -public: - DashboardTitle(ServerFilter*,Dashboard *parent); - ~DashboardTitle(); - - Dashboard* dashboard() const {return dashboard_;} - QString title() const {return title_;} - QString tooltip() const {return tooltip_;} - QString desc() const {return desc_;} - QPixmap pix() const {return pix_;} - QPixmap descPix() const {return descPix_;} - void setMaxPixWidth(int w); - void setCurrent(bool b); - int fullWidth() const; - - void notifyServerFilterAdded(ServerItem* item); - void notifyServerFilterRemoved(ServerItem* item); - void notifyServerFilterChanged(ServerItem*); - void notifyServerFilterDelete(); - - void notifyDefsChanged(ServerHandler* server, const std::vector& a); - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server) {} - void notifyEndServerClear(ServerHandler* server); - void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - void notifyEndServerScan(ServerHandler* server); - void notifyServerConnectState(ServerHandler* server); - void notifyServerActivityChanged(ServerHandler* server); - void notifyServerSuiteFilterChanged(ServerHandler* server) {} - -Q_SIGNALS: - void changed(DashboardTitle*); - -private: - void clear(); - void updateTitle(); - - Dashboard* dashboard_; - ServerFilter* filter_; - int maxPixWidth_; - QPixmap pix_; - QPixmap descPix_; - QString title_; - QString tooltip_; - QString desc_; - bool current_; - static int lighter_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/DashboardWidget.cpp ecflow-4.11.1/Viewer/src/DashboardWidget.cpp --- ecflow-4.9.0/Viewer/src/DashboardWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#include "DashboardWidget.hpp" - -#include "VSettings.hpp" - -static QString tooltipUnchk("This panel is linked and will respond to selections made in other panels. Click to toggle.") ; -static QString tooltipChk("This panel is detached and will not respond to selections made in other panels. Click to toggle."); - -static QString tooltipMaxChk("This panel is maximised and hides other panels. Click to restore its original size.") ; -static QString tooltipMaxUnchk("Maximise panel"); - -DashboardWidget::DashboardWidget(const std::string& type, QWidget* parent) : - QWidget(parent), - type_(type), - acceptSetCurrent_(false), - ignoreMaximisedChange_(false), - bcWidget_(0), - inDialog_(false) -{ - //detach - detachedAction_=new QAction("Detached",this); - QIcon ic(QPixmap(":viewer/dock_chain_closed.svg")); - ic.addPixmap(QPixmap(":viewer/dock_chain_open.svg"),QIcon::Normal,QIcon::On); - detachedAction_->setIcon(ic); - detachedAction_->setCheckable(true); - detachedAction_->setChecked(false); - - connect(detachedAction_,SIGNAL(toggled(bool)), - this,SLOT(slotDetachedToggled(bool))); - - detachedAction_->setToolTip(tooltipUnchk); - - //maximise - maximisedAction_=new QAction("Maximise",this); - QIcon icM(QPixmap(":viewer/dock_max.svg")); - icM.addPixmap(QPixmap(":viewer/dock_restore.svg"),QIcon::Normal,QIcon::On); - icM.addPixmap(QPixmap(":viewer/dock_max_disabled.svg"),QIcon::Disabled,QIcon::Off); - maximisedAction_->setIcon(icM); - maximisedAction_->setCheckable(true); - maximisedAction_->setChecked(false); - maximisedAction_->setToolTip("max"); - connect(maximisedAction_,SIGNAL(toggled(bool)), - this,SLOT(slotMaximisedToggled(bool))); - - maximisedAction_->setToolTip(tooltipMaxUnchk); -} - -void DashboardWidget::slotDetachedToggled(bool b) -{ - detachedChanged(); - detachedAction_->setToolTip((detached())?tooltipChk:tooltipUnchk); -} - -void DashboardWidget::setDetached(bool b) -{ - if(detached() != b) - { - detachedAction_->setChecked(b); - } -} - -bool DashboardWidget::detached() const -{ - return detachedAction_->isChecked(); -} - -void DashboardWidget::slotMaximisedToggled(bool b) -{ - if(!ignoreMaximisedChange_) - Q_EMIT maximisedChanged(this); - - if(b) - Q_EMIT titleUpdated("Panel maximised!","warning"); - else - Q_EMIT titleUpdated(""); - - maximisedAction_->setToolTip((isMaximised())?tooltipMaxChk:tooltipMaxUnchk); -} - -bool DashboardWidget::isMaximised() const -{ - return maximisedAction_->isChecked(); -} - -//reset the state of the maximised actione without emitting a change signal -void DashboardWidget::resetMaximised() -{ - ignoreMaximisedChange_=true; - maximisedAction_->setChecked(false); - ignoreMaximisedChange_=false; -} - -void DashboardWidget::setEnableMaximised(bool st) -{ - resetMaximised(); - maximisedAction_->setEnabled(st); -} - -void DashboardWidget::setInDialog(bool b) -{ - if(inDialog_ != b) - { - inDialog_=b; - QIcon ic(QPixmap(":viewer/chain_closed.svg")); - ic.addPixmap(QPixmap(":viewer/chain_open.svg"),QIcon::Normal,QIcon::On); - detachedAction_->setIcon(ic); - } - - if(b) - { - maximisedAction_->setEnabled(false); - maximisedAction_->setVisible(false); - } -} - -void DashboardWidget::writeSettings(VComboSettings* vs) -{ - vs->putAsBool("detached",detached()); -} - -void DashboardWidget::readSettings(VComboSettings* vs) -{ - setDetached(vs->getAsBool("detached",detached())); -} diff -Nru ecflow-4.9.0/Viewer/src/DashboardWidget.hpp ecflow-4.11.1/Viewer/src/DashboardWidget.hpp --- ecflow-4.9.0/Viewer/src/DashboardWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DashboardWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#ifndef DASHBOARDWIDGET_HPP_ -#define DASHBOARDWIDGET_HPP_ - -#include -#include -#include -#include -#include - -#include "VInfo.hpp" - -class DashboardDockTitleWidget; -class NodePathWidget; -class ServerFilter; -class VComboSettings; - -class DashboardWidget : public QWidget -{ -Q_OBJECT - -public: - DashboardWidget(const std::string& type, QWidget* parent=0); - virtual ~DashboardWidget() {} - - virtual void populateDockTitleBar(DashboardDockTitleWidget*)=0; - virtual void populateDialog()=0; - virtual void reload()=0; - virtual void rerender()=0; - virtual bool initialSelectionInView() {return false;} - virtual VInfo_ptr currentSelection() {return VInfo_ptr(); } - QAction* detachedAction() const {return detachedAction_;} - QAction* maximisedAction() const {return maximisedAction_;} - virtual QList dockTitleActions() {return QList();} - - bool detached() const; - void setDetached(bool b); - bool isMaximised() const; - void resetMaximised(); - void setEnableMaximised(bool st); - bool isInDialog() const {return inDialog_;} - - virtual void writeSettings(VComboSettings*); - virtual void readSettings(VComboSettings*); - virtual void writeSettingsForDialog() {} - virtual void readSettingsForDialog() {} - - const std::string type() const {return type_;} - void id(const std::string& id) {id_=id;} - -public Q_SLOTS: - virtual void setCurrentSelection(VInfo_ptr)=0; - -Q_SIGNALS: - void titleUpdated(QString,QString type=QString()); - void selectionChanged(VInfo_ptr); - void maximisedChanged(DashboardWidget*); - void popInfoPanel(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - -protected Q_SLOTS: - void slotDetachedToggled(bool); - void slotMaximisedToggled(bool); - -protected: - virtual void detachedChanged()=0; - void setInDialog(bool); - - std::string id_; - std::string type_; - bool acceptSetCurrent_; - QAction *detachedAction_; - QAction *maximisedAction_; - bool ignoreMaximisedChange_; - NodePathWidget* bcWidget_; - -private: - bool inDialog_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/DirectoryHandler.cpp ecflow-4.11.1/Viewer/src/DirectoryHandler.cpp --- ecflow-4.9.0/Viewer/src/DirectoryHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DirectoryHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,492 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include - -#include - -#include - -#include - -#include "DirectoryHandler.hpp" -#include "File.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" - - -std::string DirectoryHandler::exeDir_; -std::string DirectoryHandler::shareDir_; -std::string DirectoryHandler::etcDir_; -std::string DirectoryHandler::configDir_; -std::string DirectoryHandler::rcDir_; -std::string DirectoryHandler::tmpDir_; -std::string DirectoryHandler::uiLogFile_; -std::string DirectoryHandler::uiEventLogFile_; - -static bool firstStartUp=false; - -DirectoryHandler::DirectoryHandler() -{ -} - -// ----------------------------------------------------------------------------- -// DirectoryHandler::init -// We set all the directory paths containing any of the config files used by the viewer. -// If we tell this class where the executable started from, then it can work out -// where all the other directories are -// ----------------------------------------------------------------------------- - -void DirectoryHandler::init(const std::string& exeStr) -{ - boost::filesystem::path configDir; - - //The location of the config dir is specified by the user - //This could be the case for a ui test! In this case we - //must not use the rcDir! - if(char *confCh=getenv("ECFUI_CONFIG_DIR")) - { - std::string confStr(confCh); - configDir=boost::filesystem::path(confStr); - } - //By default the config dir is .ecflow_ui in $HOME, - //in this case we might import older settings from $HOME/.ecflowrc - else if(char *h=getenv("HOME")) - { - std::string home(h); - boost::filesystem::path homeDir(home); - - configDir = boost::filesystem::path(homeDir); - configDir /= ".ecflow_ui"; - - boost::filesystem::path rcDir = homeDir; - rcDir /= ".ecflowrc"; - rcDir_=rcDir.string(); - } - - //Sets configDir_ and creates it if it does not exist - if(!configDir.string().empty()) - { - configDir_=configDir.string(); - if(!boost::filesystem::exists(configDir)) - { - firstStartUp=true; - - try - { - boost::filesystem::create_directory(configDir); - } - catch(const boost::filesystem::filesystem_error& err) - { - UserMessage::message(UserMessage::ERROR, true, - std::string("Could not create configDir: " + configDir_ + " reason: " + err.what())); - exit(1); - } - } - } - else - { - UserMessage::message(UserMessage::ERROR, true, - std::string("Could not create configDir with an empty path!")); - exit(1); - } - - //Sets paths in the system directory - boost::filesystem::path exePath(exeStr); - - //If the executable path does not exist we - //will use the value of the ECFLOW_SHARED_DIR macro to get - //the location of the "share/ecflow" dir. - if(!boost::filesystem::exists(exePath)) - { - boost::filesystem::path shareDir(ECFLOW_SHARED_DIR); - if(!boost::filesystem::exists(shareDir)) - { - UserMessage::message(UserMessage::ERROR, true, - std::string("Shared dir " + shareDir.string() + " does not exist! EcflowUI cannot be launched!")); - exit(0); - } - - boost::filesystem::path etcDir = shareDir; - etcDir /= "etc"; - - shareDir_ = shareDir.string(); - etcDir_ = etcDir.string(); - } - //If the executable path exits we probably use relative paths to it. - else - { - exeDir_=exePath.parent_path().string(); - - //TODO: make it work when we run it from within "bin" - boost::filesystem::path shareDir = exePath.parent_path().parent_path(); - - shareDir /= "share"; - shareDir /= "ecflow"; - - boost::filesystem::path etcDir = shareDir; - etcDir /= "etc"; - - shareDir_ = shareDir.string(); - etcDir_ = etcDir.string(); - } - - //Tmp dir - if(char *h=getenv("ECFLOWUI_TMPDIR")) - { - tmpDir_=std::string(h); - } - else if(char *h=getenv("TMPDIR")) - { - tmpDir_=std::string(h); - boost::filesystem::path tmp(tmpDir_); - tmp /= "eclow_ui.tmp"; - tmpDir_=tmp.string(); - if(!boost::filesystem::exists(tmp)) - { - UiLog().warn() << "ECFLOWUI_TMPDIR env variable is not defined. ecFlowUI creates its tmp direcoty in TMPDIR as " << - tmp.string(); - - try - { - if(boost::filesystem::create_directory(tmp)) - { - UiLog().dbg() << "Tmp dir created: " << tmpDir_; - } - } - catch (const boost::filesystem::filesystem_error& e) - { - UserMessage::message(UserMessage::ERROR,true,"Creating tmp directory failed:" + std::string(e.what())); - } - } - } - else - { - UserMessage::message(UserMessage::ERROR, true, - "Neither of ECFLOWUI_TMPDIR and TMPDIR are defined. ecflowUI cannot be started up!"); - exit(1); - } - - //Ui log. The ui logging either goes into the stdout or into a - //file. The startup script desides on it. - if(char *h=getenv("ECFLOWUI_LOGFILE")) - { - uiLogFile_=std::string(h); - } - - //Ui event log file. Ui event logging always goes into a file - if(char *h=getenv("ECFLOWUI_UI_LOGFILE")) - { - uiEventLogFile_=std::string(h); - } - else - { - boost::filesystem::path tmp(tmpDir_); - tmp /= "ecflowui_uilog.txt"; - uiEventLogFile_=tmp.string(); - } -} - - -std::string DirectoryHandler::concatenate(const std::string &path1, const std::string &path2) -{ - boost::filesystem::path p1(path1); - boost::filesystem::path p2(path2); - boost::filesystem::path result = p1 /= p2; - return result.string(); -} - - -void DirectoryHandler::findDirContents(const std::string &dirPath,const std::string &filterStr, FileType type, std::vector& res) -{ - boost::filesystem::path path(dirPath); - boost::filesystem::directory_iterator it(path), eod; - - const boost::regex expr(filterStr); - - BOOST_FOREACH(boost::filesystem::path const &p, std::make_pair(it, eod )) - { - boost::smatch what; - std::string fileName=p.filename().string(); - - bool rightType = (type == File) ? is_regular_file(p) : is_directory(p); // file or directory? - - if(rightType && boost::regex_match(fileName, what,expr)) - { - res.push_back(fileName); - } - } -} - - -void DirectoryHandler::findFiles(const std::string &dirPath,const std::string &filterStr,std::vector& res) -{ - findDirContents(dirPath, filterStr, File, res); -} - -void DirectoryHandler::findDirs(const std::string &dirPath,const std::string &filterStr,std::vector& res) -{ - findDirContents(dirPath, filterStr, Dir, res); -} - -bool DirectoryHandler::createDir(const std::string& path) -{ - //Create configDir if if does not exist - if(!boost::filesystem::exists(path)) - { - try - { - boost::filesystem::create_directory(path); - } - catch(const boost::filesystem::filesystem_error& err) - { - UserMessage::message(UserMessage::ERROR, true, - "Could not create dir: " + path + " reason: " + err.what()); - return false; - } - } - - return true; // will also return true if the directory already exists -} - -bool DirectoryHandler::isFirstStartUp() -{ - return firstStartUp; -} - -//Return a unique non-existing tmp filename -std::string DirectoryHandler::tmpFileName() -{ - boost::filesystem::path tmp(tmpDir_); - if(boost::filesystem::exists(tmp)) - { - try - { - boost::filesystem::path model= tmp; - model /= "%%%%-%%%%-%%%%-%%%%"; - return boost::filesystem::unique_path(model).string(); - } - catch(const boost::filesystem::filesystem_error& err) - { - UiLog().warn() << "Could not generate tmp filename! Reason: " << err.what(); - } - } - - return std::string(); -} - - -// ----------------------------------------------------- -// copyDir -// recursively copy a directory and its contents -// ----------------------------------------------------- - -bool DirectoryHandler::copyDir(const std::string &srcDir, const std::string &destDir, std::string &errorMessage) -{ - boost::filesystem::path src(srcDir); - boost::filesystem::path dest(destDir); - - // does the source directory exist (and is a directory)? - if (!boost::filesystem::exists(src) || !boost::filesystem::is_directory(src)) - { - errorMessage = "Source directory (" + srcDir + ") does not exist"; - return false; - } - - - // create the destination directory if it does not already exist - if (!boost::filesystem::exists(dest)) - { - bool created = createDir(dest.string()); - if (!created) - { - errorMessage = "Could not create destination directory (" + destDir + ")"; - return false; - } - } - - // go through all the files/dirs in the dir - bool ok = true; - boost::filesystem::directory_iterator it(src), eod; - BOOST_FOREACH(boost::filesystem::path const &p, std::make_pair(it, eod)) - { - std::string fileName = p.filename().string(); - std::string srcFile=p.string(); - - if (is_regular_file(p)) // file? then copy it into its new home - { - //The original boost based copy implementation did not work with newer compilers, - //so we opted for a Qt based implementation. See ECFLOW-1207 - boost::filesystem::path destPath=dest / p.filename(); - std::string destFile=destPath.string(); - if(!copyFile(srcFile,destFile,errorMessage)) - return false; - -#if 0 - try - { - boost::filesystem::copy_file(p, dest / p.filename()); - } - catch (const boost::filesystem::filesystem_error& err) - { - errorMessage = "Could not copy file " + fileName + " to " + destDir + "; reason: " + err.what(); - return false; - } -#endif - - } - else if (is_directory(p)) // directory? then copy it recursively - { - boost::filesystem::path destSubDir(destDir); - destSubDir /= p.filename(); - ok = ok && copyDir(p.string(), destSubDir.string(), errorMessage); - } - } - - return ok; -} - - -// -------------------------------------------------------- -// removeDir -// Recursively removes the given directory and its contents -// -------------------------------------------------------- - -bool DirectoryHandler::removeDir(const std::string &dir, std::string &errorMessage) -{ - try - { - boost::filesystem::path d(dir); - remove_all(d); - } - catch (const boost::filesystem::filesystem_error& err) - { - errorMessage = "Could not remove directory " + dir + "; reason: " + err.what(); - return false; - } - - return true; -} - -// -------------------------------------------------------- -// renameDir -// Same as a 'move' - renames the directory -// -------------------------------------------------------- - -bool DirectoryHandler::renameDir(const std::string &dir, const std::string &newName, std::string &errorMessage) -{ - try - { - boost::filesystem::path d1(dir); - boost::filesystem::path d2(newName); - rename(d1, d2); - } - catch (const boost::filesystem::filesystem_error& err) - { - errorMessage = "Could not rename directory " + dir + "; reason: " + err.what(); - return false; - } - - return true; -} - - -// ------------------------------------------------------------- -// copyFile -// Copies the given file to the given destintation (full paths). -// ------------------------------------------------------------- - -bool DirectoryHandler::copyFile(const std::string &srcFile, std::string &destFile, std::string &errorMessage) -{ - //The original boost based copy implementation did not work with newer compilers, - //so we opted for a Qt based implementation. See ECFLOW-1207 - - if(srcFile == destFile) - return true; - - QString src=QString::fromStdString(srcFile); - QString dest=QString::fromStdString(destFile); - - if(!QFile::exists(src)) - { - errorMessage = "Could not copy file " + srcFile + " to " + destFile + "; reason: source file does not exist"; - return false; - } - - if(QFile::exists(dest)) - { - QFile::remove(dest); - } - if(!QFile::copy(src,dest)) - { - errorMessage = "Could not copy file " + srcFile + " to " + destFile; - return false; - } - return true; - -#if 0 - boost::filesystem::path src(srcFile); - boost::filesystem::path dest(destFile); - - try - { - boost::filesystem::copy_file(src, dest, boost::filesystem::copy_option::overwrite_if_exists); - } - catch (const boost::filesystem::filesystem_error& err) - { - errorMessage = "Could not copy file " + srcFile + " to " + destFile + "; reason: " + err.what(); - return false; - } - - return true; -#endif -} - - -// -------------------------------------------------------- -// removeFile -// Removes the given file -// -------------------------------------------------------- - -bool DirectoryHandler::removeFile(const std::string &path, std::string &errorMessage) -{ - try - { - boost::filesystem::path f(path); - remove(f); - } - catch (const boost::filesystem::filesystem_error& err) - { - errorMessage = "Could not remove file " + path + "; reason: " + err.what(); - return false; - } - - return true; -} - -bool DirectoryHandler::truncateFile(const std::string &path,int lastLineNum,std::string &errorMessage) -{ - std::string s=ecf::File::get_last_n_lines(path,lastLineNum,errorMessage); - if(!errorMessage.empty()) - { - errorMessage="Could not truncate file " + path + "; reason: " + errorMessage; - return false; - } - - if(!ecf::File::create(path,s,errorMessage)) - { - errorMessage="Could not truncate file " + path + "; reason: " + errorMessage; - return false; - } - - return true; -} diff -Nru ecflow-4.9.0/Viewer/src/DirectoryHandler.hpp ecflow-4.11.1/Viewer/src/DirectoryHandler.hpp --- ecflow-4.9.0/Viewer/src/DirectoryHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/DirectoryHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef DIRECTORY_HANDLER_HPP_ -#define DIRECTORY_HANDLER_HPP_ - -#include - -class DirectoryHandler -{ -public: - enum FileType {File, Dir}; - - DirectoryHandler(); - - static void init(const std::string& exePath); - static const std::string& exeDir() {return exeDir_;} - static const std::string& shareDir() {return shareDir_;} - static const std::string& etcDir() {return etcDir_;} - static const std::string& configDir() {return configDir_;} - static const std::string& rcDir() {return rcDir_;} - static const std::string& uiLogFileName() {return uiLogFile_;} - static const std::string& uiEventLogFileName() {return uiEventLogFile_;} - static std::string concatenate(const std::string &path1, const std::string &path2); - static std::string tmpFileName(); - static bool createDir(const std::string& path); - - static void findDirContents(const std::string &dirPath,const std::string &filterStr, - FileType type, std::vector& res); - - static void findFiles(const std::string &dirPath,const std::string ®ExpPattern, - std::vector& res); - - static void findDirs(const std::string &dirPath,const std::string ®ExpPattern, - std::vector& res); - - static bool copyDir(const std::string &srcDir, const std::string &destDir, std::string &errorMessage); - static bool removeDir(const std::string &dir, std::string &errorMessage); - static bool renameDir(const std::string &dir, const std::string &newName, std::string &errorMessage); - static bool copyFile(const std::string &srcFile, std::string &destFile, std::string &errorMessage); - static bool removeFile(const std::string &file, std::string &errorMessage); - static bool truncateFile(const std::string &path,int lastLineNum,std::string &errorMessage); - static bool isFirstStartUp(); - -private: - static std::string exeDir_; - static std::string shareDir_; - static std::string etcDir_; - static std::string configDir_; - static std::string rcDir_; - static std::string tmpDir_; - static std::string uiLogFile_; - static std::string uiEventLogFile_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/EditItemWidget.cpp ecflow-4.11.1/Viewer/src/EditItemWidget.cpp --- ecflow-4.9.0/Viewer/src/EditItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,157 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "EditItemWidget.hpp" - -#include "EditProvider.hpp" -#include "Highlighter.hpp" -#include "PropertyMapper.hpp" -#include "VConfig.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "MessageLabel.hpp" - -//======================================================== -// -// EditItemWidget -// -//======================================================== - -EditItemWidget::EditItemWidget(QWidget *parent) : - QWidget(parent) -{ - setupUi(this); - - infoProvider_=new EditProvider(this); - - //The document becomes the owner of the highlighter - new Highlighter(textEdit_->document(),"script"); - - searchLine_->setEditor(textEdit_); - - searchLine_->setVisible(false); - - externTb_->hide(); - - //connect(submitTb_,SIGNAL(clicked(bool)), - // this,SLOT(on_submitTb__clicked(bool))); - - textEdit_->setProperty("edit","1"); - textEdit_->setFontProperty(VConfig::instance()->find("panel.edit.font")); -} - -QWidget* EditItemWidget::realWidget() -{ - return this; -} - -void EditItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - info_=info; - - //Info must be a node - if(info_ && info_->isNode() && info_->node()) - { - //Get file contents - EditProvider* ep=static_cast(infoProvider_); - ep->preproc(preproc()); - infoProvider_->info(info_); - } -} - -void EditItemWidget::clearContents() -{ - InfoPanelItem::clear(); - textEdit_->clear(); - infoLabel_->clear(); - infoLabel_->hide(); -} - -void EditItemWidget::infoReady(VReply* reply) -{ - infoLabel_->hide(); - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); -} - -void EditItemWidget::infoFailed(VReply* reply) -{ - infoLabel_->showError(QString::fromStdString(reply->errorText())); - infoLabel_->show(); - //UserMessage::message(UserMessage::ERROR, true, reply->errorText()); -} - -void EditItemWidget::infoProgress(VReply*) -{ - -} - -void EditItemWidget::on_preprocTb__toggled(bool) -{ - reload(info_); -} - -void EditItemWidget::on_submitTb__clicked(bool) -{ - QStringList lst=textEdit_->toPlainText().split("\n"); - std::vector txt; - Q_FOREACH(QString s,lst) - { - txt.push_back(s.toStdString()); - } - - EditProvider* ep=static_cast(infoProvider_); - ep->submit(txt,alias()); -} - -void EditItemWidget::on_searchTb__clicked() -{ - searchLine_->setVisible(true); - searchLine_->setFocus(); - searchLine_->selectAll(); -} - -void EditItemWidget::on_gotoLineTb__clicked() -{ - textEdit_->gotoLine(); -} - -bool EditItemWidget::alias() const -{ - return aliasCb_->isChecked(); -} - -bool EditItemWidget::preproc() const -{ - return preprocTb_->isChecked(); -} - -//----------------------------------------- -// Fontsize management -//----------------------------------------- - -void EditItemWidget::on_fontSizeUpTb__clicked() -{ - //We need to call a custom slot here instead of "zoomIn"!!! - textEdit_->slotZoomIn(); -} - -void EditItemWidget::on_fontSizeDownTb__clicked() -{ - //We need to call a custom slot here instead of "zoomOut"!!! - textEdit_->slotZoomOut(); -} - -static InfoPanelItemMaker maker1("edit"); diff -Nru ecflow-4.9.0/Viewer/src/EditItemWidget.hpp ecflow-4.11.1/Viewer/src/EditItemWidget.hpp --- ecflow-4.9.0/Viewer/src/EditItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef EDITITEMWIDGET_HPP_ -#define EDITITEMWIDGET_HPP_ - -#include - -#include "InfoPanelItem.hpp" -#include "VInfo.hpp" - -#include "ui_EditItemWidget.h" - -class EditItemWidget : public QWidget, public InfoPanelItem, protected Ui::EditItemWidget -{ -Q_OBJECT - -public: - explicit EditItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected Q_SLOTS: - void on_preprocTb__toggled(bool); - void on_submitTb__clicked(bool); - void on_searchTb__clicked(); - void on_gotoLineTb__clicked(); - void on_fontSizeUpTb__clicked(); - void on_fontSizeDownTb__clicked(); - -protected: - bool preproc() const; - bool alias() const; - void updateState(const ChangeFlags&) {} -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/EditItemWidget.ui ecflow-4.11.1/Viewer/src/EditItemWidget.ui --- ecflow-4.9.0/Viewer/src/EditItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,223 +0,0 @@ - - - EditItemWidget - - - - 0 - 0 - 494 - 484 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Increase font size in text editor <br><code>Ctrl++</code> - - - ... - - - - :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg - - - Ctrl++ - - - true - - - - - - - Decrease font size in text editor <br><code>Ctrl+-</code> - - - ... - - - - :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg - - - Ctrl+- - - - true - - - - - - - ... - - - - - - - Show search bar (CTRL-F) - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - true - - - true - - - - - - - Goto line number (CTRL-L) - - - ... - - - - :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg - - - Ctrl+L - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Submit as alias - - - - - - - Pre-process text - - - Pre-process - - - true - - - - - - - Submit - - - Submit - - - - :/viewer/submit.svg:/viewer/submit.svg - - - Qt::ToolButtonTextBesideIcon - - - - - - - - - - - - - 0 - 1 - - - - QPlainTextEdit::NoWrap - - - false - - - - - - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - PlainTextSearchLine - QWidget -
        PlainTextSearchLine.hpp
        - 1 -
        - - PlainTextEdit - QPlainTextEdit -
        PlainTextEdit.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/EditorInfoLabel.cpp ecflow-4.11.1/Viewer/src/EditorInfoLabel.cpp --- ecflow-4.9.0/Viewer/src/EditorInfoLabel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditorInfoLabel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,86 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "EditorInfoLabel.hpp" - -#include -#include - - -EditorInfoLabel::EditorInfoLabel(QWidget* parent) : QLabel(parent) -{ - //Define id for the css - setProperty("editorInfo","1"); - setWordWrap(true); - - //Set size policy - /*QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed); - sizePolicy.setHorizontalStretch(0); - sizePolicy.setVerticalStretch(0); - sizePolicy.setHeightForWidth(this->sizePolicy().hasHeightForWidth()); - setSizePolicy(sizePolicy); - //setMinimumSize(QSize(0, 60)); - //setMaximumSize(QSize(16777215, 45));*/ - - setMargin(4); - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - - //Other settings - setAutoFillBackground(true); - - setFrameShape(QFrame::StyledPanel); - setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); -} - -void EditorInfoLabel::setInfo(QString parent,QString type) -{ - setText(formatKeyLabel("Node path: ") + formatNodePath(parent) + "
        " + - formatKeyLabel("Attibute type: ") + type); -} - -static QColor nodeNameColour(7,108,209); -static QColor serverNameColour(0,0,0); -static QColor labelColour(59,60,61); - -QString EditorInfoLabel::formatKeyLabel(QString n) -{ - return "" + n + ""; -} - -QString EditorInfoLabel::formatNodeName(QString n) -{ - return "" + n + ""; -} - -QString EditorInfoLabel::formatNodePath(QString p) -{ - if(p.endsWith("://")) - { - return p; - } - - QStringList lst=p.split("/"); - if(lst.count() > 0) - { - QString n=lst.back(); - lst.pop_back(); - QColor c(80,80,80); - QString s="" + lst.join("/") + "/" + - "" + n + ""; - return s; - } - - return p; -} - - - - - diff -Nru ecflow-4.9.0/Viewer/src/EditorInfoLabel.hpp ecflow-4.11.1/Viewer/src/EditorInfoLabel.hpp --- ecflow-4.9.0/Viewer/src/EditorInfoLabel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditorInfoLabel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef EDITORINFOLABEL_HPP -#define EDITORINFOLABEL_HPP - -#include - -class EditorInfoLabel : public QLabel -{ -public: - explicit EditorInfoLabel(QWidget* parent=0); - void setInfo(QString parent,QString type); - - static QString formatKeyLabel(QString n); - static QString formatNodeName(QString n); - static QString formatNodePath(QString p); -}; - -#endif // EDITORINFOLABEL_HPP diff -Nru ecflow-4.9.0/Viewer/src/EditProvider.cpp ecflow-4.11.1/Viewer/src/EditProvider.cpp --- ecflow-4.9.0/Viewer/src/EditProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "EditProvider.hpp" - -#include "NodeFwd.hpp" - -#include "VNode.hpp" -#include "VReply.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" - -#include - -//Node -void EditProvider::visit(VInfoNode* info) -{ - //Reset the reply - reply_->reset(); - - if(!info) - { - owner_->infoFailed(reply_); - return; - } - - ServerHandler* server=info_->server(); - VNode *n=info->node(); - - if(!n || !n->node()) - { - owner_->infoFailed(reply_); - return; - } - - if (preproc_) - { - //Define a task for getting the info from the server. - task_=VTask::create(VTask::ScriptPreprocTask,n,this); - } - else - { - task_=VTask::create(VTask::ScriptEditTask,n,this); - } - - //Run the task in the server. When it finishes taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - server->run(task_); -} - -void EditProvider::submit(const std::vector& txt,bool alias) -{ - if(!(info_ && info_.get() && info_->isNode() && info_->node()) && info_->server()) - return; - - VNode *node=info_->node(); - ServerHandler* server=info_->server(); - - bool run=true; - - //--------------------------- - // Extract user variables - //--------------------------- - - static std::string defMicro="%"; - static std::string comStartText = "comment - ecf user variables"; - static std::string comEndText = "end - ecf user variables"; - - NameValueVec vars; - - //Find out the micro - std::string microVar=node->findInheritedVariable("ECF_MICRO"); - std::string micro = (microVar.size() == 1) ? microVar.c_str() : defMicro; - - //Find out the full comment start and end texts - std::string comStart= micro + comStartText; - std::string comEnd= micro + comEndText; - - bool inVars = false; - for(std::vector::const_iterator it=txt.begin(); it != txt.end(); ++it) - { - const std::string& line=*it; - - //We are in the variable block - if(inVars) - { - std::size_t pos = (*it).find("="); - if(pos != std::string::npos && pos+1 != (*it).size()) - { - std::string name=(*it).substr(0,pos); - std::string val=(*it).substr(pos+1); - boost::trim(name); - boost::trim(val); - vars.push_back(std::make_pair(name,val)); - } - } - - if(line == comStart) - inVars = true; - - if(line == comEnd) - break; - } - - if(vars.empty()) - { - UiLog().dbg() << " No user variables!"; - } - - - task_=VTask::create(VTask::ScriptSubmitTask,node,this); - task_->contents(txt); - task_->vars(vars); - task_->param("alias",(alias)?"1":"0"); - task_->param("run",(run)?"1":"0"); - - //Run the task in the server. When it finishes taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - server->run(task_); -} diff -Nru ecflow-4.9.0/Viewer/src/EditProvider.hpp ecflow-4.11.1/Viewer/src/EditProvider.hpp --- ecflow-4.9.0/Viewer/src/EditProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/EditProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef EDITPROVIDER_HPP_ -#define EDITPROVIDER_HPP_ - -#include "VDir.hpp" -#include "VInfo.hpp" -#include "InfoProvider.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -class EditProvider : public InfoProvider -{ -public: - explicit EditProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::OutputTask), preproc_(false) {} - - void visit(VInfoNode*); - void submit(const std::vector& txt,bool alias); - - void preproc(bool b) {preproc_=b;} - -private: - bool preproc_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ExpandState.cpp ecflow-4.11.1/Viewer/src/ExpandState.cpp --- ecflow-4.9.0/Viewer/src/ExpandState.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ExpandState.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,395 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ExpandState.hpp" -#include "ExpandStateNode.hpp" -#include "AbstractNodeView.hpp" -#include "ServerHandler.hpp" -#include "TreeNodeModel.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VNode.hpp" -#include "VTree.hpp" - -#include - -//#define _UI_EXPANDSTATE_DEBUG - -ExpandState::ExpandState(AbstractNodeView* view,TreeNodeModel* model) : - view_(view), model_(model), root_(0) -{ -} - -ExpandState::~ExpandState() -{ - clear(); -} - -void ExpandState::init(const VNode *vnode) -{ - clear(); - - ServerHandler* server=vnode->server(); - Q_ASSERT(server); - QModelIndex idx=model_->serverToIndex(server); - bool expanded=view_->isExpanded(idx); - - root_=new ExpandStateNode(server->vRoot(),expanded); - - save(server->vRoot()); -} - -bool ExpandState::isEmpty() const -{ - return !root_ || root_->children_.size() == 0; -} - -void ExpandState::clear() -{ - if(root_) - delete root_; - - root_=0; -} - - -//Save the expand state for a whole subtree (it can be the whole VNode tree as well) -void ExpandState::save(const VNode *vnode) -{ - UI_FUNCTION_LOG - UI_ASSERT(vnode,"node is null"); - UiLog().dbg() << " " << vnode->name(); - - if(!root_) - { - init(vnode); - Q_ASSERT(root_); - return; - } - - if(ExpandStateNode* es=find(vnode->absNodePath())) - { - Q_ASSERT(root_); - QModelIndex idx=model_->nodeToIndex(vnode); - - //It can happen that the VTree is empty but the server is already - //loaded. It is the situation when we load the same server in multiple - //tabs!!! We return here because there is nothing to save (and we would - //have a crash when trying to get index of the vnodes via the model!!) - if(VTreeNode* vtn=model_->indexToServerOrNode(idx)) - { - if(VTree* vt=vtn->root()) - { - if(vt->totalNum() == 0) - return; - } - } - - //save all the children recursively - save(vnode,es,idx); - } -} - -#if 0 -//Save the expand state for a whole subtree (it can be the whole VNode tree as well) -void ExpandState::save(const VNode *root) -{ - UI_FUNCTION_LOG - UI_ASSERT(root,"root is null"); - UiLog().dbg() << " " << root->name(); - - QModelIndex rootIdx=model_->nodeToIndex(root); - bool expanded=view_->isExpanded(rootIdx); - if(root_ == 0 || root_->name_ != root->strName()) - { - clear(); - root_=new ExpandStateNode(root,expanded); - } - else - { - root_->expanded_=expanded; - } - - //It can happen that the VTree is empty but the server is already - //loaded. It is the situation when we load the same server in multiple - //tabs!!! We return here because there is nothing to save (and we would - //have a crash when trying to get index of the vnodes via the model!!) - if(VTreeNode* vtn=model_->indexToServerOrNode(rootIdx)) - { - if(VTree* vt=vtn->root()) - { - if(vt->totalNum() == 0) - return; - } - } - - //save all the children recursively - save(root,root_,rootIdx); -} -#endif - -//Save the expand state for the given node. -// node: the vnode to save -// expandNode: the expand node representing the vnode -// idx: the modelindex of the vnode in the TreeNodeModel. It can be invalid -// since the model can use a filter and its tree (the VTree) might represent only -// the subset of all the vnodes of a given server -void ExpandState::save(const VNode *node,ExpandStateNode *expandNode,const QModelIndex& idx) -{ - std::size_t numExpandNode=expandNode->children_.size(); - std::size_t numNode=node->numOfChildren(); - - //the node and the expand node does not match. We clear the whole - //contents of the expand node - //CAN IT HAPPEN AT ALL?? - if(numExpandNode != numNode || expandNode->name_ != node->strName()) - { - UI_ASSERT(0,"numExpandNode=" << numExpandNode << " numNode=" << numNode << - " expandNode->name_=" << expandNode->name_ << " node->strName()=" << - node->strName()); - - expandNode->reset(node,view_->isExpanded(idx)); - //At the this point the expand node children vector is - //reserved, but contains null pointers - } - - for(std::size_t i=0; i < numNode; i++) - { - VNode* chNode=node->childAt(i); - QModelIndex chIdx=model_->nodeToIndex(chNode); - ExpandStateNode* chExpandNode=expandNode->children_[i]; - - //The expand node exists - if(chExpandNode) - { - //We only set the expand state when the child node is in the current VTree (i.e. is filtered). - //Otherwise we keep the original value - if(chIdx.isValid()) - chExpandNode->setExpanded(view_->isExpanded(chIdx)); - } - // ... create a new child expand node at the i-th place - else - { - chExpandNode=expandNode->setChildAt(i,chNode,view_->isExpanded(chIdx)); - } - - //save all the children recursively - save(chNode,chExpandNode,chIdx); - } -} - -//Collect the modelindexes of the expanded nodes in a VNode subtree. -//This is called after a server scan ended so the structure of the VNode tree and the -//expand tree might not match. In this case the expand tree has to be adjusted to the -//VNode tree. -void ExpandState::collectExpanded(const VNode* node,QSet& theSet) -{ -#ifdef _UI_EXPANDSTATE_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(node); - - if(!root_) - return; - - QModelIndex nodeIdx=model_->nodeToIndex(node); -#ifdef _UI_EXPANDSTATE_DEBUG - UiLog().dbg() << " root=" << root_->name_; -#endif - ExpandStateNode *expand=find(node->absNodePath()); - if(expand) - collectExpanded(expand,node,nodeIdx,theSet); - else - { -#ifdef _UI_EXPANDSTATE_DEBUG - UiLog().dbg() << " Node not found in expand tree"; -#endif - } -} - -void ExpandState::collectExpanded(ExpandStateNode *expand,const VNode* node,const QModelIndex& nodeIdx, - QSet& theSet) -{ -#ifdef _UI_EXPANDSTATE_DEBUG - UI_FUNCTION_LOG -#endif - //The contents of expand node and the vnode might differ. We try to - //adjust the expand node to the vnode with this call. - bool adjusted=expand->adjustContents(node); - - std::size_t numExpand=expand->children_.size(); - std::size_t numNode=node->numOfChildren(); - - UI_ASSERT(numExpand==numNode,"node=" << node->strName() << " numExpand=" << numExpand << - " numNode=" << numNode); - - //Lookup the node in the model - //QModelIndex nodeIdx=model_->nodeToIndex(node); - if(expand->expanded_ && nodeIdx.isValid()) - { - theSet.insert(nodeIdx); - -#ifdef _UI_EXPANDSTATE_DEBUG - UiLog().dbg() << " " << expand->name_; -#endif - } - - //We need to see what to do with newly added nodes. We either expand - //all its children or leave it unexpanded. It depends on the expandedAll_ - //flag set in any of the parents. - bool parentExpandedAll=false; - if(adjusted) - { - parentExpandedAll=needToExpandNewChild(expand,node->absNodePath()); - } - - for(std::size_t i=0; i < numExpand; i++) - { - VNode *chNode=node->childAt(i); - UI_ASSERT(chNode,"chNode is null"); - QModelIndex chIdx=model_->nodeToIndex(chNode); - ExpandStateNode *chExpand=expand->children_[i]; - - //An existing expand node - if(chExpand) - { - UI_ASSERT(chExpand->name_ == chNode->strName()," chExpand->name_=" << chExpand->name_ << " chNode->strName()=" << chNode->strName()); - } - - //A not-yet-allocated expand node. The corresponding vnode has just been - //added to the tree. We have to decide what to do with its expand state!!! - else - { - //create an expand node. Collapsed by default - chExpand=expand->setChildAt(i,chNode,0); - - //save all the children recursively - save(chNode,chExpand,chIdx); - - //expand recursively the new expand node if needed - if(parentExpandedAll) - chExpand->setExpandedRecursively(1); - else - { - UiLog().dbg() << " newly added node not expanded!!"; - } - - } - - collectExpanded(chExpand,chNode,chIdx,theSet); - } -} - - -void ExpandState::saveExpandAll(const VNode* node) -{ - UI_ASSERT(node,"node is null"); - if(ExpandStateNode* expand=find(node->absNodePath())) - { - expand->setExpandedAll(); - } -} - -void ExpandState::saveCollapseAll(const VNode* node) -{ - UI_ASSERT(node,"node is null"); - if(ExpandStateNode* expand=find(node->absNodePath())) - { - expand->setCollapsedAll(); - } -} - -//Find an expand node using its ful path -ExpandStateNode* ExpandState::find(const std::string& fullPath) -{ - if(!root_) - return NULL; - - if(fullPath.empty()) - return NULL; - - if(fullPath == "/") - return root_; - - std::vector pathVec; - boost::split(pathVec,fullPath,boost::is_any_of("/")); - - if(pathVec.size() > 0 && pathVec[0].empty()) - { - pathVec.erase(pathVec.begin()); - } - - return root_->find(pathVec); -} - -bool ExpandState::needToExpandNewChild(ExpandStateNode* expandNode,const std::string& expandNodePath) const -{ - - if(expandNode->expandedAll_ == 1) - return true; - - if(expandNode->collapsedAll_ == 1) - return false; - - //Checks if any of the parents have expandedAll set. In this case we - //expand recursively all the new expand node - std::vector parents; - collectParents(expandNodePath,parents); - for(std::vector::reverse_iterator it=parents.rbegin(); - it != parents.rend(); ++it) - { - if((*it)->expandedAll_ == 1) - { - return true; - } - else if((*it)->collapsedAll_ == 1) - { - return false; - } - } - return false; -} - -//We need to do it this way because we do not store the parents in the nodes for memory efficiency. -void ExpandState::collectParents(const std::string& fullPath,std::vector& parents) const -{ - if(!root_) - return; - - //how split works: - // str="/" -> "","" - // str="/a" -> "","a" - // str=" " -> " " - // str="" -> "" - - std::vector pathVec; - boost::split(pathVec,fullPath,boost::is_any_of("/")); - if(pathVec.size() > 0 && pathVec[0].empty()) - { - pathVec.erase(pathVec.begin()); - } - - ExpandStateNode *p=root_; - std::size_t num=pathVec.size(); - for(std::size_t i=0; i < num && p != 0; i++) - { - parents.push_back(p); - p=p->findChild(pathVec[i]); - } -} - - -void ExpandState::print() const -{ - if(!root_) - return; - - std::string indent=""; - root_->print(indent,true); -} diff -Nru ecflow-4.9.0/Viewer/src/ExpandState.hpp ecflow-4.11.1/Viewer/src/ExpandState.hpp --- ecflow-4.9.0/Viewer/src/ExpandState.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ExpandState.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef EXPANDSTATE_HPP_ -#define EXPANDSTATE_HPP_ - -#include -#include - -#include -#include - -//Represents the expand state of a full/part VNode tree. This tree has the same structure -//as a VNode tree and each VNode object is represented by a ExpandStateNode object. - -class TreeNodeModel; -class AbstractNodeView; -class QModelIndex; -class VNode; -class ExpandStateNode; - -class ExpandState -{ - friend class TreeNodeView; - friend class CompactNodeView; - -public: - ExpandState(AbstractNodeView*,TreeNodeModel*); - ~ExpandState(); - - void save(const VNode*); - void collectExpanded(const VNode* node,QSet&); - void saveExpandAll(const VNode* node); - void saveCollapseAll(const VNode* node); - void print() const; - bool isEmpty() const; - -protected: - void init(const VNode *vnode); - void clear(); - ExpandStateNode* root() const {return root_;} - void save(const VNode *,ExpandStateNode*,const QModelIndex&); - void collectExpanded(ExpandStateNode *expand,const VNode* node, - const QModelIndex& nodeIdx,QSet& theSet); - - bool needToExpandNewChild(ExpandStateNode* expandNode,const std::string&) const; - void collectParents(const std::string& fullPath,std::vector& parents) const; - - ExpandStateNode* find(const std::string& fullPath); - - AbstractNodeView* view_; - TreeNodeModel* model_; - ExpandStateNode* root_; -}; - -#endif - - - diff -Nru ecflow-4.9.0/Viewer/src/ExpandStateNode.cpp ecflow-4.11.1/Viewer/src/ExpandStateNode.cpp --- ecflow-4.9.0/Viewer/src/ExpandStateNode.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ExpandStateNode.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ExpandStateNode.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VNode.hpp" - -ExpandStateNode::ExpandStateNode(const VNode* node,bool expanded) : - expanded_(expanded), - expandedAll_(0), - collapsedAll_(0) -{ - name_=node->strName(); - reserveChildren(node->numOfChildren()); -} - -ExpandStateNode::~ExpandStateNode() -{ - clear(); -} - -//Clear the contents and create a new children vector -//with NULLs! -void ExpandStateNode::reset(const VNode* node,bool expanded) -{ - clear(); - name_=node->strName(); - expanded_=expanded; - expandedAll_=0; - collapsedAll_=0; - reserveChildren(node->numOfChildren()); -} - -void ExpandStateNode::clear() -{ - name_.clear(); - expanded_=0; - expandedAll_=0; - collapsedAll_=0; - for(unsigned int i=0; i < children_.size(); i++) - { - delete children_.at(i); - } - children_=std::vector(); -} - -//Create new children vector filled with NULLs -void ExpandStateNode::reserveChildren(std::size_t num) -{ - UI_ASSERT(children_.size() == 0,"children_.size()=" << children_.size()); - - ExpandStateNode *exn=0; - children_=std::vector(); - children_.resize(num,exn); -} - -//Allocate a new child and place it into then children vector at the specified position -ExpandStateNode* ExpandStateNode::setChildAt(std::size_t index,VNode* node,bool expanded) -{ - ExpandStateNode *exn=new ExpandStateNode(node,expanded); - children_[index]=exn; - return exn; -} - -//"Expand all children" was called on the node -void ExpandStateNode::setExpandedAll() -{ - expandedAll_=1; - collapsedAll_=0; - //Set the expand state on all the descendants - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - children_[i]->setExpandedRecursively(1); -} - -//"Collapse all children" was called on the node -void ExpandStateNode::setCollapsedAll() -{ - expandedAll_=0; - collapsedAll_=1; - //Set the expand state on all the descendants - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - children_[i]->setExpandedRecursively(0); -} - -void ExpandStateNode::setExpandedRecursively(unsigned int expanded) -{ - expanded_=expanded; - expandedAll_=0; - collapsedAll_=0; - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - { - children_[i]->setExpandedRecursively(expanded); - } -} - -//Find a descendant -ExpandStateNode* ExpandStateNode::find(const std::vector& pathVec) -{ - if(pathVec.size() == 0) - return this; - - if(pathVec.size() == 1) - { - return findChild(pathVec.at(0)); - } - - std::vector rest(pathVec.begin()+1,pathVec.end()); - ExpandStateNode*n = findChild(pathVec.at(0)); - - return n?n->find(rest):NULL; -} - -//Find a child with the given name -ExpandStateNode* ExpandStateNode::findChild(const std::string& theName) const -{ - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - { - //A child can be NULL temporarily - if(children_[i] && children_[i]->name_ == theName) - return children_[i]; - } - return 0; -} - -//Find a child with the given name. Returns its position as well. -ExpandStateNode* ExpandStateNode::findChild(const std::string& theName,std::size_t& pos) const -{ - pos=-1; - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - { - //A child can be NULL temporarily - if(children_[i] && children_[i]->name_ == theName) - { - pos=i; - return children_[i]; - } - } - return 0; -} - -void ExpandStateNode::print(std::string& indent,bool recursive) const -{ - UiLog().dbg() << indent << name_ << " " << expanded_; - if(recursive) - { - indent+=" "; - std::size_t num=children_.size(); - for(std::size_t i=0; i < num; i++) - children_[i]->print(indent,true); - - indent=indent.substr(0,indent.size()-2); - } -} - -//Adjust the contents of the given expand node to the contents of the vnode it -//represents. Return true if an adjustment was actually needed. -bool ExpandStateNode::adjustContents(const VNode* node) -{ - std::size_t numExpand=children_.size(); - std::size_t numNode=node->numOfChildren(); - - //Check if the children of the expand node and the vnode are the same. - //They migh differ when: - // -the order of the nodes changed - // -nodes were added/removed - bool same=false; - if(numExpand == numNode) - { - same=true; - for(std::size_t i=0; i < numNode; i++) - { - //items in children can be null pointers - if(!children_[i] || children_[i]->name_ != node->childAt(i)->strName()) - { - same=false; - break; - } - } - } - - //the children of the expand node and the vnode are not the same!!! - if(!same) - { - //create a new children vector for the expand node and - //copy all the expand children into it whose name appear in the - //vnode children - std::vector chVec; - for(std::size_t i=0; i < numNode; i++) - { - std::size_t chPos=-1; - if(ExpandStateNode* chExpand=findChild(node->childAt(i)->strName(),chPos)) - { - chVec.push_back(chExpand); - children_[chPos]=0; - } - else - chVec.push_back(0); - } - - //Delete the children vector of the expand node. It eaither contains NULLs or - //children objects which do not exist anymore! - for(std::size_t i=0; i < numExpand; i++) - { - if(children_[i] != 0) - delete children_[i]; - } - - children_.clear(); - - //reassign the children vector to the expand node - children_=chVec; - } - - return !same; -} - - - - diff -Nru ecflow-4.9.0/Viewer/src/ExpandStateNode.hpp ecflow-4.11.1/Viewer/src/ExpandStateNode.hpp --- ecflow-4.9.0/Viewer/src/ExpandStateNode.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ExpandStateNode.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,65 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef EXPANDSTATENODE_HPP -#define EXPANDSTATENODE_HPP - -#include -#include - -class VNode; - -//Store the expanded state of a node between major updates, server clears, -//status filter changes etc... Each VNode is represented by an ExpandStateNode! -//Note: for memory efficiency we do not store the parent!!! -class ExpandStateNode -{ - friend class ExpandState; - -public: - explicit ExpandStateNode(const VNode* node,bool); - ~ExpandStateNode(); - - void reset(const VNode* node,bool expanded); - void clear(); - ExpandStateNode* setChildAt(std::size_t index,VNode* node,bool - expanded); - void setExpanded(bool expanded) {expanded_=expanded;} - void setExpandedRecursively(unsigned int expanded); - void setExpandedAll(); - void setCollapsedAll(); - - ExpandStateNode* find(const std::vector& pathVec); - ExpandStateNode* findChild(const std::string& name) const; - ExpandStateNode* findChild(const std::string& name,std::size_t& pos) const; - bool adjustContents(const VNode* node); - void print(std::string& indent,bool recursive) const; - -protected: - void reserveChildren(std::size_t num); - - std::vector children_; - std::string name_; - - //Set if the node is expanded - unsigned int expanded_: 1; - - //Set if "Expand all children" was called on the node. - //Cleared when "collaps all children" is called on this node or - //on an ancestor - unsigned int expandedAll_ : 1; - - //Set if "Collaps all children" was called on the node - //Cleared when "expand all children" is called on this node or - //on an ancestor - unsigned int collapsedAll_ : 1; -}; - -#endif // EXPANDSTATENODE_HPP diff -Nru ecflow-4.9.0/Viewer/src/FileInfoLabel.cpp ecflow-4.11.1/Viewer/src/FileInfoLabel.cpp --- ecflow-4.9.0/Viewer/src/FileInfoLabel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FileInfoLabel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,243 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "FileInfoLabel.hpp" - -#include -#include - -#include "TextFormat.hpp" -#include "VFileInfo.hpp" -#include "VReply.hpp" - -FileInfoLabel::FileInfoLabel(QWidget* parent) : QLabel(parent) -{ - //Define id for the css - setProperty("fileInfo","1"); - setWordWrap(true); - - setMargin(2); - setAlignment(Qt::AlignLeft|Qt::AlignVCenter); - - //Other settings - setAutoFillBackground(true); - - setFrameShape(QFrame::StyledPanel); - setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); -} - -void FileInfoLabel::update(VReply* reply,QString extraText) -{ - if(!reply) - { - clear(); - return; - } - - QString labelText; - QString ttText; - QString s; - - QColor col(39,49,101); - QColor colText(30,30,30); - QColor colErr(255,0,0); - - QString fileName=QString::fromStdString(reply->fileName()); - - if(fileName.isEmpty()) - { - s=Viewer::formatBoldText("File: ",col) + Viewer::formatText(" ??? ",colErr); - setText(s); - setToolTip(QString()); - return; - } - - //Name - labelText=Viewer::formatBoldText("File: ",col); - labelText+=fileName; - - s=""; - - //Local read - if(reply->fileReadMode() == VReply::LocalReadMode) - { - VFile_ptr f=reply->tmpFile(); - if(f) - { - VFileInfo fInfo(QString::fromStdString(f->path())); - if(fInfo.exists()) - { - labelText+=Viewer::formatBoldText(" Size: ",col) + - formatFileSize(fInfo.formatSize(),fInfo.size()); - s+=Viewer::formatBoldText(" Modified: ",col.name()) + fInfo.formatModDate(); - - } - } - else - { - VFileInfo f(fileName); - if(f.exists()) - { - labelText+=Viewer::formatBoldText(" Size: ",col); - labelText+=formatFileSize(f.formatSize(),f.size()); - s+=Viewer::formatBoldText(" Modified: ",col) + f.formatModDate(); - } - } - - s+="
        "; - s+=Viewer::formatBoldText("Source: ",col) + " read from disk"; - - if(f) - { - s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); - } - - } - else if(reply->fileReadMode() == VReply::ServerReadMode) - { - VFile_ptr f=reply->tmpFile(); - if(f) - { - if(f->storageMode() == VFile::MemoryStorage) - { - labelText+=Viewer::formatBoldText(" Size: ",col); - labelText+=formatFileSize(VFileInfo::formatSize(f->dataSize()),f->dataSize()); - } - else - { - VFileInfo fInfo(QString::fromStdString(f->path())); - if(fInfo.exists()) - { - labelText+=Viewer::formatBoldText(" Size: ",col); - labelText+=formatFileSize(fInfo.formatSize(),fInfo.size()); - } - } - - s+="
        "; - s+=Viewer::formatBoldText("Source: ",col) + QString::fromStdString(f->fetchModeStr()); - s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); - - int rowLimit=f->truncatedTo(); - if(rowLimit >= 0) - { - s+=" (text truncated to last " + QString::number(rowLimit) + " lines)"; - } - } - else if(reply->status() == VReply::TaskDone) - { - s+="
        "; - s+=Viewer::formatBoldText("Source: ",col) + " fetched from server " + - Viewer::formatBoldText(" at ",col) + formatDate(QDateTime::currentDateTime()); - - int rowLimit=reply->readTruncatedTo(); - if(rowLimit >= 0) - { - s+=" (text truncated to last " + QString::number(rowLimit) + " lines)"; - } - } - else - { - s+="
        Fetch attempted from server" + Viewer::formatBoldText(" at ",col) + - formatDate(QDateTime::currentDateTime()); - } - } - - else if(reply->fileReadMode() == VReply::LogServerReadMode) - { - VFile_ptr f=reply->tmpFile(); - if(f) - { - //Path + size - if(f->storageMode() == VFile::MemoryStorage) - { - labelText+=Viewer::formatBoldText(" Size: ",col); - labelText+=formatFileSize(VFileInfo::formatSize(f->dataSize()),f->dataSize()); - } - else - { - VFileInfo fInfo(QString::fromStdString(f->path())); - if(fInfo.exists()) - { - labelText+=Viewer::formatBoldText(" Size: ",col); - labelText+=formatFileSize(fInfo.formatSize(),fInfo.size()); - } - } - - s+="
        "; - - //Source - s+=Viewer::formatBoldText("Source: ",col); - - if(f->cached()) - { - s+="[from cache] "; - } - s+=QString::fromStdString(f->fetchModeStr()); - s+=" (took " + QString::number(static_cast(f->transferDuration())/1000.,'f',1) + " s)"; - s+=Viewer::formatBoldText(" at ",col) + formatDate(f->fetchDate()); - } - } - - ttText=s; - labelText += ttText; - if(!extraText.isEmpty()) - { - labelText +=" " + extraText + ""; - } - - setText(labelText); -} - -QString FileInfoLabel::formatDate(QDateTime dt) const -{ - QColor col(34,107,138); - QString s=dt.toString("yyyy-MM-dd") + "  " +dt.toString("HH:mm:ss"); - return Viewer::formatBoldText(s,col); -} - -QString FileInfoLabel::formatFileSize(QString str,qint64 size) const -{ - if(size > 10*1024*1024) - return Viewer::formatText(str,QColor(Qt::red)); - return str; -} - -//============================================= -// -// DirInfoLabel -// -//============================================= - -void DirInfoLabel::update(VReply* reply) -{ - QDateTime dt; - if(reply) - { - std::vector dVec=reply->directories(); - if(dVec.empty()) - { - dt=QDateTime::currentDateTime(); - } - //take the last item - else - { - dt=dVec[dVec.size()-1]->fetchDate(); - } - } - else - { - dt=QDateTime::currentDateTime(); - } - - QColor col(39,49,101); - QString s="Directory listing updated at " + Viewer::formatBoldText(" at ",col) + - dt.toString("yyyy-MM-dd HH:mm:ss"); - setText(s); -} diff -Nru ecflow-4.9.0/Viewer/src/FileInfoLabel.hpp ecflow-4.11.1/Viewer/src/FileInfoLabel.hpp --- ecflow-4.9.0/Viewer/src/FileInfoLabel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FileInfoLabel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef FILEINFOLABEL_HPP_ -#define FILEINFOLABEL_HPP_ - -#include -#include - -#include "VDir.hpp" - -class VReply; - -class FileInfoLabel : public QLabel -{ -public: - explicit FileInfoLabel(QWidget* parent=0); - - void update(VReply*,QString str=QString()); - QString formatDate(QDateTime) const; - QString formatFileSize(QString,qint64 size) const; -}; - -class DirInfoLabel : public FileInfoLabel -{ -public: - explicit DirInfoLabel(QWidget* parent=0) : FileInfoLabel(parent) {} - - void update(VReply*); - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/FileWatcher.cpp ecflow-4.11.1/Viewer/src/FileWatcher.cpp --- ecflow-4.9.0/Viewer/src/FileWatcher.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FileWatcher.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "FileWatcher.hpp" - -FileWatcher::FileWatcher(const std::string& filePath,qint64 offset,QObject* parent) : - QFileSystemWatcher(parent), - offset_(offset) -{ - connect(this,SIGNAL(fileChanged(QString)), - this,SLOT(slotChanged(QString))); - - file_.setFileName(QString::fromStdString(filePath)); - if (!file_.open(QIODevice::ReadOnly | QIODevice::Text)) - return; - file_.seek(offset_); - - addPath(file_.fileName()); -} - -void FileWatcher::slotChanged(const QString& path) -{ - QStringList lst; - if(path == file_.fileName()) - { - while (!file_.atEnd()) - lst << file_.readLine(); - } - - Q_EMIT linesAppended(lst); - -} diff -Nru ecflow-4.9.0/Viewer/src/FileWatcher.hpp ecflow-4.11.1/Viewer/src/FileWatcher.hpp --- ecflow-4.9.0/Viewer/src/FileWatcher.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FileWatcher.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_FILEWATCHER_HPP_ -#define VIEWER_SRC_FILEWATCHER_HPP_ - -#include -#include -#include - -class FileWatcher : public QFileSystemWatcher -{ -Q_OBJECT - -public: - FileWatcher(const std::string& filePath,qint64 offset,QObject* parent); - -protected Q_SLOTS: - void slotChanged(const QString& path); - -Q_SIGNALS: - void linesAppended(QStringList); - -protected: - QFile file_; - qint64 offset_; -}; - - -#endif /* VIEWER_SRC_FILEWATCHER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/FilterWidget.cpp ecflow-4.11.1/Viewer/src/FilterWidget.cpp --- ecflow-4.9.0/Viewer/src/FilterWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,523 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "FilterWidget.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "VNState.hpp" -#include "VAttributeType.hpp" -#include "VConfig.hpp" -#include "VIcon.hpp" -#include "VFilter.hpp" - -#include "ServerItem.hpp" -#include "ServerFilter.hpp" - -//=========================================== -// -// VParamFilterMenu -// -//=========================================== - -VParamFilterMenu::VParamFilterMenu(QMenu * parent,VParamSet* filter,QString title,ItemMode itemMode,DecorMode decorMode) : - menu_(parent), - filter_(filter), - itemMode_(itemMode), - decorMode_(decorMode) -{ - buildTitle(title,parent); - - QAction* acSep = new QAction(this); - acSep->setSeparator(true); - menu_->addAction(acSep); - - //Param name must be unique - for(std::vector::const_iterator it=filter_->all().begin(); it != filter_->all().end(); ++it) - { - addAction((*it)->label(), - (*it)->name()); - } - - //For status - if(decorMode_ == ColourDecor) - { - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(0,1); - - int lighter=150; - bool useStateGrad=true; - if(VProperty* p=VConfig::instance()->find("view.common.node_gradient")) - { - useStateGrad=p->value().toBool(); - } - - QFont f; - QFontMetrics fm(f); - int pixSize=fm.height()-2; - - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator()) - { - if(VNState* vs=VNState::find(ac->data().toString().toStdString())) - { - //Fill rect - QColor bg=vs->colour(); - QColor bgLight=bg.lighter(lighter); - QColor border=bg.darker(125); - QBrush bgBrush; - if(useStateGrad) - { - grad.setColorAt(0,bgLight); - grad.setColorAt(1,bg); - bgBrush=QBrush(grad); - } - else - bgBrush=QBrush(bg); - - QPixmap pix(pixSize,pixSize); - QPainter painter(&pix); - - QRect fillRect(0,0,pixSize,pixSize); - painter.fillRect(fillRect,bgBrush); - painter.setPen(border); - painter.drawRect(fillRect.adjusted(0,0,-1,-1)); - ac->setIcon(pix); - } - } - } - } - - //For icons - else if(decorMode_ == PixmapDecor) - { - QFont f; - QFontMetrics fm(f); - int pixSize=fm.height()-2; - - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator()) - { - if(VIcon* vs=VIcon::find(ac->data().toString().toStdString())) - { - QPixmap pix(vs->pixmap(pixSize)); - ac->setIcon(pix); - } - } - } - } - - QAction *ac = new QAction(this); - ac->setSeparator(true); - menu_->addAction(ac); - - selectAllAc_ = new QAction(this); - if(itemMode_ == FilterMode) - selectAllAc_->setText(tr("Select all")); - else - selectAllAc_->setText(tr("Show all")); - menu_->addAction(selectAllAc_); - connect(selectAllAc_,SIGNAL(triggered(bool)), - this,SLOT(slotSelectAll(bool))); - - unselectAllAc_ = new QAction(this); - if(itemMode_ == FilterMode) - unselectAllAc_->setText(tr("Clear filter")); - else - unselectAllAc_->setText(tr("Hide all")); - menu_->addAction(unselectAllAc_); - connect(unselectAllAc_,SIGNAL(triggered(bool)), - this,SLOT(slotUnselectAll(bool))); - - reload(); -} - -void VParamFilterMenu::buildTitle(QString title,QMenu* parent) -{ - QLabel* titleLabel=new QLabel(title,menu_); - QFont f=menu_->font(); - f.setBold(true); - titleLabel->setFont(f); - titleLabel->setAlignment(Qt::AlignHCenter); - titleLabel->setAutoFillBackground(true); - QPalette pal=titleLabel->palette(); - pal.setColor(QPalette::Window,QColor(237,238,238)); - titleLabel->setPalette(pal); - - int titlePadding=3; - int topMargin=2; - if(parent && parent->isTearOffEnabled()) - { - titlePadding=1; - topMargin=0; - } - - QString titleQss="QLabel {padding: " + QString::number(titlePadding) + "px;}"; - titleLabel->setStyleSheet(titleQss); - - QWidget *w=new QWidget(menu_); - QVBoxLayout *vb=new QVBoxLayout(w); - vb->setContentsMargins(2,topMargin,2,2); - //vb->addSpacing(2); - vb->addWidget(titleLabel); - //vb->addSpacing(2); - - QWidgetAction *titleAc = new QWidgetAction(menu_); - //Qt doc says: the ownership of the widget is passed to the widgetaction. - //So when the action is deleted it will be deleted as well. - titleAc->setDefaultWidget(w); - //titleAc->setEnabled(false); - menu_->addAction(titleAc); -} - - -void VParamFilterMenu::addAction(QString name,QString id) -{ - QAction *ac = new QAction(this); - ac->setText(name); - ac->setData(id); - ac->setCheckable(true); - ac->setChecked(false); - - menu_->addAction(ac); - - //It will not be emitted when setChecked is called! - connect(ac,SIGNAL(triggered(bool)), - this,SLOT(slotChanged(bool))); -} - -void VParamFilterMenu::slotSelectAll(bool) -{ - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator() && - ac->isCheckable()) - { - ac->setChecked(true); - } - } - - slotChanged(true); -} - -void VParamFilterMenu::slotUnselectAll(bool) -{ - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator() && - ac->isCheckable()) - { - ac->setChecked(false); - } - } - - slotChanged(true); -} - -void VParamFilterMenu::slotChanged(bool) -{ - std::vector items; - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator() && - ac->isCheckable() && ac->isChecked()) - { - items.push_back(ac->data().toString().toStdString()); - } - } - - if(filter_) - filter_->setCurrent(items); - - checkActionState(); -} - -void VParamFilterMenu::reload() -{ - Q_FOREACH(QAction* ac,menu_->actions()) - { - if(!ac->isSeparator()) - { - ac->setChecked(filter_->isSet(ac->data().toString().toStdString())); - } - } - checkActionState(); -} - -void VParamFilterMenu::checkActionState() -{ - if(filter_) - { - selectAllAc_->setEnabled(!filter_->isComplete()); - unselectAllAc_->setEnabled(!filter_->isEmpty()); - } -} - -//=========================================== -// -// ServerFilterMenu -// -//=========================================== - -ServerFilterMenu::ServerFilterMenu(QMenu * parent) : - menu_(parent), - filter_(NULL) -{ - loadFont_.setBold(true); - - allMenu_=new QMenu("All servers",menu_); - menu_->addMenu(allMenu_); - - QAction* acFavSep = new QAction(this); - acFavSep->setSeparator(true); - menu_->addAction(acFavSep); - - QAction* acFavTitle = new QAction(this); - acFavTitle->setText(tr("Favourite or loaded servers")); - QFont f=acFavTitle->font(); - f.setBold(true); - acFavTitle->setFont(f); - - menu_->addAction(acFavTitle); - - init(); - - ServerList::instance()->addObserver(this); -} - -ServerFilterMenu::~ServerFilterMenu() -{ - ServerList::instance()->removeObserver(this); - if(filter_) - filter_->removeObserver(this); -} - -void ServerFilterMenu::aboutToDestroy() -{ - ServerList::instance()->removeObserver(this); - if(filter_) - filter_->removeObserver(this); - - clear(); -} - -void ServerFilterMenu::clear() -{ - Q_FOREACH(QAction* ac,acAllMap_) - { - delete ac; - } - acAllMap_.clear(); - - clearFavourite(); -} - -void ServerFilterMenu::clearFavourite() -{ - Q_FOREACH(QAction* ac,acFavMap_) - { - delete ac; - } - acFavMap_.clear(); -} - -void ServerFilterMenu::init() -{ - clear(); - - for(int i=0; i < ServerList::instance()->count(); i++) - { - ServerItem* item=ServerList::instance()->itemAt(i); - QString name=QString::fromStdString(item->name()); - QAction *ac=createAction(name,i); - acAllMap_[name]=ac; - } - - Q_FOREACH(QAction *ac,acAllMap_) - { - allMenu_->addAction(ac); - } - - buildFavourite(); -} - -void ServerFilterMenu::buildFavourite() -{ - clearFavourite(); - - for(int i=0; i < ServerList::instance()->count(); i++) - { - ServerItem* item=ServerList::instance()->itemAt(i); - if(item->isFavourite() || (filter_ && filter_->isFiltered(item))) - { - QString name=QString::fromStdString(item->name()); - acFavMap_[name]=createAction(name,i); - } - } - - Q_FOREACH(QAction *ac,acFavMap_) - { - menu_->addAction(ac); - } - -} - -QAction* ServerFilterMenu::createAction(QString name,int id) -{ - QAction *ac = new QAction(this); - ac->setText(name); - ac->setData(id); - ac->setCheckable(true); - ac->setChecked(false); - - //It will not be emitted when setChecked is called!! - connect(ac,SIGNAL(triggered(bool)), - this,SLOT(slotChanged(bool))); - - return ac; -} - - -void ServerFilterMenu::slotChanged(bool) -{ - if(!filter_) - return; - - if(QAction *ac=static_cast(sender())) - { - if(ac->isSeparator()) return; - - if(ServerItem *item=ServerList::instance()->itemAt(ac->data().toInt())) - { - QString name=ac->text(); - bool checked=ac->isChecked(); - if(checked) - filter_->addServer(item); - else - filter_->removeServer(item); - - //At this point the action (ac) might be deleted so - //we need to use the name and check state for syncing - syncActionState(name,checked); - } - } -} - -void ServerFilterMenu::syncActionState(QString name,bool checked) -{ - QMap::const_iterator it = acAllMap_.find(name); - if(it != acAllMap_.end() && it.value()->isChecked() != checked) - { - //Triggered() will not be called!! - it.value()->setChecked(checked); - } - - it = acFavMap_.find(name); - if(it != acFavMap_.end() && it.value()->isChecked() != checked) - { - //Triggered() will not be called!! - it.value()->setChecked(checked); - } -} - -//Reset actions state when a new filter is loaded -void ServerFilterMenu::reload(ServerFilter *filter) -{ - if(filter_) - filter_->removeObserver(this); - - filter_=filter; - - if(filter_) - filter_->addObserver(this); - - reload(); -} - -//Reset actions state when a new filter is loaded -void ServerFilterMenu::reload() -{ - buildFavourite(); - - QMap::const_iterator it=acAllMap_.constBegin(); - while(it != acAllMap_.constEnd()) - { - if(ServerItem *item=ServerList::instance()->find(it.value()->text().toStdString())) - { - bool current=it.value()->isChecked(); - if(current != filter_->isFiltered(item)) - { - //Triggered() will not be called!! - it.value()->setChecked(!current); - it.value()->setFont(current?font_:loadFont_); - } - } - ++it; - } - - it=acFavMap_.constBegin(); - while(it != acFavMap_.constEnd()) - { - if(ServerItem *item=ServerList::instance()->find(it.value()->text().toStdString())) - { - bool current=it.value()->isChecked(); - if(current != filter_->isFiltered(item)) - { - //Triggered() will not be called!! - it.value()->setChecked(!current); - it.value()->setFont(current?font_:loadFont_); - } - } - ++it; - } -} - -void ServerFilterMenu::notifyServerListChanged() -{ - init(); - reload(); -} - -void ServerFilterMenu::notifyServerListFavouriteChanged(ServerItem* item) -{ - reload(); -} - -void ServerFilterMenu::notifyServerFilterAdded(ServerItem*) -{ - reload(); -} - -void ServerFilterMenu::notifyServerFilterRemoved(ServerItem*) -{ - reload(); -} - -void ServerFilterMenu::notifyServerFilterChanged(ServerItem*) -{ - reload(); -} - -void ServerFilterMenu::notifyServerFilterDelete() -{ - reload(0); -} diff -Nru ecflow-4.9.0/Viewer/src/FilterWidget.hpp ecflow-4.11.1/Viewer/src/FilterWidget.hpp --- ecflow-4.9.0/Viewer/src/FilterWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef FILTERWIDGET_HPP_ -#define FILTERWIDGET_HPP_ - -#include -#include -#include -#include - -#include "DState.hpp" -#include "ServerFilter.hpp" -#include "ServerList.hpp" - -class QToolButton; -class VParam; -class VParamSet; -class ServerFilter; - - -class VParamFilterMenu : public QObject -{ -Q_OBJECT - -public: - enum DecorMode {NoDecor,ColourDecor,PixmapDecor}; - enum ItemMode {FilterMode,ShowMode}; - - VParamFilterMenu(QMenu* parent,VParamSet* filter,QString title,ItemMode,DecorMode decorMode=NoDecor); - void reload(); - -protected Q_SLOTS: - void slotChanged(bool); - void slotSelectAll(bool); - void slotUnselectAll(bool); - -protected: - void buildTitle(QString,QMenu*); - void addAction(QString name,QString id); - void checkActionState(); - - QMenu* menu_; - VParamSet* filter_; - ItemMode itemMode_; - DecorMode decorMode_; - QAction *selectAllAc_; - QAction *unselectAllAc_; -}; - - -class ServerFilterMenu : public QObject, public ServerListObserver, public ServerFilterObserver -{ -Q_OBJECT - -public: - explicit ServerFilterMenu(QMenu* parent); - ~ServerFilterMenu(); - - void reload(ServerFilter*); - void aboutToDestroy(); //Called when the parent mainwindow is being destroyed - - //From ServerListObserver - void notifyServerListChanged(); - void notifyServerListFavouriteChanged(ServerItem*); - - //From ConfigObserver - void notifyServerFilterAdded(ServerItem*); - void notifyServerFilterRemoved(ServerItem*); - void notifyServerFilterChanged(ServerItem*); - void notifyServerFilterDelete(); - -protected Q_SLOTS: - void slotChanged(bool); - -protected: - void init(); - void clear(); - QAction* createAction(QString name,int id); - void reload(); - void buildFavourite(); - void clearFavourite(); - void syncActionState(QString,bool); - - QMenu* menu_; - QMenu* allMenu_; - QMap acAllMap_; - QMap acFavMap_; - ServerFilter* filter_; - QFont font_; - QFont loadFont_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/FlagSet.hpp ecflow-4.11.1/Viewer/src/FlagSet.hpp --- ecflow-4.9.0/Viewer/src/FlagSet.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FlagSet.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef FLAGSET_HPP_ -#define FLAGSET_HPP_ - -template -class FlagSet -{ -public: - FlagSet() : flags_(0) {} - FlagSet(T t) : flags_(0) {set(t);} - - void clear() {flags_=0;} - void set(T flag ) { flags_ |= (1 << flag); } - void unset(T flag ) { flags_ &= ~ (1 << flag); } - bool isSet(T flag) const { return (flags_ >> flag) & 1; } - bool isEmpty() const {return flags_==0;} - bool sameAs(T flag) const {return flags_ == flag;} - -private: - int flags_; - -}; - -#endif // FLAGSET_HPP_ diff -Nru ecflow-4.9.0/Viewer/src/FontMetrics.cpp ecflow-4.11.1/Viewer/src/FontMetrics.cpp --- ecflow-4.9.0/Viewer/src/FontMetrics.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FontMetrics.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "FontMetrics.hpp" - -#include -#include - -FontMetrics::FontMetrics(const QFont &font) : - QFontMetrics(font), - realHeight_(height()), - topPadding_(0), - bottomPadding_(0) -{ - computeRealHeight(font); -} - -void FontMetrics::computeRealHeight(QFont f) -{ - QFontMetrics fm(f); - QString txt="Ayfgl"; - QImage img(fm.width(txt)+6,fm.height(),QImage::Format_ARGB32_Premultiplied); - img.fill(Qt::white); - QPainter p(&img); - p.setPen(Qt::black); - f.setBold(true); - p.setFont(f); - p.drawText(QRect(0,0,img.width(),img.height()),Qt::AlignCenter,txt); - - int minRow=img.height()+100; - int maxRow=-1; - for(int i=0; i < img.height(); i++) - for(int j=0; j < img.width(); j++) - { - QRgb c=img.pixel(j,i); - if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) - { - if(i > maxRow) - maxRow=i; - if(i < minRow) - minRow=i; - } - } - - if(minRow >=0 && maxRow < img.height()) - { - realHeight_=maxRow-minRow+1; - topPadding_=minRow; - bottomPadding_=img.height()-1-maxRow; - } -} diff -Nru ecflow-4.9.0/Viewer/src/FontMetrics.hpp ecflow-4.11.1/Viewer/src/FontMetrics.hpp --- ecflow-4.9.0/Viewer/src/FontMetrics.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/FontMetrics.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef FONTMETRICS_HPP -#define FONTMETRICS_HPP - -#include - -class FontMetrics : public QFontMetrics -{ -public: - FontMetrics(const QFont &font); - int realHeight() const {return realHeight_;} - int topPaddingForCentre() const {return topPadding_;} - int bottomPaddingForCentre() const {return bottomPadding_;} - -protected: - int realHeight_; - int topPadding_; - int bottomPadding_; - -private: - void computeRealHeight(QFont f); -}; - - -#endif // FONTMETRICS_HPP diff -Nru ecflow-4.9.0/Viewer/src/GotoLineDialog.cpp ecflow-4.11.1/Viewer/src/GotoLineDialog.cpp --- ecflow-4.9.0/Viewer/src/GotoLineDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/GotoLineDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "GotoLineDialog.hpp" - -#include - -GotoLineDialog::GotoLineDialog(QWidget *parent) : QDialog(parent) -{ - setupUi(this); // this sets up GUI// setupFileMenu(); - - - connect (buttonBox, SIGNAL(accepted()), this, SLOT(done())); - connect (buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - connect (lineEdit, SIGNAL(textChanged(const QString &)), this, SLOT(setButtonStatus())); -} - - -GotoLineDialog::~GotoLineDialog() -{ - -} - - -// --------------------------------------------------------------------------- -// GotoLineDialog::setButtonStatus -// if there is text in the input box, then we can activate the 'OK' button, -// otherwise we should disable it. This function is called each time the text -// in the box is changed. -// --------------------------------------------------------------------------- - -void GotoLineDialog::setButtonStatus() -{ - QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); - - if (lineEdit->text().isEmpty()) - { - okButton->setEnabled(false); - } - else - { - okButton->setEnabled(true); - } -} - - -// --------------------------------------------------------------------------- -// GotoLineDialog::setupUIBeforeShow -// sets up UI elements before the dialog is displayed. -// --------------------------------------------------------------------------- - -void GotoLineDialog::setupUIBeforeShow() -{ - lineEdit->setFocus(Qt::OtherFocusReason); - buttonBox->button(QDialogButtonBox::Ok)->setDefault(true); - - setButtonStatus(); -} - - -// --------------------------------------------------------------------------- -// GotoLineDialog::accept -// called when the user clicks the 'OK' button - emits a signal to tell the -// text editor to go to the chosen line -// --------------------------------------------------------------------------- - -void GotoLineDialog::done() -{ - int line = lineEdit->text().toInt(); - Q_EMIT gotoLine(line); - close(); -} diff -Nru ecflow-4.9.0/Viewer/src/GotoLineDialog.hpp ecflow-4.11.1/Viewer/src/GotoLineDialog.hpp --- ecflow-4.9.0/Viewer/src/GotoLineDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/GotoLineDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef GotoLineDialog_H -#define GotoLineDialog_H - -#include "ui_GotoLineDialog.h" - -using namespace std; - - -class GotoLineDialog : public QDialog, private Ui::GotoLineDialogQ -{ - Q_OBJECT - -public: - explicit GotoLineDialog(QWidget *parent = 0); - ~GotoLineDialog(); - void setupUIBeforeShow(); - -Q_SIGNALS: - void gotoLine(int line); // emitted when the user says 'ok' - - -public Q_SLOTS: - void done(); - void setButtonStatus(); - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/GotoLineDialog.ui ecflow-4.11.1/Viewer/src/GotoLineDialog.ui --- ecflow-4.9.0/Viewer/src/GotoLineDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/GotoLineDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ - - - GotoLineDialogQ - - - - 0 - 0 - 186 - 113 - - - - Dialog - - - - - 10 - 20 - 162 - 79 - - - - - - - - - Goto Line: - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - buttonBox - lineEdit - label - label - - - - - buttonBox - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/Highlighter.cpp ecflow-4.11.1/Viewer/src/Highlighter.cpp --- ecflow-4.9.0/Viewer/src/Highlighter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Highlighter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,171 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "Highlighter.hpp" -#include "UserMessage.hpp" -#include "VParam.hpp" - -#include -#include -#include -#include - -#include -#include - -std::string Highlighter::parFile_; - -#include "VProperty.hpp" - - -Highlighter::Highlighter(QTextDocument *parent,QString id) - : QSyntaxHighlighter(parent) -{ - load(id); -} - -void Highlighter::addRule(QString pattern,QTextCharFormat format) -{ - HighlightingRule rule; - rule.pattern = QRegExp(pattern,Qt::CaseSensitive); - rule.format = format; - rules_.append(rule); -} - -void Highlighter::highlightBlock(const QString &text) -{ - Q_FOREACH(HighlightingRule rule, rules_) - { - QRegExp expression(rule.pattern); - int index = text.indexOf(expression); - while (index >= 0) - { - int length = expression.matchedLength(); - setFormat(index, length, rule.format); - index = text.indexOf(expression, index + length); - } - } - setCurrentBlockState(0); -} - -void Highlighter::init(const std::string& parFile) -{ - parFile_=parFile; -} - -void Highlighter::load(QString id) -{ - //Parse param file using the boost JSON property tree parser - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(parFile_,pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, - std::string("Error! Highlighter::load() unable to parse definition file: " + parFile_ + " Message: " +errorMessage)); - return; - } - - ptree::const_assoc_iterator itTop=pt.find(id.toStdString()); - if(itTop == pt.not_found()) - { - return; - } - - //For each parameter - for(ptree::const_iterator itRule = itTop->second.begin(); itRule != itTop->second.end(); ++itRule) - { - QString pattern; - QTextCharFormat format; - - ptree::const_assoc_iterator itPar; - ptree ptPar=itRule->second; - - if((itPar=ptPar.find("pattern")) !=ptPar.not_found()) - { - pattern=QString::fromStdString(itPar->second.get_value()); - } - if((itPar=ptPar.find("colour")) !=ptPar.not_found()) - { - format.setForeground(VProperty::toColour(itPar->second.get_value())); - } - if((itPar=ptPar.find("bold")) !=ptPar.not_found()) - { - if(itPar->second.get_value() == "true") - format.setFontWeight(QFont::Bold); - } - if((itPar=ptPar.find("italic")) !=ptPar.not_found()) - { - if(itPar->second.get_value() == "true") - format.setFontItalic(true); - } - addRule(pattern,format); - } -} - -void Highlighter::toHtml(QString& html) -{ - // Create a new document from all the selected text document. - QTextCursor cursor(document()); - cursor.select(QTextCursor::Document); - - QTextDocument* tmpDoc(new QTextDocument()); - Q_ASSERT(tmpDoc); - QTextCursor tmpCursor(tmpDoc); - tmpCursor.insertFragment(cursor.selection()); - tmpCursor.select(QTextCursor::Document); - - // Set the default foreground for the inserted characters. - //QTextCharFormat textfmt = tmpCursor.charFormat(); - //textfmt.setForeground(Qt::black); - //tmpCursor.setCharFormat(textfmt); - - // Apply the additional formats set by the syntax highlighter - QTextBlock start = document()->findBlock(cursor.selectionStart()); - QTextBlock end = document()->findBlock(cursor.selectionEnd()); - end = end.next(); - - const int selectionStart = cursor.selectionStart(); - const int endOfDocument = tmpDoc->characterCount() - 1; - for(QTextBlock current = start; current.isValid() and current not_eq end; current = current.next()) - { - const QTextLayout* layout(current.layout()); - - Q_FOREACH(const QTextLayout::FormatRange &range, layout->additionalFormats()) - { - const int start = current.position() + range.start - selectionStart; - const int end = start + range.length; - if(end <= 0 or start >= endOfDocument) - continue; - tmpCursor.setPosition(qMax(start, 0)); - tmpCursor.setPosition(qMin(end, endOfDocument), QTextCursor::KeepAnchor); - tmpCursor.setCharFormat(range.format); - } - } - - // Reset the user states since they are not interesting - for(QTextBlock block = tmpDoc->begin(); block.isValid(); block = block.next()) - block.setUserState(-1); - - // Make sure the text appears pre-formatted, and set the background we want. - tmpCursor.select(QTextCursor::Document); - QTextBlockFormat blockFormat = tmpCursor.blockFormat(); - blockFormat.setNonBreakableLines(true); - blockFormat.setBackground(Qt::black); - tmpCursor.setBlockFormat(blockFormat); - - // Finally retreive the syntax higlighted and formatted html. - html = tmpCursor.selection().toHtml(); - delete tmpDoc; -} diff -Nru ecflow-4.9.0/Viewer/src/Highlighter.hpp ecflow-4.11.1/Viewer/src/Highlighter.hpp --- ecflow-4.9.0/Viewer/src/Highlighter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Highlighter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef HIGHLIGHTER_HPP_ -#define HIGHLIGHTER_HPP_ - -#include -#include - -class Highlighter : public QSyntaxHighlighter -{ -public: - Highlighter(QTextDocument *parent,QString id); - static void init(const std::string& parFile); - void toHtml(QString& html); - -protected: - void highlightBlock(const QString &text); - void addRule(QString,QTextCharFormat); - -private: - void load(QString); - - struct HighlightingRule - { - QRegExp pattern; - QTextCharFormat format; - }; - - QList rules_; - static std::string parFile_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/HistoryItemWidget.cpp ecflow-4.11.1/Viewer/src/HistoryItemWidget.cpp --- ecflow-4.9.0/Viewer/src/HistoryItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HistoryItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "HistoryItemWidget.hpp" - -#include -#include -#include - -#include "LogProvider.hpp" -#include "LogModel.hpp" -#include "VReply.hpp" - -static bool firstRun=true; - -HistoryItemWidget::HistoryItemWidget(QWidget *parent) : QWidget(parent) -{ - setupUi(this); - - searchTb_->setEnabled(false); - searchTb_->setVisible(false); - searchLine_->setVisible(false); - - infoProvider_=new LogProvider(this); - infoProvider_->setAutoUpdate(true); - - model_=new LogModel(this); - - treeView_->setProperty("log","1"); - treeView_->setModel(model_); - treeView_->setItemDelegate(new LogDelegate(this)); - treeView_->setContextMenuPolicy(Qt::ActionsContextMenu); - - checkActionState(); - - //Define context menu - treeView_->addAction(actionCopyEntry_); - treeView_->addAction(actionCopyRow_); -} - -QWidget* HistoryItemWidget::realWidget() -{ - return this; -} - -void HistoryItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - info_=info; - - if(info_) - { - infoProvider_->info(info_); - } -} - -void HistoryItemWidget::clearContents() -{ - InfoPanelItem::clear(); - model_->clearData(); -} - -void HistoryItemWidget::infoReady(VReply* reply) -{ - model_->setData(reply->text()); - adjustColumnSize(); - fileLabel_->update(reply,"(Last 100 lines)"); - treeView_->scrollTo(model_->lastIndex()); - checkActionState(); -} - -void HistoryItemWidget::infoProgress(VReply* reply) -{ - QString s=QString::fromStdString(reply->text()); -} - -void HistoryItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); - - checkActionState(); -} - -void HistoryItemWidget::infoAppended(VReply* reply) -{ - model_->appendData(reply->textVec()); - adjustColumnSize(); - treeView_->scrollTo(model_->lastIndex()); - - checkActionState(); -} - -void HistoryItemWidget::updateState(const ChangeFlags& flags) -{ - if(flags.isSet(SelectedChanged)) - { - if(!selected_) - { - infoProvider_->setAutoUpdate(false); - reloadTb_->setEnabled(false); - return; - } - } - - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - infoProvider_->setAutoUpdate(false); - reloadTb_->setEnabled(false); - return; - } - - } - - checkActionState(); -} - -void HistoryItemWidget::checkActionState() -{ - if(suspended_) - return; - - if(infoProvider_->autoUpdate() == frozen_) - { - infoProvider_->setAutoUpdate(!frozen_); - } - reloadTb_->setEnabled(!infoProvider_->autoUpdate() || !infoProvider_->inAutoUpdate()); -} - -//Adjust column size if it is the first run -void HistoryItemWidget::adjustColumnSize() -{ - if(firstRun && model_->hasData()) - { - firstRun=false; - for(int i=0; i < model_->columnCount()-1; i++) - { - treeView_->resizeColumnToContents(i); - } - } -} - -void HistoryItemWidget::on_reloadTb__clicked(bool) -{ - if(info_ && info_.get()) - { - infoProvider_->info(info_); - } -} - -void HistoryItemWidget::on_actionCopyEntry__triggered() -{ - toClipboard(model_->entryText(treeView_->currentIndex())); -} - -void HistoryItemWidget::on_actionCopyRow__triggered() -{ - toClipboard(model_->fullText(treeView_->currentIndex())); -} - -void HistoryItemWidget::toClipboard(QString txt) const -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QClipboard* cb=QGuiApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#else - QClipboard* cb=QApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#endif -} - -static InfoPanelItemMaker maker1("history"); diff -Nru ecflow-4.9.0/Viewer/src/HistoryItemWidget.hpp ecflow-4.11.1/Viewer/src/HistoryItemWidget.hpp --- ecflow-4.9.0/Viewer/src/HistoryItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HistoryItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef HISTORYITEMWIDGET_HPP_ -#define HISTORYITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" -#include "VInfo.hpp" - -#include "ServerHandler.hpp" - -#include "ui_HistoryItemWidget.h" - -class LogModel; - -class HistoryItemWidget : public QWidget, public InfoPanelItem, protected Ui::HistoryItemWidget -{ -Q_OBJECT - -public: - explicit HistoryItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - void infoAppended(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected Q_SLOTS: - void on_reloadTb__clicked(bool); - void on_actionCopyEntry__triggered(); - void on_actionCopyRow__triggered(); - -protected: - void updateState(const ChangeFlags&); - void adjustColumnSize(); - void checkActionState(); - void toClipboard(QString txt) const; - - LogModel* model_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/HistoryItemWidget.ui ecflow-4.11.1/Viewer/src/HistoryItemWidget.ui --- ecflow-4.9.0/Viewer/src/HistoryItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HistoryItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ - - - HistoryItemWidget - - - - 0 - 0 - 510 - 465 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show search bar - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - Refresh log - - - ... - - - - :/viewer/sync.svg:/viewer/sync.svg - - - true - - - - - - - - - - - - QAbstractItemView::SingleSelection - - - false - - - true - - - true - - - - - - - - - - Copy text of &Entry - - - Copy text of the log entry - - - Ctrl+C - - - - - Copy text of full &row - - - Copy text of full row - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - PlainTextSearchLine - QWidget -
        PlainTextSearchLine.hpp
        - 1 -
        - - TreeView - QTreeView -
        TreeView.hpp
        -
        - - FileInfoLabel - QLabel -
        FileInfoLabel.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/HtmlEdit.cpp ecflow-4.11.1/Viewer/src/HtmlEdit.cpp --- ecflow-4.9.0/Viewer/src/HtmlEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HtmlEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,80 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "HtmlEdit.hpp" - -#include -#include - -#include "UiLog.hpp" - -HtmlEdit::HtmlEdit(QWidget * parent) : - QTextBrowser(parent), - fontProp_(0) -{ -#if 0 - QFont f("Courier"); - //QFont f("Monospace"); - //f.setStyleHint(QFont::TypeWriter); - f.setFixedPitch(true); - f.setPointSize(10); - //f.setStyleStrategy(QFont::PreferAntialias); - setFont(f); -#endif -} - -HtmlEdit::~HtmlEdit() -{ - if(fontProp_) - fontProp_->removeObserver(this); -} - -//--------------------------------------------- -// Fontsize management -//--------------------------------------------- - -void HtmlEdit::setFontProperty(VProperty* p) -{ - fontProp_=p; - fontProp_->addObserver(this); - updateFont(); -} - -void HtmlEdit::wheelEvent(QWheelEvent *event) -{ - int fps=font().pointSize(); - - QTextBrowser::wheelEvent(event); - if(font().pointSize() != fps) - fontSizeChangedByZoom(); -} - -void HtmlEdit::fontSizeChangedByZoom() -{ - if(fontProp_) - fontProp_->setValue(font()); -} - -void HtmlEdit::updateFont() -{ - if(fontProp_) - { - QFont f=fontProp_->value().value(); - if(font() != f) - setFont(f); - } -} - -void HtmlEdit::notifyChange(VProperty* p) -{ - if(fontProp_ ==p) - { - setFont(p->value().value()); - } -} diff -Nru ecflow-4.9.0/Viewer/src/HtmlEdit.hpp ecflow-4.11.1/Viewer/src/HtmlEdit.hpp --- ecflow-4.9.0/Viewer/src/HtmlEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HtmlEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef HTMLEDIT_HPP -#define HTMLEDIT_HPP - -#include - -#include "VProperty.hpp" - -class HtmlEdit : public QTextBrowser, public VPropertyObserver -{ -public: - explicit HtmlEdit(QWidget* parent = 0); - ~HtmlEdit(); - - void setFontProperty(VProperty* p); - void updateFont(); - void notifyChange(VProperty* p); - -protected: - void wheelEvent(QWheelEvent *event); - -private: - void fontSizeChangedByZoom(); - - VProperty *fontProp_; -}; - -#endif // HTMLEDIT_HPP - - diff -Nru ecflow-4.9.0/Viewer/src/HtmlItemWidget.cpp ecflow-4.11.1/Viewer/src/HtmlItemWidget.cpp --- ecflow-4.9.0/Viewer/src/HtmlItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HtmlItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "HtmlItemWidget.hpp" - -#include -#include - -HtmlItemWidget::HtmlItemWidget(QWidget *parent) : - QWidget(parent) -{ - setupUi(this); - - externalTb_->hide(); - - fileLabel_->setProperty("fileInfo","1"); - - searchLine_->setEditor(textEdit_); - searchLine_->setVisible(false); - - textEdit_->setOpenExternalLinks(false); - textEdit_->setOpenLinks(false); - textEdit_->setReadOnly(true); -} - -HtmlItemWidget::~HtmlItemWidget() -{ -} - -void HtmlItemWidget::removeSpacer() -{ - //Remove the first spacer item!! - for(int i=0; horizontalLayout->count(); i++) - { - if(QSpacerItem* sp=horizontalLayout->itemAt(i)->spacerItem()) - { - horizontalLayout->takeAt(i); - delete sp; - break; - } - } -} - -void HtmlItemWidget::on_searchTb__clicked() -{ - searchLine_->setVisible(true); - searchLine_->setFocus(); - searchLine_->selectAll(); -} - - -void HtmlItemWidget::on_fontSizeUpTb__clicked() -{ - textEdit_->slotZoomIn(); -} - -void HtmlItemWidget::on_fontSizeDownTb__clicked() -{ - textEdit_->slotZoomOut(); -} diff -Nru ecflow-4.9.0/Viewer/src/HtmlItemWidget.hpp ecflow-4.11.1/Viewer/src/HtmlItemWidget.hpp --- ecflow-4.9.0/Viewer/src/HtmlItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HtmlItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,38 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef HTMLITEMWIDGET_HPP -#define HTMLITEMWIDGET_HPP - -#include - -#include "ui_HtmlItemWidget.h" - -class HtmlItemWidget : public QWidget, protected Ui::HtmlItemWidget -{ -Q_OBJECT - -public: - explicit HtmlItemWidget(QWidget *parent=0); - ~HtmlItemWidget(); - -protected Q_SLOTS: - void on_searchTb__clicked(); - void on_fontSizeUpTb__clicked(); - void on_fontSizeDownTb__clicked(); - -Q_SIGNALS: - void editorFontSizeChanged(); - -protected: - void removeSpacer(); -}; - -#endif // HTMLITEMWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/src/HtmlItemWidget.ui ecflow-4.11.1/Viewer/src/HtmlItemWidget.ui --- ecflow-4.9.0/Viewer/src/HtmlItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/HtmlItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,180 +0,0 @@ - - - HtmlItemWidget - - - - 0 - 0 - 510 - 465 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - false - - - QFrame::StyledPanel - - - - - - 2 - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg - - - Ctrl++ - - - true - - - - - - - Decrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg - - - Ctrl+- - - - true - - - - - - - ... - - - - - - - Show search bar (CTRL-F) - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - - - - - - - - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - FileInfoLabel - QLabel -
        FileInfoLabel.hpp
        -
        - - RichTextSearchLine - QWidget -
        RichTextSearchLine.hpp
        - 1 -
        - - RichTextEdit - QTextBrowser -
        RichTextEdit.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/IconProvider.cpp ecflow-4.11.1/Viewer/src/IconProvider.cpp --- ecflow-4.9.0/Viewer/src/IconProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/IconProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,168 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "IconProvider.hpp" - -#include -#include -#include -#include - -#include "UiLog.hpp" - -static UnknownIconItem unknownIcon(":/desktop/unknown.svg"); -static IconItem linkIcon(":/desktop/link.svg"); -static IconItem linkBrokenIcon(":/desktop/link_broken.svg"); -static IconItem lockIcon(":/viewer/padlock.svg"); -static IconItem warningIcon(":/viewer/warning.svg"); -static IconItem errorIcon(":/viewer/error.svg"); -static IconItem bookmarkGroupIcon(":/desktop/bookmark_group.svg"); -static IconItem embeddedIcon(":/desktop/embedded.svg"); -static IconItem infoIcon(":/viewer/info.svg"); - -std::map IconProvider::icons_; -std::map IconProvider::iconsById_; - -static int idCnt=0; - -//=========================================== -// -// IconItem -// -//=========================================== - -IconItem::IconItem(QString path) : path_(path), id_(idCnt++) -{ -} - -QPixmap IconItem::pixmap(int size) -{ - std::map::iterator it=pixmaps_.find(size); - if(it != pixmaps_.end()) - return it->second; - else - { - QPixmap pix; - QImageReader imgR(path_); - if(imgR.canRead()) - { - imgR.setScaledSize(QSize(size,size)); - QImage img=imgR.read(); - pix=QPixmap::fromImage(img); - } - else - { - pix=unknown(size); - } - - pixmaps_[size]=pix; - return pix; - } - return QPixmap(); -} - -QPixmap IconItem::unknown(int size) -{ - return unknownIcon.pixmap(size); -} - -UnknownIconItem::UnknownIconItem(QString path) : IconItem(path) -{ - -} - -QPixmap UnknownIconItem::unknown(int size) -{ - return QPixmap(); -} - -//=========================================== -// -// IconProvider -// -//=========================================== - -IconProvider::IconProvider() -{ -} - -QString IconProvider::path(int id) -{ - std::map::iterator it=iconsById_.find(id); - if(it != iconsById_.end()) - return it->second->path(); - - return QString(); - -} - -int IconProvider::add(QString path,QString name) -{ - std::map::iterator it=icons_.find(name); - if(it == icons_.end()) - { - IconItem *p=new IconItem(path); - icons_[name]=p; - iconsById_[p->id()]=p; - return p->id(); - } - - return it->second->id(); -} - -IconItem* IconProvider::icon(QString name) -{ - std::map::iterator it=icons_.find(name); - if(it != icons_.end()) - return it->second; - - return &unknownIcon; -} - -IconItem* IconProvider::icon(int id) -{ - std::map::iterator it=iconsById_.find(id); - if(it != iconsById_.end()) - return it->second; - - return &unknownIcon; -} - -QPixmap IconProvider::pixmap(QString name,int size) -{ - return icon(name)->pixmap(size); -} - -QPixmap IconProvider::pixmap(int id,int size) -{ - return icon(id)->pixmap(size); -} - -QPixmap IconProvider::lockPixmap(int size) -{ - return lockIcon.pixmap(size); -} - -QPixmap IconProvider::warningPixmap(int size) -{ - return warningIcon.pixmap(size); -} - -QPixmap IconProvider::errorPixmap(int size) -{ - return errorIcon.pixmap(size); -} - -QPixmap IconProvider::infoPixmap(int size) -{ - return infoIcon.pixmap(size); -} - -static IconProvider iconProvider; diff -Nru ecflow-4.9.0/Viewer/src/IconProvider.hpp ecflow-4.11.1/Viewer/src/IconProvider.hpp --- ecflow-4.9.0/Viewer/src/IconProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/IconProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef ICONPROVIDER_HPP_ -#define ICONPROVIDER_HPP_ - -#include - -#include - -class IconItem -{ -public: - explicit IconItem(QString); - virtual ~IconItem() {} - - QPixmap pixmap(int); - int id () const {return id_;} - QString path() const {return path_;} - -protected: - virtual QPixmap unknown(int); - - QString path_; - std::map pixmaps_; - int id_; -}; - -class UnknownIconItem : public IconItem -{ -public: - explicit UnknownIconItem(QString); - -protected: - QPixmap unknown(int); -}; - - -class IconProvider -{ -public: - IconProvider(); - - static int add(QString path,QString name); - - static QString path(int id); - static QPixmap pixmap(QString name,int size); - static QPixmap pixmap(int id,int size); - - static QPixmap lockPixmap(int); - static QPixmap warningPixmap(int); - static QPixmap errorPixmap(int); - static QPixmap infoPixmap(int); - -private: - static IconItem* icon(QString name); - static IconItem* icon(int id); - - static std::map icons_; - static std::map iconsById_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InfoPanel.cpp ecflow-4.11.1/Viewer/src/InfoPanel.cpp --- ecflow-4.9.0/Viewer/src/InfoPanel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,815 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "InfoPanel.hpp" - -#include -#include -#include -#include - -#include "DashboardDock.hpp" -#include "InfoPanelItem.hpp" -#include "InfoPanelHandler.hpp" -#include "NodePathWidget.hpp" -#include "ServerHandler.hpp" -#include "SessionHandler.hpp" -#include "UiLog.hpp" -#include "VSettings.hpp" -#include "WidgetNameProvider.hpp" - -//#define _UI_INFOPANEL_DEBUG - -//============================================== -// -// InfoPanelItemHandler -// -//============================================== - -QWidget* InfoPanelItemHandler::widget() -{ - return item_->realWidget(); -} - -bool InfoPanelItemHandler::match(const std::vector& ids) const -{ - return (std::find(ids.begin(),ids.end(),def_) != ids.end()); -} - -void InfoPanelItemHandler::addToTab(QTabWidget *tab) -{ - int idx=tab->addTab(item_->realWidget(),QString::fromStdString(def_->label())); - tab->setTabIcon(idx,QPixmap(":/viewer/" + QString::fromStdString(def_->icon()))); -} - -//============================================== -// -// InfoPanel -// -//============================================== - -InfoPanel::InfoPanel(QWidget* parent) : - DashboardWidget("info",parent), - tabBeingCleared_(false), - tabBeingAdjusted_(false) -{ - setupUi(this); - - bcWidget_=new NodePathWidget(this); - - connect(tab_,SIGNAL(currentChanged(int)), - this,SLOT(slotCurrentWidgetChanged(int))); - - connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), - this,SLOT(slotReloadFromBc(VInfo_ptr))); - - tab_->setIconSize(QSize(16,16)); - - messageLabel_->hide(); - - //Initialise action state - actionBreadcrumbs_->setChecked(bcWidget_->isGuiMode()); - actionFrozen_->setChecked(false); - - WidgetNameProvider::nameChildren(this); - - //Create the handler for all the possible panels! - for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); - it != InfoPanelHandler::instance()->panels().end(); ++it) - { - createHandler(*it); - } -} - -InfoPanel::~InfoPanel() -{ - localClear(); - - Q_FOREACH(InfoPanelItemHandler *d,items_) - delete d; -} - -QMenu* InfoPanel::buildOptionsMenu() -{ - QMenu *menu=new QMenu(this); - menu->setTearOffEnabled(true); - - menu->addAction(actionBreadcrumbs_); - menu->addAction(actionFrozen_); - - return menu; -} - -//When the infopanel is in a dockwidget we add the option actions -//to the dockwidget title bar widget -void InfoPanel::populateDockTitleBar(DashboardDockTitleWidget* tw) -{ - QMenu *menu=buildOptionsMenu(); - - //Sets the menu on the toolbutton - tw->optionsTb()->setMenu(menu); - - //Add the bc to the titlebar. This will reparent the bcWidget!!! So we must not - //access it in the destructor!!! - tw->setBcWidget(bcWidget_); -} - -//When the infopanel is in a dialog we need to add the optionsTb to the dialog. -void InfoPanel::populateDialog() -{ - setInDialog(true); - - //Add the bcWidget_ to the top of the dialogue - bcWidget_->useTransparentBg(false); - verticalLayout_->insertWidget(0,bcWidget_); - - QMenu *menu=buildOptionsMenu(); - - QWidget *cornerW=new QWidget(this); - QHBoxLayout *hb=new QHBoxLayout(cornerW); - hb->setContentsMargins(0,0,0,0); - hb->setSpacing(1); - - QToolButton *detachedTb=new QToolButton(this); - detachedTb->setAutoRaise(true); - detachedTb->setDefaultAction(detachedAction_); - hb->addWidget(detachedTb); - setDetached(true); //by default a dialog is detached! - - QToolButton* optionsTb=new QToolButton(this); - optionsTb->setAutoRaise(true); - optionsTb->setIcon(QPixmap(":/viewer/cogwheel.svg")); - optionsTb->setPopupMode(QToolButton::InstantPopup); - optionsTb->setToolTip(tr("Options")); - optionsTb->setMenu(menu); - hb->addWidget(optionsTb); - - tab_->setCornerWidget(cornerW); - - //This will set the dialog title - updateTitle(); -} - -void InfoPanel::setCurrent(const std::string& name) -{ - for(int i=0; i < tab_->count(); i++) - { - if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) - { - //Clear the contents - if(d->def() && d->def()->name() == name) - { - tab_->setCurrentIndex(i); - } - } - } -} - -void InfoPanel::clear() -{ - localClear(); - - //Clear the breadcrumbs - bcWidget_->clear(); -} - -//This is safe to call from the destructor -void InfoPanel::localClear() -{ - messageLabel_->hide(); - messageLabel_->clear(); - - //Unregister from observer lists - if(info_ && info_.get()) - { - if(info_->server()) - { - info_->server()->removeServerObserver(this); - } - - info_->removeObserver(this); - } - - //release info - info_.reset(); - - //Clear the tab contents - for(int i=0; i < tab_->count(); i++) - { - if(InfoPanelItem* item=findItem(tab_->widget(i))) - { - //Diable and clear the contents - item->setActive(false); - } - } - //Clear the tabs - clearTab(); -} - -//TODO: It should be the slot -void InfoPanel::reset(VInfo_ptr info) -{ - if(info_ && info) - { - //UiLog().dbg() << "path: " << info_->path() << " " << info->path(); - - if(*(info_.get()) == *(info.get())) - return; - - //it can happen that the stored info was not yet updated after a - //server reload. If there is chance for it we try to regain its data and - //comapare it again to the incoming node - else if(info_->server() == info->server() && - info_->storedNodePath() == info->storedNodePath() && - !info_->node() && info->node()) - { - info_->regainData(); - if(info_->node() == info->node()) - return; - } - } - - messageLabel_->hide(); - messageLabel_->clear(); - - //Set info - adjustInfo(info); - - //Set tabs - adjustTabs(info); - - //Set breadcrumbs - bcWidget_->setPath(info); - - updateTitle(); -} - -//This slot is called when the info object is selected in another panel -void InfoPanel::slotReload(VInfo_ptr info) -{ - //When the mode is detached it cannot receive - //the reload request - if(info_ && detached()) - return; - - if(info && info->isAttribute()) - { - reset(VInfo::createParent(info)); - } - else - { - reset(info); - } -} - -void InfoPanel::slotReloadFromBc(VInfo_ptr info) -{ - reset(info); - if(info_) - Q_EMIT selectionChanged(info_); -} - -void InfoPanel::linkSelected(VInfo_ptr info) -{ - //Here info can be an attribute! - slotReload(info); - if(info_ && info) - Q_EMIT selectionChanged(info); -} - -//Set the new VInfo object. -//We also we need to manage the node observers. The InfoItem -//will be the observer of the server of the object stored in -//the new VInfo -void InfoPanel::adjustInfo(VInfo_ptr info) -{ - //Check if there is data in info - if(info) - { - ServerHandler *server=info->server(); - - bool sameServer=(info_)?(info_->server() == server):false; - - //Handle observers - if(!sameServer) - { - if(info_ && info_->server()) - { - info_->server()->removeServerObserver(this); - //info_->server()->removeNodeObserver(this); - } - - info->server()->addServerObserver(this); - //info->server()->addNodeObserver(this); - } - } - //If the there is no data we clean everything and return - else - { - if(info_ && info_->server()) - { - info_->server()->removeServerObserver(this); - //info_->server()->removeNodeObserver(this); - } - } - - //Set the info - if(info_) - { - info_->removeObserver(this); - } - - info_=info; - - if(info_) - { - info_->addObserver(this); - } - -} - -void InfoPanel::adjustTabs(VInfo_ptr info) -{ - //Set tabs according to the current set of roles - std::vector ids; - InfoPanelHandler::instance()->visible(info,ids); - -#ifdef _UI_INFOPANEL_DEBUG - for(int i=0; i < ids.size(); i++) - { - UiLog().dbg() << "InfoPanel --> tab: " << ids[i]->name(); - } -#endif - - int match=0; - for(int i=0; i < tab_->count(); i++) - { - if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) - { - //Disable and force to clear the contents - d->item()->setActive(false); - - if(d->match(ids)) - match++; - } - } - - //Remember the current widget - QWidget *current=tab_->currentWidget(); - InfoPanelItem* currentItem=findItem(current); - - //A new set of tabs is needed! - if(tab_->count() != static_cast(ids.size()) || match != static_cast(ids.size())) - { - //We set this flag true so that the change of the current tab should not - //trigger a reload! We want to reload the current tab only after the - //tab adjustment. - tabBeingAdjusted_=true; - - //Remove the pages but does not delete them. - clearTab(); - - for(std::vector::iterator it=ids.begin(); it != ids.end(); ++it) - { - if(InfoPanelItemHandler* d=findHandler(*it)) - { - d->addToTab(tab_); - d->item()->setActive(true); - } - } - - //Try to set the previous current widget as current again - currentItem=0; - bool hasCurrent=false; - for(int i=0 ; i < tab_->count(); i++) - { - if(tab_->widget(i) == current) - { - tab_->setCurrentIndex(i); - currentItem=findItem(current); - hasCurrent=true; - break; - } - } - //If the current widget is not present select the first - if(!hasCurrent && tab_->count() >0) - { - tab_->setCurrentIndex(0); - currentItem=findItem(tab_->widget(0)); - } - - tabBeingAdjusted_=false; - } - - //We use the same set of tabs - else - { - for(int i=0; i < tab_->count(); i++) - { - if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) - { - d->item()->setActive(true); - } - } - } - - //We reload the current tab - if(currentItem) - { - currentItem->setSelected(true,info); - //currentItem->reload(info); - } -} - -InfoPanelItem* InfoPanel::findItem(QWidget* w) -{ - if(!w) - return 0; - - Q_FOREACH(InfoPanelItemHandler *d,items_) - { - if(d->widget() == w) - return d->item(); - } - - return 0; -} - -InfoPanelItemHandler* InfoPanel::findHandler(QWidget* w) -{ - if(!w) - return 0; - - Q_FOREACH(InfoPanelItemHandler *d,items_) - { - if(d->widget() == w) - return d; - } - - return 0; -} - -InfoPanelItemHandler* InfoPanel::findHandler(InfoPanelDef* def) -{ - Q_FOREACH(InfoPanelItemHandler *d,items_) - { - if(d->def() == def) - return d; - } - - return createHandler(def); -} - -InfoPanelItemHandler* InfoPanel::createHandler(InfoPanelDef* def) -{ - if(InfoPanelItem *iw=InfoPanelItemFactory::create(def->name())) - { - WidgetNameProvider::nameChildren(iw->realWidget()); - iw->setOwner(this); - iw->setFrozen(frozen()); - iw->setDetached(detached()); - - //iw will be added to the tab so the tab will be its parent. Moreover - //the tab will stay its parent even if iw got removed from the tab! - //So when the tab is deleted all the iw-s will be correctly deleted as well. - - InfoPanelItemHandler* h=new InfoPanelItemHandler(def,iw); - items_ << h; - return h; - } - return 0; -} - - -void InfoPanel::slotCurrentWidgetChanged(int idx) -{ - if(tabBeingCleared_ || tabBeingAdjusted_) - return; - - if(!info_.get()) - return; - - if(InfoPanelItem* current=findItem(tab_->widget(idx))) - { - current->setSelected(true,info_); - - //Reload the item if it is needed - /*if(!current->isSuspended() && !current->info()) - current->reload(info_);*/ - - //Deselect the others - for(int i=0; i < tab_->count(); i++) - { - if(InfoPanelItemHandler* d=findHandler(tab_->widget(i))) - { - if(d->item() != current) - d->item()->setSelected(false,info_); - } - } - } -} - -void InfoPanel::clearTab() -{ - tabBeingCleared_=true; - tab_->clear(); - tabBeingCleared_=false; -} - -void InfoPanel::detachedChanged() -{ - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->setDetached(detached()); - } - updateTitle(); -} - -void InfoPanel::on_actionBreadcrumbs__toggled(bool b) -{ - if(isInDialog()) - { - bcWidget_->setVisible(b); - } - else - { - if(b) - { - bcWidget_->setMode(NodePathWidget::GuiMode); - } - else - { - bcWidget_->setMode(NodePathWidget::TextMode); - } - } -} - -void InfoPanel::on_actionFrozen__toggled(bool b) -{ - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->setFrozen(b); - } - updateTitle(); -} - -bool InfoPanel::frozen() const -{ - return actionFrozen_->isChecked(); -} - -void InfoPanel::updateTitle() -{ - if(isInDialog()) - { - QString txt; - if(frozen()) - txt+="(frozen) "; - - if(info_) - { - txt+=QString::fromStdString(info_->path()); - } - - Q_EMIT titleUpdated(txt); - } -} - -void InfoPanel::relayInfoPanelCommand(VInfo_ptr info,QString cmd) -{ - Q_EMIT popInfoPanel(info,cmd); -} - -void InfoPanel::relayDashboardCommand(VInfo_ptr info,QString cmd) -{ - Q_EMIT dashboardCommand(info,cmd); -} - -void InfoPanel::notifyDataLost(VInfo* info) -{ - if(info_ && info_.get() == info) - { - clear(); - } -} - -//------------------------------------------------- -// ServerObserver methods -//------------------------------------------------- - -void InfoPanel::notifyDefsChanged(ServerHandler *server, const std::vector& aspect) -{ - if(frozen()) - return; - - if(info_) - { - if(info_->server() && info_->server() == server) - { - //Dispatch the change - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->defsChanged(aspect); - } - } - } -} - -void InfoPanel::notifyServerDelete(ServerHandler* server) -{ - if(info_ && info_->server() == server) - { - clear(); - } -} - -//This must be called at the beginning of a reset -void InfoPanel::notifyBeginServerClear(ServerHandler* server) -{ - if(info_) - { - if(info_->server() && info_->server() == server) - { - messageLabel_->showWarning("Server " + QString::fromStdString(server->name()) + " is being reloaded. \ - Until it is finished only limited functionalty is avaliable in the Info Panel!"); - - messageLabel_->startLoadLabel(); - - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->setSuspended(true,info_); - } - } - } -} - -//This must be called at the end of a reset -void InfoPanel::notifyEndServerScan(ServerHandler* server) -{ - if(info_) - { - if(info_->server() && info_->server() == server) - { - messageLabel_->hide(); - messageLabel_->clear(); - - //We try to ressurect the info. We have to do it explicitly because it is not guaranteed - //that notifyEndServerScan() will be first called on the VInfo then on the InfoPanel. So it - //is possible that the node exists but is still set to NULL in VInfo. - info_->regainData(); - - //If the info is not available dataLost() might have already been called and - //the panel was reset! - if(!info_) - return; - - Q_ASSERT(info_->server() && info_->node()); - - //Otherwise we resume all the tabs - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->setSuspended(false,info_); - } - } - } -} - -void InfoPanel::notifyServerConnectState(ServerHandler* server) -{ - if(frozen()) - return; - - if(info_) - { - if(info_->server() && info_->server() == server) - { - //Dispatch the change - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->connectStateChanged(); - } - } - } -} - -void InfoPanel::notifyServerSuiteFilterChanged(ServerHandler* server) -{ - //TODO: does frozen make sense in this case? - if(frozen()) - return; - - if(info_) - { - if(info_->server() && info_->server() == server) - { - //Dispatch the change - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->suiteFilterChanged(); - } - } - } -} - -void InfoPanel::notifyEndServerSync(ServerHandler* server) -{ - //TODO: does frozen make sense in this case? - if(frozen()) - return; - - if(info_) - { - if(info_->server() && info_->server() == server) - { - //Dispatch the change - Q_FOREACH(InfoPanelItemHandler *item,items_) - { - item->item()->serverSyncFinished(); - } - } - } -} - -void InfoPanel::rerender() -{ - bcWidget_->rerender(); -} - -void InfoPanel::writeSettings(VComboSettings* vs) -{ - vs->put("type",type_); - vs->put("dockId",id_); - - bcWidget_->writeSettings(vs); - - vs->putAsBool("frozen",frozen()); - - DashboardWidget::writeSettings(vs); - - Q_FOREACH(InfoPanelItemHandler *d,items_) - { - if(d->item()) - d->item()->writeSettings(vs); - } -} - -void InfoPanel::readSettings(VComboSettings* vs) -{ - std::string type=vs->get("type",""); - if(type != type_) - { - return; - } - - //-------------------------- - //Breadcrumbs - //-------------------------- - - bcWidget_->readSettings(vs); - - //Synchronise the action and the breadcrumbs state - //This will not emit the trigered signal of the action!! - actionBreadcrumbs_->setChecked(bcWidget_->isGuiMode()); - - actionFrozen_->setChecked(vs->getAsBool("frozen",frozen())); - - DashboardWidget::readSettings(vs); - - Q_FOREACH(InfoPanelItemHandler *d,items_) - { - if(d->item()) - d->item()->readSettings(vs); - } -} - -void InfoPanel::writeSettingsForDialog() -{ - SessionItem* cs=SessionHandler::instance()->current(); - assert(cs); - VSettings vs(cs->infoPanelDialogFile()); - - vs.putAsBool("breadcrumbs",bcWidget_->isVisible()); - vs.putAsBool("frozen",frozen()); - vs.putAsBool("detached",detached()); - vs.write(); -} - -void InfoPanel::readSettingsForDialog() -{ - SessionItem* cs=SessionHandler::instance()->current(); - assert(cs); - VSettings vs(cs->infoPanelDialogFile()); - vs.read(false); - - actionBreadcrumbs_->setChecked(vs.getAsBool("breadcrumbs",true)); - bcWidget_->setVisible(actionBreadcrumbs_->isChecked()); - - actionFrozen_->setChecked(vs.getAsBool("frozen",frozen())); - detachedAction_->setChecked(vs.getAsBool("detached",detached())); -} diff -Nru ecflow-4.9.0/Viewer/src/InfoPanelHandler.cpp ecflow-4.11.1/Viewer/src/InfoPanelHandler.cpp --- ecflow-4.9.0/Viewer/src/InfoPanelHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanelHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "InfoPanelHandler.hpp" - - -#include -#include -#include - -#include "NodeExpression.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" - -InfoPanelHandler* InfoPanelHandler::instance_=0; - - -InfoPanelDef::InfoPanelDef(const std::string& name) : - name_(name), - hidden_(false), - visibleCondition_(0), - enabledCondition_(0) -{ -} - -InfoPanelHandler::InfoPanelHandler() -{ -} - -InfoPanelHandler* InfoPanelHandler::instance() -{ - if(!instance_) - instance_=new InfoPanelHandler(); - - return instance_; -} - -void InfoPanelHandler::init(const std::string &configFile) -{ - // parse the response using the boost JSON property tree parser - - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(configFile, pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse JSON menu file : " + errorMessage)); - return; - } - - - // iterate over the top level of the tree - for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) - { - if (itTopLevel->first == "info_panel") - { - UiLog().dbg() << "Panels:"; - - ptree const &panelsPt = itTopLevel->second; - - // iterate through all the panels - for (ptree::const_iterator itPanel = panelsPt.begin(); itPanel != panelsPt.end(); ++itPanel) - { - ptree const &panelPt = itPanel->second; - - std::string cname = panelPt.get("name", ""); - - UiLog().dbg() << " " << cname; - - InfoPanelDef* def= new InfoPanelDef(cname); - - def->setLabel(panelPt.get("label","")); - def->setIcon(panelPt.get("icon","")); - def->setDockIcon(panelPt.get("dock_icon","")); - def->setShow(panelPt.get("show","")); - def->setTooltip(panelPt.get("tooltip","")); - def->setButtonTooltip(panelPt.get("button_tooltip","")); - - std::string enabled = panelPt.get("enabled_for", ""); - std::string visible = panelPt.get("visible_for", ""); - - if(panelPt.get("hidden", "") == "1") - { - def->setHidden(true); - } - - BaseNodeCondition *enabledCond = NodeExpressionParser::instance()->parseWholeExpression(enabled); - if (enabledCond == NULL) - { - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse enabled condition: " + enabled)); - enabledCond = new FalseNodeCondition(); - } - def->setEnabledCondition(enabledCond); - - - BaseNodeCondition *visibleCond = NodeExpressionParser::instance()->parseWholeExpression(visible); - if (visibleCond == NULL) - { - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse visible condition: " + visible)); - visibleCond = new FalseNodeCondition(); - } - def->setVisibleCondition(visibleCond); - - panels_.push_back(def); - - } - } - } -} - -void InfoPanelHandler::visible(VInfo_ptr info,std::vector& lst) -{ - if(!info || !info.get()) - return; - - for(std::vector::const_iterator it=panels_.begin(); it != panels_.end(); ++it) - { - if(!(*it)->hidden() && (*it)->visibleCondition()->execute(info)) - lst.push_back((*it)); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/InfoPanelHandler.hpp ecflow-4.11.1/Viewer/src/InfoPanelHandler.hpp --- ecflow-4.9.0/Viewer/src/InfoPanelHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanelHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INFOPANELHANDLER_HPP_ -#define INFOPANELHANDLER_HPP_ - -#include -#include - -#include "VInfo.hpp" - -class BaseNodeCondition; - -class InfoPanelDef -{ -public: - explicit InfoPanelDef(const std::string&); - - std::string name() const {return name_;} - std::string label() const {return label_;} - std::string icon() const {return icon_;} - std::string dockIcon() const {return dockIcon_;} - std::string show() const {return show_;} - std::string tooltip() const {return tooltip_;} - std::string buttonTooltip() const {return buttonTooltip_;} - bool hidden() const {return hidden_;} - BaseNodeCondition* visibleCondition() const {return visibleCondition_;} - - void setLabel(const std::string& s) {label_=s;} - void setIcon(const std::string& s) {icon_=s;} - void setDockIcon(const std::string& s) {dockIcon_=s;} - void setShow(const std::string& s) {show_=s;} - void setTooltip(const std::string& tooltip) {tooltip_=tooltip;} - void setButtonTooltip(const std::string& tooltip) {buttonTooltip_=tooltip;} - void setVisibleCondition(BaseNodeCondition *visibleCond) {visibleCondition_=visibleCond;} - void setEnabledCondition(BaseNodeCondition *enabledCond) {enabledCondition_=enabledCond;} - void setHidden(bool b) {hidden_=b;} - -protected: - std::string name_; - std::string label_; - std::string icon_; - std::string dockIcon_; - std::string show_; - std::string tooltip_; - std::string buttonTooltip_; - bool hidden_; - - BaseNodeCondition *visibleCondition_; - BaseNodeCondition *enabledCondition_; -}; - -class InfoPanelHandler -{ -public: - InfoPanelHandler(); - - void init(const std::string& file); - void visible(VInfo_ptr info,std::vector& lst); - const std::vector& panels() const {return panels_;} - - static InfoPanelHandler* instance(); - -protected: - static InfoPanelHandler* instance_; - - std::vector panels_; - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InfoPanel.hpp ecflow-4.11.1/Viewer/src/InfoPanel.hpp --- ecflow-4.9.0/Viewer/src/InfoPanel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INFOPANEL_HPP_ -#define INFOPANEL_HPP_ - -#include - -#include "DashboardWidget.hpp" -#include "ServerObserver.hpp" -#include "VInfo.hpp" - -#include "ui_InfoPanel.h" - -class QMenu; -class QTabWidget; - -class DashboardDockTitleWidget; -class InfoPanel; -class InfoPanelDef; -class InfoPanelItem; - -class InfoPanelItemHandler -{ -friend class InfoPanel; - -public: - InfoPanelItemHandler(InfoPanelDef* def,InfoPanelItem* item) : - def_(def), item_(item) {} - - bool match(const std::vector& defs) const; - InfoPanelItem* item() const {return item_;} - QWidget* widget(); - InfoPanelDef* def() const {return def_;} - -protected: - void addToTab(QTabWidget *); - -private: - InfoPanelDef* def_; - InfoPanelItem* item_; -}; - - -class InfoPanel : public DashboardWidget, public ServerObserver, public VInfoObserver, private Ui::InfoPanel -{ - Q_OBJECT - -public: - explicit InfoPanel(QWidget* parent=0); - virtual ~InfoPanel(); - bool frozen() const; - void clear(); - void setCurrent(const std::string& name); - void linkSelected(VInfo_ptr); - void relayInfoPanelCommand(VInfo_ptr info,QString cmd); - void relayDashboardCommand(VInfo_ptr info,QString cmd); - - void populateDialog(); - - //From DashboardWidget - void populateDockTitleBar(DashboardDockTitleWidget*); - void reload() {} - void rerender(); - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - void writeSettingsForDialog(); - void readSettingsForDialog(); - - //From VInfoObserver - void notifyDelete(VInfo*) {} - void notifyDataLost(VInfo*); - - //From ServerObserver - void notifyDefsChanged(ServerHandler* server, const std::vector& a); - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - void notifyEndServerClear(ServerHandler* server) {} - void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - void notifyEndServerScan(ServerHandler* server); - void notifyServerConnectState(ServerHandler* server); - void notifyServerSuiteFilterChanged(ServerHandler* server); - void notifyEndServerSync(ServerHandler* server); - -public Q_SLOTS: - void slotReload(VInfo_ptr node); - void setCurrentSelection(VInfo_ptr node) {slotReload(node);} - -protected Q_SLOTS: - void slotReloadFromBc(VInfo_ptr node); - void slotCurrentWidgetChanged(int); - void on_actionBreadcrumbs__toggled(bool b); - void on_actionFrozen__toggled(bool b); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - -protected: - void detachedChanged(); - -private: - void localClear(); - void reset(VInfo_ptr node); - void adjustInfo(VInfo_ptr node); - void adjustTabs(VInfo_ptr node); - InfoPanelItemHandler* findHandler(QWidget* w); - InfoPanelItemHandler* findHandler(InfoPanelDef*); - InfoPanelItem* findItem(QWidget* w); - InfoPanelItemHandler* createHandler(InfoPanelDef*); - void clearTab(); - void updateTitle(); - QMenu* buildOptionsMenu(); - - QList items_; - VInfo_ptr info_; - bool tabBeingCleared_; - bool tabBeingAdjusted_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InfoPanelItem.cpp ecflow-4.11.1/Viewer/src/InfoPanelItem.cpp --- ecflow-4.9.0/Viewer/src/InfoPanelItem.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanelItem.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,320 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "InfoPanelItem.hpp" - -#include "InfoPanel.hpp" -#include "InfoProvider.hpp" -#include "ServerHandler.hpp" -#include "VNode.hpp" - -#include - -static std::map* makers = 0; - -InfoPanelItemFactory::InfoPanelItemFactory(const std::string& name) -{ - if(makers == 0) - makers = new std::map; - - // Put in reverse order... - (*makers)[name] = this; -} - -InfoPanelItemFactory::~InfoPanelItemFactory() -{ - // Not called -} - -InfoPanelItem* InfoPanelItemFactory::create(const std::string& name) -{ - std::map::iterator j = makers->find(name); - if(j != makers->end()) - return (*j).second->make(); - - return 0; -} - -//======================================================= -// -// InfoPanelItem -// -//======================================================= - - -InfoPanelItem::~InfoPanelItem() -{ - clear(); -} - -void InfoPanelItem::setOwner(InfoPanel* owner) -{ - assert(!owner_); - owner_=owner; -} - -//Set the new VInfo object. -//We also we need to manage the node observers. The InfoItem -//will be the observer of the server of the object stored in -//the new VInfo -void InfoPanelItem::adjust(VInfo_ptr info) -{ - //Check if there is data in info - if(info) - { - ServerHandler *server=info->server(); - - bool sameServer=(info_)?(info_->server() == server):false; - - //Handle observers - if(!sameServer) - { - if(info_ && info_->server()) - { - info_->server()->removeNodeObserver(this); - } - info->server()->addNodeObserver(this); - } - } - //If the there is no data we clean everything and return - else - { - if(info_ && info_->server()) - { - info_->server()->removeNodeObserver(this); - } - } - - //Set the info - info_=info; -} - -void InfoPanelItem::clear() -{ - if(info_ && info_->server()) - { - //info_->server()->removeServerObserver(this); - info_->server()->removeNodeObserver(this); - } - - info_.reset(); - - for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) - { - (*it)->clear(); - } -} - -//This function is called when the infopanel -// is being reset. The info_ might be unset. -void InfoPanelItem::setActive(bool active) -{ - active_=active; - - if(active_) - { - //Enable the infoProviders - for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) - { - (*it)->setActive(true); - } - } - else - { - clearContents(); - - selected_=false; - suspended_=false; - - //Disable the info provider - for(std::vector::iterator it=infoProviders_.begin(); it != infoProviders_.end(); ++it) - { - //This will clear the providers again - (*it)->setActive(false); - } - } - - //updateWidgetState(); -} - -void InfoPanelItem::setSelected(bool selected,VInfo_ptr info) -{ - if(selected_ == selected) - return; - - ChangeFlags flags(SelectedChanged); - selected_=selected; - - assert(active_); - - if(selected_) - { - //Suspend - if(suspended_) {} - //Resume - else - { - if(unselectedFlags_.isSet(KeepContents)) - { - if(!info_) - { - reload(info); - return; - } - } - else - { - reload(info); - return; - } - } - } - - //if the item becomes unselected we do not do anything if it is frozen - //or the contents must be kept (e.g. for output) - else - { - if(!frozen_) - { - if(!unselectedFlags_.isSet(KeepContents)) - { - //This will also clear the providers - clearContents(); - } - } - - return; - } - - //We update the derived class - updateState(flags); -} - -void InfoPanelItem::setSuspended(bool suspended,VInfo_ptr info) -{ - if(suspended_ == suspended) - return; - - suspended_=suspended; - ChangeFlags flags(SuspendedChanged); - - if(!active_) - return; - - //Suspend - if(suspended_) {} - //Resume - else - { - if(selected_ && !info_) - { - reload(info); - return; - } - } - - //We update the derived class - updateState(flags); -} - -void InfoPanelItem::setFrozen(bool b) -{ - frozen_=b; - if(!active_) - return; - - //We update the derived class - updateState(FrozenChanged); - -} - -void InfoPanelItem::setDetached(bool b) -{ - detached_=b; - - if(!active_) - return; - - //We update the derived class - updateState(DetachedChanged); -} - -void InfoPanelItem::linkSelected(const std::string& path) -{ - if(!suspended_) - { - VInfo_ptr info=VInfo::createFromPath(info_->server(),path); - - if(info) - { - assert(owner_); - owner_->linkSelected(info); - } - } -} - -void InfoPanelItem::linkSelected(VInfo_ptr info) -{ - if(!suspended_) - { - if(info) - { - assert(owner_); - owner_->linkSelected(info); - } - } -} - -void InfoPanelItem::relayInfoPanelCommand(VInfo_ptr info,QString cmd) -{ - if(info) - { - assert(owner_); - owner_->relayInfoPanelCommand(info,cmd); - } -} - -void InfoPanelItem::relayDashboardCommand(VInfo_ptr info,QString cmd) -{ - if(info) - { - assert(owner_); - owner_->relayDashboardCommand(info,cmd); - } -} - -//From NodeObserver -void InfoPanelItem::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange&) -{ - if(!node || frozen_ || !active_ || suspended_) - return; - - //Check if there is data in info - if(info_) - { - if(info_->isNode()) - { - //Check if updates are handled when unselected - if(!selected_ && !unselectedFlags_.isSet(KeepContents)) - { - return; - } - - //Check if the updated node is handled by the item - if(handleAnyChange_ || info_->node() == node || - (useAncestors_ && info_->node()->isAncestor(node))) - { - //We call the method implemented in the concrete class - //to handle the changes - nodeChanged(node,aspect); - return; - } - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/InfoPanelItem.hpp ecflow-4.11.1/Viewer/src/InfoPanelItem.hpp --- ecflow-4.9.0/Viewer/src/InfoPanelItem.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanelItem.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INFOPANELITEM_HPP_ -#define INFOPANELITEM_HPP_ - -#include "FlagSet.hpp" -#include "NodeObserver.hpp" -#include "VInfo.hpp" -#include "InfoPresenter.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -#include -#include - -class QWidget; -class InfoPanel; -class InfoProvider; -class VComboSettings; - -//This is the (abstract) base class to represent one tab in the info panel. -//It cannot be inheried from QObject beacuse we would end up with double inheritance since -//all the derived calsses are inherited from QObject!! - -class InfoPanelItem : public VTaskObserver, public InfoPresenter, public NodeObserver -{ -friend class InfoPanel; - -public: - InfoPanelItem() : owner_(0), active_(false), selected_(false), suspended_(false), - frozen_(false), detached_(false), unselectedFlags_(KeepContents), - useAncestors_(false),handleAnyChange_(false) {} - virtual ~InfoPanelItem(); - - enum ChangeFlag {ActiveChanged=1,SelectedChanged=2,SuspendedChanged=4,FrozenChanged=8,DetachedChanged=16}; - typedef FlagSet ChangeFlags; - - //What to do when the item is unselected - enum UnselectedFlag {KeepContents=1,KeepActivity=2}; - typedef FlagSet UnselectedFlags; - - virtual void reload(VInfo_ptr info)=0; - virtual QWidget* realWidget()=0; - virtual void clearContents()=0; - - void setOwner(InfoPanel*); - - virtual void setActive(bool); - void setSelected(bool,VInfo_ptr); - void setSuspended(bool,VInfo_ptr); - void setFrozen(bool); - void setDetached(bool); - - bool isSuspended() const {return suspended_;} - - //From VTaskObserver - void taskChanged(VTask_ptr) {} - - //From VInfoPresenter - void infoReady(VReply*) {} - void infoFailed(VReply*) {} - void infoProgress(VReply*) {} - void infoProgressStart(int min,int max,const std::string& text) {} - void infoProgress(int value,const std::string& text) {} - - //From NodeObserver - void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); - void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) {} - - virtual void writeSettings(VComboSettings* vs) {} - virtual void readSettings(VComboSettings* vs) {} - -protected: - void adjust(VInfo_ptr); - void linkSelected(const std::string& path); - void linkSelected(VInfo_ptr); - void relayInfoPanelCommand(VInfo_ptr info,QString cmd); - void relayDashboardCommand(VInfo_ptr info,QString cmd); - - virtual void clear(); - virtual void updateState(const ChangeFlags&)=0; - - //Notifications about the server changes - virtual void defsChanged(const std::vector&)=0; - virtual void connectStateChanged() {} - virtual void suiteFilterChanged() {} - virtual void serverSyncFinished() {} - - //Notifications about the node changes - virtual void nodeChanged(const VNode*, const std::vector&)=0; - - InfoPanel* owner_; - bool active_; - bool selected_; - bool suspended_; - bool frozen_; - bool detached_; - UnselectedFlags unselectedFlags_; - bool useAncestors_; - bool handleAnyChange_; -}; - -class InfoPanelItemFactory -{ -public: - explicit InfoPanelItemFactory(const std::string&); - virtual ~InfoPanelItemFactory(); - - virtual InfoPanelItem* make() = 0; - static InfoPanelItem* create(const std::string& name); - -private: - explicit InfoPanelItemFactory(const InfoPanelItemFactory&); - InfoPanelItemFactory& operator=(const InfoPanelItemFactory&); - -}; - -template -class InfoPanelItemMaker : public InfoPanelItemFactory -{ - InfoPanelItem* make() { return new T(); } -public: - explicit InfoPanelItemMaker(const std::string& name) : InfoPanelItemFactory(name) {} -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InfoPanel.ui ecflow-4.11.1/Viewer/src/InfoPanel.ui --- ecflow-4.9.0/Viewer/src/InfoPanel.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPanel.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ - - - InfoPanel - - - - 0 - 0 - 683 - 468 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - - - - Tab 1 - - - - - - - - true - - - Frozen - - - Do not update panel when node is updated - - - - - true - - - Breadcrumbs - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        -
        - - -
        diff -Nru ecflow-4.9.0/Viewer/src/InfoPresenter.hpp ecflow-4.11.1/Viewer/src/InfoPresenter.hpp --- ecflow-4.9.0/Viewer/src/InfoPresenter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoPresenter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INFOPRESENTER_HPP_ -#define INFOPRESENTER_HPP_ - -#include "VInfo.hpp" -#include - -class InfoProvider; -class VReply; - -//This is a base class for presenting a VInfo object. The InfoPanelItems -//are derived from this class. It can contain an InfoProvider member that -//is able to generate the information we want to display about the VInfo. - -class InfoPresenter -{ -public: - InfoPresenter() : infoProvider_(0) {} - virtual ~InfoPresenter() {} - virtual void infoReady(VReply*) {} - virtual void infoFailed(VReply*) {} - virtual void infoProgress(VReply*) {} - virtual void infoProgressStart(const std::string& text,int max) {} - virtual void infoProgress(const std::string& text,int value) {} - virtual void infoAppended(VReply*) {} - VInfo_ptr info() const {return info_;} - void registerInfoProvider(InfoProvider* ip) {infoProviders_.push_back(ip);} - -protected: - VInfo_ptr info_; - InfoProvider* infoProvider_; //the main info provider - std::vector infoProviders_; //the list of all the providers including the main one -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InfoProvider.cpp ecflow-4.11.1/Viewer/src/InfoProvider.cpp --- ecflow-4.9.0/Viewer/src/InfoProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,291 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "InfoProvider.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "ServerHandler.hpp" - -#include - -#include - -InfoProvider::InfoProvider(InfoPresenter* owner,VTask::Type taskType) : - owner_(owner), - taskType_(taskType), - active_(false), - autoUpdate_(false), - inAutoUpdate_(false) -{ - reply_=new VReply(this); - if(owner_) - owner_->registerInfoProvider(this); -} - -InfoProvider::~InfoProvider() -{ - delete reply_; - clear(); -} - -void InfoProvider::clear() -{ - if(task_) - task_->status(VTask::CANCELLED); - - reply_->reset(); - info_.reset(); -} - -void InfoProvider::setActive(bool b) -{ - active_=b; - if(!active_) - clear(); -} - -void InfoProvider::setAutoUpdate(bool b) -{ - autoUpdate_=b; -} - -void InfoProvider::info(VInfo_ptr info) -{ - //We keep it alive - info_=info; - - if(task_) - { - task_->status(VTask::CANCELLED); - task_.reset(); - } - - if(owner_ && info_) - info_->accept(this); -} - -//Server -void InfoProvider::visit(VInfoServer* info) -{ - reply_->reset(); - - if(!info->server()) - { - owner_->infoFailed(reply_); - } - - //Define a task for getting the info from the server. - task_=VTask::create(taskType_,this); - - //Run the task in the server. When it completes taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - info->server()->run(task_); -} - -//Node -void InfoProvider::visit(VInfoNode* info) -{ - reply_->reset(); - - if(!info->node() || !info->node()->node()) - { - owner_->infoFailed(reply_); - } - - //Check if we have a server - if(!info->server()) - { - owner_->infoFailed(reply_); - } - - VNode *n=info->node(); - - std::string fileName; - if(!fileVarName_.empty()) - { - //Get the fileName - fileName=n->genVariable(fileVarName_); - } - - //We try to read the file directly from the disk - if(info->server()->readFromDisk()) - { - //There is a variable defined for the filename - if(!fileName.empty()) - { - if(reply_->textFromFile(fileName)) - { - reply_->fileReadMode(VReply::LocalReadMode); - reply_->fileName(fileName); - owner_->infoReady(reply_); - return; - } - /*else if(handleFileMissing(fileName,reply_)) - { - return; - }*/ - } - } - - //We try to get the file contents from the server - //(this will go through the threaded communication) - - //Define a task for getting the info from the server. - task_=VTask::create(taskType_,n,this); - task_->reply()->fileName(fileName); - task_->reply()->fileReadMode(VReply::ServerReadMode); - - //Run the task in the server. When it finish taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - info->server()->run(task_); - -} - -void InfoProvider::handleFileNotDefined(VReply *reply) -{ - reply->setInfoText(fileNotDefinedText_); - owner_->infoReady(reply_); -} - -bool InfoProvider::handleFileMissing(const std::string& fileName,VReply *reply) -{ - return false; - - //reply->setWarningText(fileMissingText_); - //owner_->infoReady(reply_); -} - -void InfoProvider::taskChanged(VTask_ptr task) -{ - if(task_ != task) - return; - - //temporary hack! - task_->reply()->setSender(this); - - switch(task->status()) - { - case VTask::FINISHED: - { - task->reply()->addLog("TRY>fetch file from ecflow server: OK"); - - //The file should have a copy of the reply log - VFile_ptr f=task_->reply()->tmpFile(); - if(f) - { - f->setFetchDate(QDateTime::currentDateTime()); - f->setFetchMode(VFile::ServerFetchMode); - f->setLog(task_->reply()->log()); - } - task->reply()->status(VReply::TaskDone); - owner_->infoReady(task->reply()); - task_.reset(); - } - break; - case VTask::ABORTED: - case VTask::REJECTED: - task->reply()->addLog("TRY>fetch file from ecflow server: FAILED"); - task->reply()->status(VReply::TaskFailed); - owner_->infoFailed(task->reply()); - task_.reset(); - break; - case VTask::CANCELLED: - if(!task->reply()->errorText().empty()) - { - task->reply()->addLog("TRY>fetch file from ecflow server: FAILED"); - task->reply()->status(VReply::TaskCancelled); - owner_->infoFailed(task->reply()); - } - //We do not need the task anymore. - task_.reset(); - break; - default: - break; - } -} - -JobProvider::JobProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::JobTask) -{ - fileVarName_="ECF_JOB"; - - fileNotDefinedText_="Job is not defined"; - - fileMissingText_="Job not found!
        Check ECF_HOME directory \ - for read/write access. Check for file presence and read access below. \ - The file may have been deleted or this may be a 'dummy' task"; -} - -bool JobProvider::handleFileMissing(const std::string& fileName,VReply *reply) -{ - if(fileName.find(".job0") != std::string::npos) - { - reply->setInfoText("No job to be expected when TRYNO is 0!"); - owner_->infoReady(reply_); - return true; - } - - - /*else - { - reply->setWarningText(fileMissingText_); - }*/ - return false; -} - -ManualProvider::ManualProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::ManualTask) -{ - fileVarName_="ECF_MANUAL"; - fileNotDefinedText_="Manual is not available"; - fileMissingText_="Manual is not available"; -} - -MessageProvider::MessageProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::MessageTask) -{ - -} - -ScriptProvider::ScriptProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::ScriptTask) -{ - fileVarName_="ECF_SCRIPT"; - fileNotDefinedText_="Script is not defined"; - fileMissingText_="Script not/b> found!
        Check ECF_FILES or ECF_HOME directories, \ - for read access. Check for file presence and read access below files directory \ - or this may be a 'dummy' task"; -} - -HistoryProvider::HistoryProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::HistoryTask) -{ - -} - -SuiteProvider::SuiteProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::SuiteListTask) -{ - -} - - -ZombieProvider::ZombieProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::ZombieListTask) -{ - -} - -WhyProvider::WhyProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::WhyTask) -{ - -} - - diff -Nru ecflow-4.9.0/Viewer/src/InfoProvider.hpp ecflow-4.11.1/Viewer/src/InfoProvider.hpp --- ecflow-4.9.0/Viewer/src/InfoProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InfoProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INFOPROVIDER_HPP_ -#define INFOPROVIDER_HPP_ - -#include "FlagSet.hpp" -#include "VInfo.hpp" -#include "InfoPresenter.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -class InfoPanelItem; - -class InfoProvider : public VTaskObserver, public VInfoVisitor -{ -public: - InfoProvider(InfoPresenter* owner,VTask::Type); - virtual ~InfoProvider(); - - void info(VInfo_ptr); - void command(VTask::Type); - virtual void clear(); - - void setActive(bool); - virtual void setAutoUpdate(bool); - bool autoUpdate() const {return autoUpdate_;} - bool inAutoUpdate() const {return inAutoUpdate_;} - - //From VInfoVisitor - void visit(VInfoServer*); - void visit(VInfoNode*); - void visit(VInfoAttribute*) {} - - //From VTaskObserver - void taskChanged(VTask_ptr); - -protected: - virtual void handleFileNotDefined(VReply *reply); - virtual bool handleFileMissing(const std::string& fileName,VReply *reply); - virtual void optionsChanged() {} - - InfoPresenter* owner_; - VInfo_ptr info_; - VTask_ptr task_; - VReply* reply_; - VTask::Type taskType_; - std::string fileVarName_; - std::string fileNotDefinedText_; - std::string fileMissingText_; - bool active_; - bool autoUpdate_; - bool inAutoUpdate_; -}; - -class JobProvider : public InfoProvider -{ -public: - explicit JobProvider(InfoPresenter* owner); -protected: - bool handleFileMissing(const std::string& fileName,VReply *reply); -}; - -class ManualProvider : public InfoProvider -{ -public: - explicit ManualProvider(InfoPresenter* owner); -}; - -class MessageProvider : public InfoProvider -{ -public: - explicit MessageProvider(InfoPresenter* owner); -}; - -class ScriptProvider : public InfoProvider -{ -public: - explicit ScriptProvider(InfoPresenter* owner); -}; - -class HistoryProvider : public InfoProvider -{ -public: - explicit HistoryProvider(InfoPresenter* owner); -}; - -class SuiteProvider : public InfoProvider -{ -public: - explicit SuiteProvider(InfoPresenter* owner); -}; - -class ZombieProvider : public InfoProvider -{ -public: - explicit ZombieProvider(InfoPresenter* owner); -}; - -class WhyProvider : public InfoProvider -{ -public: - explicit WhyProvider(InfoPresenter* owner); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/InputEventLog.cpp ecflow-4.11.1/Viewer/src/InputEventLog.cpp --- ecflow-4.9.0/Viewer/src/InputEventLog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InputEventLog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "InputEventLog.hpp" - -#include "DirectoryHandler.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "LogTruncator.hpp" -#include "TimeStamp.hpp" -#include "UiLog.hpp" - -InputEventLog* InputEventLog::instance_=0; -static bool firstStart=true; - -static QString objectPath(QObject *obj) -{ - QString res; - for(; obj; obj = obj->parent()) - { - if (!res.isEmpty()) - res.prepend("/"); - QString s=obj->objectName(); - //QString cn(obj->metaObject()->className()); - if(s.isEmpty()) s="?"; - //res.prepend("[" + cn + "]" +s); - res.prepend(s); - } - return res; -} - -InputEventLog::InputEventLog(QObject* parent) : QObject(parent), paused_(false) -{ - QString path=QString::fromStdString(DirectoryHandler::uiEventLogFileName()); - outFile_=new QFile(path); - - truncator_=new LogTruncator(path,86400*1000,5*1024*1024,2000,this); - connect(truncator_,SIGNAL(truncateBegin()),this,SLOT(truncateLogBegin())); - connect(truncator_,SIGNAL(truncateEnd()),this,SLOT(truncateLogEnd())); -} - -InputEventLog::~InputEventLog() -{ - outFile_->close(); - delete outFile_; -} - -InputEventLog* InputEventLog::instance() -{ - if(!instance_) - instance_=new InputEventLog(0); - return instance_; -} - -void InputEventLog::start() -{ - Q_ASSERT(!paused_); - - if(out_.device()) - return; - - QFile::OpenMode mode=(firstStart)?QFile::WriteOnly:QFile::Append; - if(outFile_->open(mode)) - { - firstStart=false; - out_.setDevice(outFile_); - qApp->removeEventFilter(this); - qApp->installEventFilter(this); - } - else - { - QFileInfo info(*outFile_); - UiLog().err() << "InputEventLog --> cannot open log file for writing: " << info.absoluteFilePath(); - } -} - -void InputEventLog::stop() -{ - qApp->removeEventFilter(this); - outFile_->close(); - out_.setDevice(0); -} - -void InputEventLog::truncateLogBegin() -{ - paused_=(out_.device() != 0); - stop(); -} - -void InputEventLog::truncateLogEnd() -{ - if(paused_) - { - paused_=false; - start(); - } -} - -bool InputEventLog::eventFilter(QObject *obj, QEvent *event) -{ - if(out_.device()) - { - //out_ << event->type() << " " << obj->objectName() << " " << obj->metaObject()->className() << "\n"; - //out_.flush(); - - if(event->type() == QEvent::MouseButtonPress) - { - logMousePress(obj,static_cast(event)); - } - else if(event->type() == QEvent::MouseButtonRelease) - { - logMouseRelease(obj,static_cast(event)); - } - else if(event->type() == QEvent::Close) - { - logClose(obj,static_cast(event)); - } - else if(event->type() == QEvent::ContextMenu) - { - logContextMenu(obj,static_cast(event)); - } - } - - return QObject::eventFilter(obj,event); -} - -void InputEventLog::logMousePress(QObject* obj,QMouseEvent *event) -{ - QString cn(obj->metaObject()->className()); - if(cn != "QWidgetWindow" && cn != "QMenuBar" && - cn != "QToolBar" && cn != "MainWindow" && cn != "QScrollBar" ) - { - std::string s; - ecf::TimeStamp::now_in_brief(s); - out_ << s.c_str() << "mp " << cn << " " << objectPath(obj); - - if(cn == "QTabBar") - { - if(QTabBar* t=static_cast(obj)) - { - int idx=t->tabAt(event->pos()); - if(idx >=0) - { - out_ << " tab=" << t->tabText(idx); - } - } - } - else if(cn == "QMenu") - { - if(QMenu* m=static_cast(obj)) - { - if(QAction* ac=m->actionAt(event->pos())) - { - out_ << " ac=" << ac->objectName(); - } - } - } - out_ << "\n"; - out_.flush(); - } -} - -void InputEventLog::logMouseRelease(QObject* obj,QMouseEvent *event) -{ - QString cn(obj->metaObject()->className()); - if(cn == "QMenu") - { - std::string s; - ecf::TimeStamp::now_in_brief(s); - out_ << s.c_str() << "mr " << cn << " " << objectPath(obj); - if(QMenu* m=static_cast(obj)) - { - if(QAction* ac=m->actionAt(event->pos())) - { - out_ << " ac=" << ac->objectName(); - } - } - out_ << "\n"; - out_.flush(); - } -} - -void InputEventLog::logClose(QObject* obj,QCloseEvent *event) -{ - QString cn(obj->metaObject()->className()); - if(cn != "QWidgetWindow" && cn != "QTipLabel") - { - std::string s; - ecf::TimeStamp::now_in_brief(s); - out_ << s.c_str() << "cl " << cn << " " << objectPath(obj) << "\n"; - out_.flush(); - } -} - -void InputEventLog::logContextMenu(QObject* obj,QContextMenuEvent *event) -{ - QString cn(obj->metaObject()->className()); - if(cn != "QWidgetWindow") - { - std::string s; - ecf::TimeStamp::now_in_brief(s); - out_ << s.c_str() << "cm " << cn << " " << objectPath(obj) << "\n"; - out_.flush(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/InputEventLog.hpp ecflow-4.11.1/Viewer/src/InputEventLog.hpp --- ecflow-4.9.0/Viewer/src/InputEventLog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/InputEventLog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,55 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef INPUTEVENTLOG_HPP -#define INPUTEVENTLOG_HPP - -#include -#include - -class QCloseEvent; -class QContextMenuEvent; -class QFile; -class QMouseEvent; -class LogTruncator; - -class InputEventLog : public QObject -{ - Q_OBJECT -public: - ~InputEventLog(); - - void start(); - void stop(); - - static InputEventLog* instance(); - -protected Q_SLOTS: - void truncateLogBegin(); - void truncateLogEnd(); - -protected: - InputEventLog(QObject* parent=0); - - bool eventFilter(QObject *obj, QEvent *event); - void logMousePress(QObject* obj,QMouseEvent *e); - void logMouseRelease(QObject* obj,QMouseEvent *e); - void logClose(QObject* obj,QCloseEvent *e); - void logContextMenu(QObject* obj,QContextMenuEvent *e); - - static InputEventLog* instance_; - bool paused_; - QFile *outFile_; - QTextStream out_; - LogTruncator* truncator_; -}; - - -#endif // INPUTEVENTLOG_HPP - diff -Nru ecflow-4.9.0/Viewer/src/JobItemWidget.cpp ecflow-4.11.1/Viewer/src/JobItemWidget.cpp --- ecflow-4.9.0/Viewer/src/JobItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/JobItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "JobItemWidget.hpp" - -#include "Highlighter.hpp" -#include "InfoProvider.hpp" -#include "VConfig.hpp" -#include "VReply.hpp" -#include "VNode.hpp" - -JobItemWidget::JobItemWidget(QWidget *parent) : CodeItemWidget(parent) -{ - messageLabel_->hide(); - messageLabel_->setShowTypeTitle(false); - - //Remove the first spacer item!! - removeSpacer(); - - //The document becomes the owner of the highlighter - new Highlighter(textEdit_->document(),"job"); - - infoProvider_=new JobProvider(this); - - //Editor font - textEdit_->setFontProperty(VConfig::instance()->find("panel.job.font")); -} - -JobItemWidget::~JobItemWidget() -{ -} - -QWidget* JobItemWidget::realWidget() -{ - return this; -} - -void JobItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - info_=info; - messageLabel_->hide(); - - //Info must be a node - if(info_ && info_->isNode() && info_->node()) - { - reloadTb_->setEnabled(false); - infoProvider_->info(info_); - } -} - -void JobItemWidget::clearContents() -{ - InfoPanelItem::clear(); - textEdit_->clear(); - messageLabel_->hide(); - reloadTb_->setEnabled(true); - clearCurrentFileName(); -} - -void JobItemWidget::infoReady(VReply* reply) -{ - Q_ASSERT(reply); - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); - - if(reply->hasWarning()) - { - messageLabel_->showWarning(QString::fromStdString(reply->warningText())); - } - else if(reply->hasInfo()) - { - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); - } - - fileLabel_->update(reply); - reloadTb_->setEnabled(true); - - setCurrentFileName(reply->fileName()); -} - -void JobItemWidget::infoProgress(VReply* reply) -{ - QString s=QString::fromStdString(reply->infoText()); - messageLabel_->showInfo(s); -} - -void JobItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); - messageLabel_->showError(s); - reloadTb_->setEnabled(true); -} - -void JobItemWidget::reloadRequested() -{ - reload(info_); -} - -void JobItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - reloadTb_->setEnabled(false); - } - //Resume - else - { - if(info_ && info_->node()) - { - reloadTb_->setEnabled(true); - } - else - { - clearContents(); - } - } - } -} - -static InfoPanelItemMaker maker1("job"); diff -Nru ecflow-4.9.0/Viewer/src/JobItemWidget.hpp ecflow-4.11.1/Viewer/src/JobItemWidget.hpp --- ecflow-4.9.0/Viewer/src/JobItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/JobItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef JOBITEMWIDGET_HPP_ -#define JOBITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" -#include "CodeItemWidget.hpp" -#include "VInfo.hpp" - -#include "ServerHandler.hpp" - -class JobItemWidget : public CodeItemWidget, public InfoPanelItem -{ -public: - explicit JobItemWidget(QWidget *parent=0); - ~JobItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected: - void updateState(const ChangeFlags&); - void reloadRequested(); -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/LabelEditor.cpp ecflow-4.11.1/Viewer/src/LabelEditor.cpp --- ecflow-4.9.0/Viewer/src/LabelEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LabelEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "LabelEditor.hpp" - -#include - -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "SessionHandler.hpp" - -LabelEditorWidget::LabelEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); - - QLayoutItem *item; - item=grid_->itemAtPosition(1,0); - Q_ASSERT(item); - item->setAlignment(Qt::AlignLeft|Qt::AlignTop); -} - -LabelEditor::LabelEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"label",parent) -{ - w_=new LabelEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "label"); - - if(a->data().count() < 2) - return; - - QString name=a->data().at(1); - QString val; - if(a->data().count() > 2) - val=a->data().at(2); - - oriVal_=val; - - w_->nameLabel_->setText(name); - w_->valueTe_->setPlainText(val); - w_->valueTe_->setFocus(); - - header_->setInfo(QString::fromStdString(info_->path()),"Label"); - - connect(w_->valueTe_,SIGNAL(textChanged()), - this,SLOT(slotValueChanged())); - - checkButtonStatus(); - - readSettings(); -} - -LabelEditor::~LabelEditor() -{ - writeSettings(); -} - -void LabelEditor::apply() -{ - std::string val=w_->valueTe_->toPlainText().toStdString(); - std::string name=w_->nameLabel_->text().toStdString(); - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","label",name,val); - CommandHandler::run(info_,cmd); -} - -void LabelEditor::resetValue() -{ - w_->valueTe_->setPlainText(oriVal_); - checkButtonStatus(); -} - -void LabelEditor::slotValueChanged() -{ - checkButtonStatus(); -} - -bool LabelEditor::isValueChanged() -{ - return (oriVal_ != w_->valueTe_->toPlainText()); -} - -void LabelEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("LabelEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void LabelEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("LabelEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(310,200)); - } - - settings.endGroup(); -} - -static AttributeEditorMaker makerStr("label"); diff -Nru ecflow-4.9.0/Viewer/src/LabelEditor.hpp ecflow-4.11.1/Viewer/src/LabelEditor.hpp --- ecflow-4.9.0/Viewer/src/LabelEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LabelEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef LABELEDITOR_HPP -#define LABELEDITOR_HPP - -#include "ui_LabelEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -class LabelEditor; - -class LabelEditorWidget : public QWidget, protected Ui::LabelEditorWidget -{ -friend class LabelEditor; -public: - LabelEditorWidget(QWidget *parent=0); -}; - -class LabelEditor : public AttributeEditor -{ -Q_OBJECT -public: - LabelEditor(VInfo_ptr,QWidget* parent=0); - ~LabelEditor(); - -protected Q_SLOTS: - void slotValueChanged(); - -protected: - void apply(); - void resetValue(); - bool isValueChanged(); - void readSettings(); - void writeSettings(); - - LabelEditorWidget* w_; - QString oriVal_; -}; - -#endif // LABELEDITOR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/LabelEditorWidget.ui ecflow-4.11.1/Viewer/src/LabelEditorWidget.ui --- ecflow-4.9.0/Viewer/src/LabelEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LabelEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ - - - LabelEditorWidget - - - - 0 - 0 - 329 - 227 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - TextLabel - - - - - - - Value: - - - - - - - - 0 - 1 - - - - - - - - Name: - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/LimitEditor.cpp ecflow-4.11.1/Viewer/src/LimitEditor.cpp --- ecflow-4.9.0/Viewer/src/LimitEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LimitEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,330 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "LimitEditor.hpp" - -#include -#include -#include - -#include "Aspect.hpp" - -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "MainWindow.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VLimitAttr.hpp" -#include "SessionHandler.hpp" - -LimitEditorWidget::LimitEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); - removeTb_->setDefaultAction(actionRemove_); - removeAllTb_->setDefaultAction(actionRemoveAll_); - //pathView_->addAction(actionRemove_); - pathView_->addAction(actionLookUp_); - pathView_->setSelectionMode(QAbstractItemView::ExtendedSelection); - pathView_->setContextMenuPolicy(Qt::ActionsContextMenu); -} - -LimitEditor::LimitEditor(VInfo_ptr info,QWidget* parent) : - AttributeEditor(info,"limit",parent), - model_(0) -{ - w_=new LimitEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "limit"); - QStringList aData=a->data(); - - if(aData.count() < 4) - return; - - QString name=aData[1]; - oriVal_=aData[2].toInt(); - oriMax_=aData[3].toInt(); - - w_->nameLabel_->setText(name); - w_->valueLabel_->setText(QString::number(oriVal_)); - - w_->maxSpin_->setRange(0,10000000); - w_->maxSpin_->setValue(oriMax_); - w_->maxSpin_->setFocus(); - - if(aData[2].isEmpty() || aData[3].isEmpty()) - { - w_->actionRemove_->setEnabled(false); - w_->actionRemoveAll_->setEnabled(false); - return; - } - - buildList(a); - - connect(w_->maxSpin_,SIGNAL(valueChanged(int)), - this,SLOT(slotMaxChanged(int))); - - connect(w_->actionRemove_,SIGNAL(triggered()), - this,SLOT(slotRemove())); - - connect(w_->actionRemoveAll_,SIGNAL(triggered()), - this,SLOT(slotRemoveAll())); - - connect(w_->actionLookUp_,SIGNAL(triggered()), - this,SLOT(slotLookUp())); - - connect(w_->pathView_,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClicked(const QModelIndex&))); - - header_->setInfo(QString::fromStdString(info_->path()),"Limit"); - - checkButtonStatus(); - - readSettings(); - - //No reset button is allowed because we can perform irreversible changes! - doNotUseReset(); -} - -LimitEditor::~LimitEditor() -{ - writeSettings(); -} - -void LimitEditor::buildList(VAttribute *a) -{ - VLimitAttr* lim=static_cast(a); - Q_ASSERT(lim); - - model_=new QStringListModel(this); - w_->pathView_->setModel(model_); - - //Update the model(=node list) - setModelData(lim->paths()); -} - -void LimitEditor::apply() -{ - int intVal=w_->valueLabel_->text().toInt(); - int intMax=w_->maxSpin_->value(); - std::string val=QString::number(intVal).toStdString(); - std::string max=QString::number(intMax).toStdString(); - std::string name=w_->nameLabel_->text().toStdString(); - - std::vector valCmd; - VAttribute::buildAlterCommand(valCmd,"change","limit_value",name,val); - - std::vector maxCmd; - VAttribute::buildAlterCommand(maxCmd,"change","limit_max",name,max); - - if(oriVal_ != intVal && oriMax_ != intMax) - { - if(intVal < oriMax_) - { - CommandHandler::run(info_,valCmd); - CommandHandler::run(info_,maxCmd); - } - else - { - CommandHandler::run(info_,maxCmd); - CommandHandler::run(info_,valCmd); - } - } - else if(oriVal_ != intVal) - { - CommandHandler::run(info_,valCmd); - } - - else if(oriMax_ != intMax) - { - CommandHandler::run(info_,maxCmd); - } -} - -void LimitEditor::resetValue() -{ -} - -void LimitEditor::slotMaxChanged(int) -{ - checkButtonStatus(); -} - -bool LimitEditor::isValueChanged() -{ - return (oriMax_ != w_->maxSpin_->value()); -} - -void LimitEditor::slotRemove() -{ - remove(false); -} - -void LimitEditor::slotRemoveAll() -{ - remove(true); -} - -void LimitEditor::remove(bool all) -{ - if(!info_) - return; - - //We cannot cancel the setting after remove is callled - disableCancel(); - - Q_ASSERT(model_); - - VAttribute* a=info_->attribute(); - Q_ASSERT(a); - VLimitAttr* lim=static_cast(a); - Q_ASSERT(lim); - - if(all) - { - std::vector valCmd; - VAttribute::buildAlterCommand(valCmd,"change","limit_value",a->strName(),"0"); - CommandHandler::run(info_,valCmd); - } - else - { - std::vector paths; - Q_FOREACH(QModelIndex idx,w_->pathView_->selectionModel()->selectedRows()) - { - std::vector valCmd; - VAttribute::buildAlterCommand(valCmd,"delete","limit_path",a->strName(), - model_->data(idx,Qt::DisplayRole).toString().toStdString()); - CommandHandler::run(info_,valCmd); - } - } - - //Updating the gui with the new state will happen later - //because command() is asynchronous -} - -void LimitEditor::nodeChanged(const std::vector& aspect) -{ - bool limitCh=(std::find(aspect.begin(),aspect.end(),ecf::Aspect::LIMIT) != aspect.end()); - if(limitCh && info_) - { - VAttribute* a=info_->attribute(); - Q_ASSERT(a); - VLimitAttr* lim=static_cast(a); - Q_ASSERT(lim); - - QStringList aData=a->data(); - if(aData.count() < 4) - return; - - oriVal_=aData[2].toInt(); - w_->valueLabel_->setText(QString::number(oriVal_)); - - oriMax_=aData[3].toInt(); - w_->maxSpin_->setValue(oriMax_); - - //Update the model (=node list) - setModelData(lim->paths()); - } -} - -void LimitEditor::setModelData(QStringList lst) -{ - Q_ASSERT(model_); - - bool hadData=(modelData_.isEmpty() == false); - modelData_=lst; - model_->setStringList(modelData_); - - if(!modelData_.isEmpty()) - { - if(!hadData) - { - w_->pathView_->setCurrentIndex(model_->index(0,0)); - w_->pathView_->setFocus(Qt::MouseFocusReason); - } - w_->actionRemove_->setEnabled(true); - w_->actionRemoveAll_->setEnabled(true); - } - else - { - w_->actionRemove_->setEnabled(false); - w_->actionRemoveAll_->setEnabled(false); - } -} -//Lookup in tree - -void LimitEditor::slotLookUp() -{ - QModelIndex idx=w_->pathView_->currentIndex(); - lookup(idx); -} - -void LimitEditor::slotDoubleClicked(const QModelIndex &index) -{ - lookup(index); -} - -void LimitEditor::lookup(const QModelIndex &idx) -{ - if(!info_) - return; - - Q_ASSERT(model_); - - std::string nodePath= - model_->data(idx,Qt::DisplayRole).toString().toStdString(); - - VInfo_ptr ni=VInfo::createFromPath(info_->server(),nodePath); - if(ni) - { - MainWindow::lookUpInTree(ni); - } -} - -void LimitEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("LimitEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void LimitEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("LimitEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(420,400)); - } - - settings.endGroup(); -} - -static AttributeEditorMaker makerStr("limit"); diff -Nru ecflow-4.9.0/Viewer/src/LimitEditor.hpp ecflow-4.11.1/Viewer/src/LimitEditor.hpp --- ecflow-4.9.0/Viewer/src/LimitEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LimitEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef LIMITEDITOR_HPP -#define LIMITEDITOR_HPP - -#include "ui_LimitEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -#include - -class LimitEditor; -class QStringListModel; - -class LimitEditorWidget : public QWidget, protected Ui::LimitEditorWidget -{ -friend class LimitEditor; -public: - LimitEditorWidget(QWidget *parent=0); -}; - -class LimitEditor : public AttributeEditor -{ -Q_OBJECT -public: - LimitEditor(VInfo_ptr,QWidget* parent=0); - ~LimitEditor(); - -protected Q_SLOTS: - void slotMaxChanged(int); - void slotRemove(); - void slotRemoveAll(); - void slotLookUp(); - void slotDoubleClicked(const QModelIndex &index); - -protected: - void resetValue(); - void apply(); - bool isValueChanged(); - void buildList(VAttribute *a); - void remove(bool all); - void nodeChanged(const std::vector& a); - void setModelData(QStringList lst); - void lookup(const QModelIndex &index); - void readSettings(); - void writeSettings(); - - LimitEditorWidget* w_; - int oriVal_; - int oriMax_; - QStringListModel* model_; - QStringList modelData_; -}; - -#endif // LIMITEDITOR_HPP - - diff -Nru ecflow-4.9.0/Viewer/src/LimitEditorWidget.ui ecflow-4.11.1/Viewer/src/LimitEditorWidget.ui --- ecflow-4.9.0/Viewer/src/LimitEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LimitEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,169 +0,0 @@ - - - LimitEditorWidget - - - - 0 - 0 - 329 - 227 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Name: - - - - - - - TextLabel - - - - - - - Value: - - - - - - - Maximum: - - - - - - - - - - TextLabel - - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 5 - - - - - - - - - - Nodes consuming the limit: - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Remove - - - Remove - - - Qt::ToolButtonTextBesideIcon - - - - - - - Reset - - - - - - - - - QAbstractItemView::ExtendedSelection - - - - - - - Remove - - - Remove selected node paths - - - - - Reset - - - Remove all node paths and reset the limit value to <b>zero</b> - - - - - &Look up node in tree - - - Look up node in tree - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/LineEdit.cpp ecflow-4.11.1/Viewer/src/LineEdit.cpp --- ecflow-4.9.0/Viewer/src/LineEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LineEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "LineEdit.hpp" - -#include -#include -#include -#include - -LineEdit::LineEdit(QWidget *parent): - QLineEdit(parent), - iconLabel_(0) -{ - clearTb_=new QToolButton(this); - QPixmap pix(":/viewer/clear_left.svg"); - clearTb_->setIcon(pix); - clearTb_->setIconSize(QSize(12,12)); - clearTb_->setAutoRaise(true); - clearTb_->setCursor(Qt::ArrowCursor); - clearTb_->setToolTip(tr("Clear text")); - clearTb_->setObjectName("clearTextTb"); - - clearTb_->setStyleSheet("QToolButton{ border: node; padding: 0px;}"); - - connect(clearTb_,SIGNAL(clicked()), - this,SLOT(slotClear())); - - adjustSize(); - - /*QSize tbSize=clearTb_->sizeHint(); - - int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(tbSize.width()+frame+1)); - QSize minSize = minimumSizeHint(); - setMinimumSize(qMax(minSize.width(),tbSize.height()+frame*2+2), - qMax(minSize.height(),tbSize.height()+frame*2+2));*/ - -} - -void LineEdit::setDecoration(QPixmap pix) -{ - if(!iconLabel_) - iconLabel_=new QLabel(this); - - iconLabel_->setPixmap(pix); - iconLabel_->setProperty("lineEdit","true"); - - adjustSize(); - - /*QSize icSize=iconLabel_->sizeHint(); - QSize tbSize=clearTb_->sizeHint(); - - int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(icSize.width()+frame+1)); - QSize minSize = minimumSizeHint(); - setMinimumSize(qMax(minSize.width(),icSize.width()+tbSize.width()+frame*2+2), - qMax(minSize.height(),tbSize.height()+frame*2+2));*/ -} - - -void LineEdit::adjustSize() -{ - int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - QSize tbSize=clearTb_->sizeHint(); - setStyleSheet(QString("QLineEdit { padding-right: %1px; } ").arg(tbSize.width()+frame+1)); - - int leftWidth=0; - if(iconLabel_) - { - QSize icSize=iconLabel_->sizeHint(); - setStyleSheet(QString("QLineEdit { padding-left: %1px; } ").arg(icSize.width()+frame+1)); - leftWidth=+icSize.width(); - } - QSize minSize = minimumSizeHint(); - setMinimumSize(qMax(minSize.width(),leftWidth+tbSize.width()+frame*2+2), - qMax(minSize.height(),tbSize.height()+frame*2+2)); -} - - -void LineEdit::resizeEvent(QResizeEvent *) -{ - int frame = style()->pixelMetric(QStyle::PM_DefaultFrameWidth); - - QSize tbSize = clearTb_->sizeHint(); - clearTb_->move(rect().right()-frame-tbSize.width(), - (rect().bottom()+ 1-tbSize.height())/2); - - if(iconLabel_) - { - QSize icSize=iconLabel_->sizeHint(); - iconLabel_->move(rect().left()+frame+1, - (rect().bottom()+ 1-icSize.height())/2); - } -} - - -void LineEdit::slotClear() -{ - clear(); - Q_EMIT textCleared(); -} - diff -Nru ecflow-4.9.0/Viewer/src/LineEdit.hpp ecflow-4.11.1/Viewer/src/LineEdit.hpp --- ecflow-4.9.0/Viewer/src/LineEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LineEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef LINEEDIT_INC_ -#define LINEEDIT_INC_ - -#include - -class QLabel; -class QToolButton; - -class LineEdit : public QLineEdit -{ -Q_OBJECT - -public: - explicit LineEdit (QWidget *parent=0); - void setDecoration(QPixmap); - -public Q_SLOTS: - void slotClear(); - -Q_SIGNALS: - void textCleared(); - -protected: - void adjustSize(); - void resizeEvent(QResizeEvent*); - - QToolButton *clearTb_; - QLabel* iconLabel_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/LogEvent.cpp ecflow-4.11.1/Viewer/src/LogEvent.cpp --- ecflow-4.9.0/Viewer/src/LogEvent.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogEvent.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,315 +0,0 @@ -// #define MAIN - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/* - -shell = new QProcess(this); -QStringList argv() << myfile.fileName(); -shell.start("./src/unzip",argv); // or in win32 - src/unzip.exe -*/ - -#include -// foreach(QString x, strings) QTextStream(stdout) << x << endl; -#include "LogEvent.hpp" - -const int boxSize = 3; -// namespace status { -const QVector status (QVector() << "unknown" - << "suspended" - << "complete" - << "queued" - << "submitted" - << "active" - << "aborted" - << "shutdown" - << "halted" - << "event" - << "meter" - << "label" - ); -// } -static EventSorter *sorter = 0x0; - -counted::counted() - : count_(0) -{} - -counted::~counted() -{} - -void counted::attach() -{ ++count_; } - -void counted::detach() -{ if (--count_==0) delete this; } - -static int compare(const void*a, const void*b) { - LogEvent** pa = (LogEvent**)a; - LogEvent** pb = (LogEvent**)b; - return sorter->compare(*pa, *pb); -} - -bool lessThan( const LogEvent* le1, const LogEvent *le2 ) -{ - if (le1 && le2) return le1->time() < le2->time(); - return true; -} - -class LogCache // : public QArray -{ -public: - QVector add; - void reset() { int c = add.size(); while (c) { add[--c]->detach(); } add.clear(); } - void sort() { qSort(add.begin(), add.end(), lessThan); } - ~LogCache() {} -}; - -static LogCache cache; -static QString cached; - -LogEvent::LogEvent(const QString& path, const QDateTime& time) - : time_(time) - , path_(path) -{ - attach(); - cache.add.append(this); - // observe -} - -LogEvent::~LogEvent() -{} - -int LogEvent::load(bool reset) -{ - if (reset) { - cache.reset(); - cached = QString(); - } - return 0; -} - -int LogEvent::sort(EventSorter& s) -{ - sorter = &s; - cache.sort(); - sorter = 0x0; - return 0; -} - -int LogEvent::scan(const QString& path, EventLister&l) -{ - int i = cache.add.size(); - while (i--) { - if (cache.add[i]->path_.indexOf(path) != -1) - l.next(cache.add[i]); - } - return 0; -} - -int LogEvent::find(const QString&path) { return 0; } - -/********************************/ - -class StatusEvent: public LogEvent { - QString status_; - virtual bool start() { return status_ == "submitted"; } - virtual bool end() { return status_ == "complete"; } - virtual const QString& text(QString&) { return path_;} - virtual QString status() { return status_; } -public: - StatusEvent(const QString&path, const QDateTime& time, const QString& status) - : LogEvent(path, time) - , status_ (status) - {} -}; - -class EventEvent: public LogEvent { - bool set_; -public: - virtual QString status() { return "event"; } - EventEvent(const QString&path, const QDateTime& time, bool b) - : LogEvent(path, time) - , set_ (b) - {} -}; - -class MeterEvent: public LogEvent { - int step_; -public: - virtual QString status() { return "meter"; } - MeterEvent(const QString&path, const QDateTime& time, int step) - : LogEvent(path, time) - , step_ (step) - {} -}; - -/********************************/ -QString logs = "/media/map/boxster/map/ecm/201502/ect/tmp/map/work/p4/metapps/suites/o/def"; -QString logf1 = logs + "/timeline/ibis.900130.log"; -QString logf2 = logs + "/tkinter/logs/vsms1.ecf.3.log"; -typedef QMap > log_map_type; -log_map_type log_map; -QDateTime dt_min(QDateTime::fromString("00:00:00 1.1.2101", "hh:mm:ss d.M.yyyy")); -QDateTime dt_max(QDateTime::fromString("00:00:00 1.1.2001", "hh:mm:ss d.M.yyyy")); - -void reader(QString filename, - bool onlyTask=true, - int max=-1, - int debug=0 ) { - - QFile inputFile(filename); - log_map_type& rc = log_map; - - if (inputFile.open(QIODevice::ReadOnly)) { - QTextStream in(&inputFile); - QTextStream out (stdout); - int num = 0; - QString pkind (""), ppath(""); - while (!in.atEnd()) { - QString line = in.readLine(); - int pos (line.indexOf("LOG:")), - sql (line.indexOf("[")), - sqr (line.indexOf("]")); - if (pos == -1 || sqr == -1) continue; - QString time(line.left(sqr).mid(sql+1)); - QString log(line.mid(sqr+1).trimmed()); - QDateTime dt(QDateTime::fromString(time, "hh:mm:ss d.M.yyyy")); - if (dt < dt_min) dt_min = dt; - if (dt > dt_max) dt_max = dt; - - int ikd(log.indexOf(":")), - isp(log.indexOf(" ")); - QString kind (log.left(ikd)), - path(log.mid(ikd+1).trimmed()); - - if (onlyTask && ppath.indexOf(path) == 0) continue; - if (kind=="meter") - rc[path].append(new MeterEvent(path, dt, 1)); - else if (kind=="event") - rc[path].append(new EventEvent(path, dt, 1)); - else - rc[path].append(new StatusEvent(path, dt, kind)); - - pkind = kind; - ppath = path.split(" ")[0]; - if (debug) { - out << time << " " << dt.toString() - // << endl << log << endl << line << endl; - << " " << kind << " " << path.trimmed() << endl; - if (max > 0 && num > max) break; - ++num; - } - } - inputFile.close(); - } - // return rc; -} - -#ifdef MAIN_LOG -/* -make VERBOSE=1 -*/ -#include -#include "TimeItemWidget.hpp" - -int drawScene(TimeItemWidget* qwd) -{ - QGraphicsScene* scene = new QGraphicsScene(); - QMap colors; - colors.insert("submitted", Qt::blue); - colors["complete"] = Qt::yellow; - colors["aborted"] = Qt::red; - colors["active"] = Qt::green; - colors["meter"] = Qt::blue; - colors["event"] = Qt::black; - colors["unknown"] = Qt::gray; - // colors["unknown"] = Qt::grey; - - qwd->view()->setScene(scene); - if (1) { //Populate the scene - for(int x = 0; x < 8400; x = x + 25) { - for(int y = 0; y < 100; y = y + 25) { - if(x % 100 == 0 && y % 100 == 0) { - scene->addRect(x, y, 2, 2); - QString pointString; - QTextStream stream(&pointString); - if (0) stream << "(" << x << "," << y << ")"; - QGraphicsTextItem* item = scene->addText(pointString); - item->setPos(x, y); - } else { - scene->addRect(x, y, 1, 1); - } - } - } - } - /* ./work/sms/qt4/qt-book/chap08/diagram - /mnt/wdir/map/work/qt-5-4-1 */ - int num = 0, inc = 10; - log_map_type::const_iterator mit = log_map.begin(); - QVector::const_iterator lit; - while (mit != log_map.end()) { - num += inc; - QString line; - QTextStream stream(&line); - stream << mit.key(); - QGraphicsTextItem* item = scene->addText(line); - item->setPos(-100, num); - // scene->addText(mit.key()); - lit = mit.value().begin(); - while (lit != mit.value().end()) { - int yy = ((*lit)->time().toTime_t() - - // kSmallDate.toTime_t()) / 10; - dt_min.toTime_t()) / 10; - - /* QPixmap pm(5, 5); - pm.fill(); - QPainter p(&pm); - p.setRenderHint(QPainter::Antialiasing, true); - p.setPen(colors[(*lit)->status()]); - p.setBrush(QBrush(colors[(*lit)->status()])); */ - // QGraphicsEllipseItem *ell = p.drawEllipse(yy, num, 5, 5); - - stream << yy << "\n"; - QGraphicsEllipseItem *ell = scene->addEllipse(yy, num, 10, 10, - QPen(colors[(*lit)->status()]), - QBrush(colors[(*lit)->status()])); - ell->setPos(yy, num); - - ++lit; - } - ++mit; - } -} - -int main(int argc, char* argv[]) -{ - // Q_INIT_RESOURCE(images); - reader(logf1, 1, 500, 1); - reader(logf2, 1, 500, 1); - - QApplication app(argc, argv); - TimeItemWidget window(0x0); - window.setWindowTitle("TimeLine"); - window.show(); - log_map.clear(); - reader(logf2, 1); - drawScene(&window); - return app.exec(); -} -#endif diff -Nru ecflow-4.9.0/Viewer/src/LogEvent.hpp ecflow-4.11.1/Viewer/src/LogEvent.hpp --- ecflow-4.9.0/Viewer/src/LogEvent.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogEvent.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -#ifndef LogEvent_H -#define LogEvent_H - -class LogEvent; -class EventLister { -public: - virtual void next(LogEvent*) = 0; -}; - - -class EventSorter { -public: - virtual int compare(LogEvent*, LogEvent*) = 0; -}; - -// inline bool operator<(const DateTime& -const QDateTime kSmallDate(QDate(1900, 1, 1), QTime()); -const QDateTime kLargeDate(QDate(2100, 1, 1), QTime()); - -class counted -{ -public: - counted(); - void attach(); - void detach(); -protected: - ~counted(); -private: - counted(const counted&); - counted& operator=(const counted&); - int count_; -}; - -class LogEvent: public counted // , public observer -{ -public: - LogEvent(const QString&, const QDateTime&); - const QDateTime& time() const { return time_; } - virtual bool start() { return false; } - virtual bool end() { return false; } - virtual const QString& get_node() { return path_; } - virtual QString status() { return "none"; } - - static int load(bool reset); - static int scan(const QString&, EventLister&); - static int sort(EventSorter&); - static int find(const QString&); - static int compare(const LogEvent*, const LogEvent*); - virtual ~LogEvent(); - -protected: - QDateTime time_; - QString path_; -private: - LogEvent(const LogEvent&); - LogEvent& operator=(const LogEvent&); - // notification - // adoption - // gone -}; -#endif diff -Nru ecflow-4.9.0/Viewer/src/LogModel.cpp ecflow-4.11.1/Viewer/src/LogModel.cpp --- ecflow-4.9.0/Viewer/src/LogModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,401 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "LogModel.hpp" - -#include -#include - -#include "IconProvider.hpp" - -LogModelLine::LogModelLine(QString s) : type_(NoType) -{ - QString s1=s.section("]",0,0); - date_=s1.section("[",1); - entry_=s.section("]",1); - - QString t=s1.section("[",0); - - if(t.contains("MSG:")) - { - type_=MessageType; - } - else if(t.contains("LOG:")) - { - type_=LogType; - } - else if(t.contains("ERR:")) - { - type_=ErrorType; - } - else if(t.contains("WAR:")) - { - type_=WarningType; - } - else if(t.contains("DBG:")) - { - type_=DebugType; - } -} - - -LogModel::LogModel(QObject *parent) : - QAbstractItemModel(parent) -{ - IconProvider::add(":/viewer/log_error.svg","log_error"); - IconProvider::add(":/viewer/log_info.svg","log_info"); - IconProvider::add(":/viewer/log_warning.svg","log_warning"); -} - -LogModel::~LogModel() -{ -} - -void LogModel::setData(const std::string& data) -{ - beginResetModel(); - - data_.clear(); - - QString in=QString::fromStdString(data); - Q_FOREACH(QString s,in.split("\n")) - { - if(!s.simplified().isEmpty()) - data_ << LogModelLine(s); - } - - endResetModel(); -} - -void LogModel::setData(const std::vector& data) -{ - beginResetModel(); - - data_.clear(); - - for(std::vector::const_iterator it=data.begin(); it != data.end(); ++it) - { - QString s=QString::fromStdString(*it); - if(!s.simplified().isEmpty()) - { - data_ << LogModelLine(s); - } - } - endResetModel(); -} - -void LogModel::appendData(const std::vector& data) -{ - int num=0; - - for(std::vector::const_iterator it=data.begin(); it != data.end(); ++it) - { - QString s=QString::fromStdString(*it); - if(!s.simplified().isEmpty()) - { - num++; - } - } - - if(num >0) - { - beginInsertRows(QModelIndex(),rowCount(),rowCount()+num-1); - - for(std::vector::const_iterator it=data.begin(); it != data.end(); ++it) - { - QString s=QString::fromStdString(*it); - if(!s.simplified().isEmpty()) - { - data_ << LogModelLine(s); - } - } - - endInsertRows(); - } -} - - - -void LogModel::clearData() -{ - beginResetModel(); - data_.clear(); - endResetModel(); -} - -bool LogModel::hasData() const -{ - return !data_.empty(); -} - -int LogModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 3; -} - -int LogModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return data_.size(); - } - - return 0; -} - -Qt::ItemFlags LogModel::flags ( const QModelIndex & index) const -{ - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -QVariant LogModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData()) - { - return QVariant(); - } - int row=index.row(); - if(row < 0 || row >= data_.size()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - switch(index.column()) - { - case 0: - { - switch(data_.at(row).type_) - { - case LogModelLine::MessageType: - return "MSG "; - case LogModelLine::LogType: - return "LOG "; - case LogModelLine::ErrorType: - return "ERR "; - case LogModelLine::WarningType: - return "WAR "; - case LogModelLine::DebugType: - return "DBG "; - default: - return QVariant(); - } - } - break; - case 1: - return data_.at(row).date_; - break; - case 2: - return data_.at(row).entry_; - break; - default: - break; - } - } - - else if(role == Qt::DecorationRole) - { - if(index.column() ==0) - { - switch(data_.at(row).type_) - { - case LogModelLine::MessageType: - return IconProvider::pixmap("log_info",12); - case LogModelLine::LogType: - return IconProvider::pixmap("log_info",12); - case LogModelLine::ErrorType: - return IconProvider::pixmap("log_error",12); - case LogModelLine::WarningType: - return IconProvider::pixmap("log_warning",12); - default: - return QVariant(); - } - } - } -/* - else if(role == Qt::BackgroundRole) - { - if(data_.at(row).type_ == LogModelLine::ErrorType) - { - return QColor(223,152,152); - } - } -*/ - - else if(role == Qt::FontRole) - { - QFont f; - - /*if(data_.at(row).type_ == LogModelLine::ErrorType) - { - f.setBold(true); - }*/ - - return f; - } - - else if(role == Qt::ToolTipRole) - { - QString txt="Type: "; - switch(data_.at(row).type_) - { - case LogModelLine::MessageType: - txt +="MSG "; - break; - case LogModelLine::LogType: - txt +="LOG "; - break; - case LogModelLine::ErrorType: - txt +="ERR "; - break; - case LogModelLine::WarningType: - txt +="WAR "; - break; - case LogModelLine::DebugType: - txt +="DBG "; - default: - break; - } - - txt+="
        Date: " + data_.at(row).date_; - txt+="
        Entry: " + data_.at(row).entry_; - return txt; - } - - /*else if(role == Qt::SizeHintRole) - { - QFont f; - f.setBold(true); - QFontMetrics fm(f); - return fm.height()+10; - }*/ - - - return QVariant(); -} - -QVariant LogModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) - return QAbstractItemModel::headerData( section, orient, role ); - - if(role == Qt::DisplayRole) - { - switch ( section ) - { - case 0: return tr("Type"); - case 1: return tr("Date"); - case 2: return tr("Entry"); - default: return QVariant(); - } - } - else if(role== Qt::ToolTipRole) - { - switch ( section ) - { - case 0: return tr("Type"); - case 1: return tr("Date"); - case 2: return tr("Entry"); - default: return QVariant(); - } - } - return QVariant(); -} - -QModelIndex LogModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex LogModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - -QModelIndex LogModel::lastIndex() const -{ - return index(rowCount()-1,0); -} - -QString LogModel::entryText(const QModelIndex &idx) const -{ - return data(index(idx.row(),2)).toString(); -} - -QString LogModel::fullText(const QModelIndex &idx) const -{ - return data(index(idx.row(),0)).toString() + " " + - data(index(idx.row(),1)).toString() + " " + - data(index(idx.row(),2)).toString(); -} - -//======================================================== -// -// LogDelegate -// -//======================================================== - -LogDelegate::LogDelegate(QWidget *parent) : QStyledItemDelegate(parent) -{ - -} - -void LogDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ - QStyledItemDelegate::paint(painter,option,index); - - /*if(index.column()==11) - { - QStyleOptionViewItem vopt(option); - initStyleOption(&vopt, index); - - const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); - const QWidget* widget = vopt.widget; - - QString text=index.data(Qt::DisplayRole).toString(); - QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt, widget); - if(text == "ERR") - { - QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt, widget); - } - - painter->fillRect(textRect,Qt::red); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - } - else - { - QStyledItemDelegate::paint(painter,option,index); - }*/ -} - - -QSize LogDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const -{ - QSize size=QStyledItemDelegate::sizeHint(option,index); - - size+=QSize(0,2); - - return size; -} diff -Nru ecflow-4.9.0/Viewer/src/LogModel.hpp ecflow-4.11.1/Viewer/src/LogModel.hpp --- ecflow-4.9.0/Viewer/src/LogModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef LOGMODEL_H -#define LOGMODEL_H - -#include -#include -#include - -#include - -class LogModelLine -{ -public: - explicit LogModelLine(QString); - - enum Type {NoType,MessageType,ErrorType,LogType,WarningType,DebugType}; - - QString date_; - QString entry_; - Type type_; -}; - - -class LogModel : public QAbstractItemModel -{ -public: - explicit LogModel(QObject *parent=0); - ~LogModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - void setData(const std::string&); - void setData(const std::vector&); - void appendData(const std::vector&); - bool hasData() const; - void clearData(); - QModelIndex lastIndex() const; - - QString entryText(const QModelIndex&) const; - QString fullText(const QModelIndex&) const; - -protected: - QList data_; -}; - -class LogDelegate : public QStyledItemDelegate -{ -public: - explicit LogDelegate(QWidget *parent=0); - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - -}; - - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/LogProvider.cpp ecflow-4.11.1/Viewer/src/LogProvider.cpp --- ecflow-4.9.0/Viewer/src/LogProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,197 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include - -#include -#include -#include -#include "LogProvider.hpp" - -#include "FileWatcher.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "ServerHandler.hpp" -#include "File.hpp" - - -LogProvider::LogProvider(InfoPresenter* owner,QObject* parent) : - QObject(parent), - InfoProvider(owner,VTask::HistoryTask), - fileWatcher_(0) -{ - //NOTE: fileWatcher_'s parent (if it exits) will be "this", so - //fileWatcher_ will be automatically deleted in the destructor! -} - -void LogProvider::clear() -{ - stopWatchFile(); - InfoProvider::clear(); -} - -void LogProvider::setAutoUpdate(bool autoUpdate) -{ - InfoProvider::setAutoUpdate(autoUpdate); - - if(active_) - { - if(!autoUpdate_) - { - stopWatchFile(); - } - else - { - if(!inAutoUpdate_) - fetchFile(); - } - } - else - { - stopWatchFile(); - } -} - -void LogProvider::visit(VInfoServer* info) -{ - fetchFile(); -} - -void LogProvider::fetchFile() -{ - if(!active_) - return; - - stopWatchFile(); - - //Reset the reply - reply_->reset(); - - if(!info_) - { - owner_->infoFailed(reply_); - return; - } - - ServerHandler* server=info_->server(); - - //Get the filename - std::string fileName=server->vRoot()->genVariable("ECF_LOG"); - - fetchFile(server,fileName); -} - -void LogProvider::fetchFile(ServerHandler *server,const std::string& fileName) -{ - if(!server) - { - owner_->infoFailed(reply_); - return; - } - - //Set the filename in reply - reply_->fileName(fileName); - - //No filename is available - if(fileName.empty()) - { - reply_->setErrorText("Variable ECF_LOG is not defined!"); - owner_->infoFailed(reply_); - } - - //First we try to read the file directly from the disk - //if(server->readFromDisk()) - { - size_t file_size = 0; - std::string err_msg; - reply_->text( ecf::File::get_last_n_lines(fileName,100,file_size,err_msg)); - if(err_msg.empty()) - { - reply_->fileReadMode(VReply::LocalReadMode); - - if(autoUpdate_) - inAutoUpdate_=true; - - owner_->infoReady(reply_); - - //Try to track the changes in the log file - watchFile(fileName,file_size); - return; - } - } - - //Finally we try the server - reply_->fileReadMode(VReply::ServerReadMode); - - //Define a task for getting the info from the server. - task_=VTask::create(taskType_,server->vRoot(),this); - - //Run the task in the server. When it finish taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - server->run(task_); - -#if 0 - //If we are we could not get the file - //owner_->infoFailed(reply_); -#endif -} - -void LogProvider::watchFile(const std::string& fileName,size_t offset) -{ - if(autoUpdate_) - { - assert(fileWatcher_ == 0); - fileWatcher_=new FileWatcher(fileName,offset,this); - - connect(fileWatcher_,SIGNAL(linesAppended(QStringList)), - this,SLOT(slotLinesAppend(QStringList))); - - inAutoUpdate_=true; - } -} - -void LogProvider::stopWatchFile() -{ - if(fileWatcher_) - { - delete fileWatcher_; - fileWatcher_=0; - } - - inAutoUpdate_=false; -} - -void LogProvider::slotLinesAppend(QStringList lst) -{ - //Check if the task is already running - if(task_) - { - task_->status(VTask::CANCELLED); - task_.reset(); - } - - //Reset the reply - reply_->reset(); - - if(!info_) - { - owner_->infoFailed(reply_); - } - - std::vector vec; - Q_FOREACH(QString s,lst) - { - vec.push_back(s.toStdString()); - } - - reply_->setTextVec(vec); - owner_->infoAppended(reply_); -} - diff -Nru ecflow-4.9.0/Viewer/src/LogProvider.hpp ecflow-4.11.1/Viewer/src/LogProvider.hpp --- ecflow-4.9.0/Viewer/src/LogProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - - -#ifndef VIEWER_SRC_LOGPROVIDER_HPP_ -#define VIEWER_SRC_LOGPROVIDER_HPP_ - -#include -#include - -#include "VDir.hpp" -#include "VInfo.hpp" -#include "InfoProvider.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -class FileWatcher; - -class LogProvider : public QObject, public InfoProvider -{ - Q_OBJECT - -public: - LogProvider(InfoPresenter* owner,QObject* parent=0); - - void visit(VInfoServer*); - void clear(); - void setAutoUpdate(bool); - - public Q_SLOTS: - void slotLinesAppend(QStringList); - - private: - void fetchFile(); - void fetchFile(ServerHandler *server,const std::string& fileName); - void watchFile(const std::string&,size_t); - void stopWatchFile(); - - FileWatcher* fileWatcher_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/LogTruncator.cpp ecflow-4.11.1/Viewer/src/LogTruncator.cpp --- ecflow-4.9.0/Viewer/src/LogTruncator.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogTruncator.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "LogTruncator.hpp" - -#include -#include -#include -#include - -#include "DirectoryHandler.hpp" -#include "UiLog.hpp" - -LogTruncator::LogTruncator(QString path, int timeout,int sizeLimit,int lineNum,QObject* parent) : - QObject(parent), - path_(path), - timeout_(timeout), - sizeLimit_(sizeLimit), - lineNum_(lineNum) -{ - timer_=new QTimer(this); - connect(timer_,SIGNAL(timeout()),this,SLOT(truncate())); -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - int secToM=86400-QTime::currentTime().msecsSinceStartOfDay()/1000; -#else - int secToM=86400-QTime(0,0).secsTo(QTime::currentTime()); -#endif - if(secToM > 5*60) - timer_->start(secToM*1000); - else - timer_->start(secToM*1000+timeout_); - - UiLog().dbg() << "LogTruncator --> secs to midnight=" << secToM << "s" << - " initial timeout=" << timer_->interval()/1000 << "s"; -} - -void LogTruncator::truncate() -{ - //The log file might not exist. It is not necessarily an error because - //the startup script decides on whether the main log goes into the stdout or into - //a file. - QFileInfo info(path_); - if(!info.exists()) - return; - - Q_EMIT truncateBegin(); - - qint64 fSize=info.size(); - if(fSize > sizeLimit_) - { - std::string errStr; - DirectoryHandler::truncateFile(path_.toStdString(), - lineNum_,errStr); - - info.refresh(); - UiLog().info() << "LogTruncator::truncate --> truncated from " << fSize << - " bytes to " << info.size() << " bytes; log file: " << path_; - } - - Q_EMIT truncateEnd(); - - if(timeout_ != timer_->interval()) - timer_->setInterval(timeout_); -} diff -Nru ecflow-4.9.0/Viewer/src/LogTruncator.hpp ecflow-4.11.1/Viewer/src/LogTruncator.hpp --- ecflow-4.9.0/Viewer/src/LogTruncator.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/LogTruncator.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef LOGTRUNCATOR_HPP -#define LOGTRUNCATOR_HPP - -#include -#include - -class QTimer; - -class LogTruncator : public QObject -{ - Q_OBJECT -public: - LogTruncator(QString, int,int,int,QObject* parent=0); - -protected Q_SLOTS: - void truncate(); - -Q_SIGNALS: - void truncateBegin(); - void truncateEnd(); - -private: - QString path_; - QTimer* timer_; - int timeout_; - int sizeLimit_; - int lineNum_; -}; - -#endif // LOGTRUNCATOR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/MainWindow.cpp ecflow-4.11.1/Viewer/src/MainWindow.cpp --- ecflow-4.9.0/Viewer/src/MainWindow.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MainWindow.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,910 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "MainWindow.hpp" - -#include "AboutDialog.hpp" -#include "ChangeNotify.hpp" -#include "ChangeNotifyWidget.hpp" -#include "ClockWidget.hpp" -#include "FilterWidget.hpp" -#include "InfoPanel.hpp" -#include "InfoPanelHandler.hpp" -#include "MenuConfigDialog.hpp" -#include "NodePathWidget.hpp" -#include "NodePanel.hpp" -#include "PropertyDialog.hpp" -#include "ServerComInfoWidget.hpp" -#include "ServerHandler.hpp" -#include "ServerList.hpp" -#include "ServerListDialog.hpp" -#include "ServerListSyncWidget.hpp" -#include "SessionHandler.hpp" -#include "SaveSessionAsDialog.hpp" -#include "CommandOutputDialog.hpp" -#include "TextFormat.hpp" -#include "UiLog.hpp" -#include "VConfig.hpp" -#include "VIcon.hpp" -#include "VSettings.hpp" -#include "Version.hpp" -#include "WidgetNameProvider.hpp" - -#include -#include - -bool MainWindow::quitStarted_=false; -QList MainWindow::windows_; -int MainWindow::maxWindowNum_=25; - -MainWindow::MainWindow(QStringList idLst,QWidget *parent) : - QMainWindow(parent), - serverSyncNotifyTb_(0) -{ - setupUi(this); - - //Assigns name to each object - setObjectName("win_" + QString::number(windows_.count())); - - setAttribute(Qt::WA_DeleteOnClose); - - //the window title - winTitle_=new MainWindowTitleHandler(this); - winTitle_->update(); - - //Create the main layout - QVBoxLayout* layout=new QVBoxLayout(); - layout->setContentsMargins(0,0,0,0); - QWidget *w=new QWidget(this); - w->setObjectName("c"); - w->setLayout(layout); - setCentralWidget(w); - - //Servers menu menu - serverFilterMenu_=new ServerFilterMenu(menuServer); - - //Create a node panel - nodePanel_=new NodePanel(this); - layout->addWidget(nodePanel_); - - connect(nodePanel_,SIGNAL(currentWidgetChanged()), - this,SLOT(slotCurrentChangedInPanel())); - - connect(nodePanel_,SIGNAL(selectionChangedInCurrent(VInfo_ptr)), - this,SLOT(slotSelectionChanged(VInfo_ptr))); - - connect(nodePanel_,SIGNAL(contentsChanged()), - this,SLOT(slotContentsChanged())); - - //-------------- - // Toolbar - //-------------- - - //Add server refresh widget to the front of the toolbar - serverComWidget_=new ServerRefreshInfoWidget(actionRefreshSelected,this); - Q_ASSERT(actionSearch); - viewToolBar->insertWidget(actionSearch,serverComWidget_); - //viewToolBar->addWidget(serverComWidget_); - - connect(serverComWidget_,SIGNAL(serverSettingsEditRequested(ServerHandler*)), - this,SLOT(slotEditServerSettings(ServerHandler*))); - - - //insert a spacer after the the server refresh widget - QWidget* spacer = new QWidget(); - spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - //viewToolBar->insertWidget(actionSearch,spacer); - viewToolBar->addWidget(spacer); - - //Add more actions - addInfoPanelActions(viewToolBar); - - //Add shortcuts to action tooltips - Viewer::addShortCutToToolTip(viewToolBar->actions()); - - //Initialise actions based on selection - actionRefreshSelected->setEnabled(false); - actionResetSelected->setEnabled(false); - - //-------------- - //Status bar - //-------------- - - //Add server list sync notification - if(ServerList::instance()->hasSyncChange()) - { - //Add server list sync notification - serverSyncNotifyTb_=new QToolButton(this); - serverSyncNotifyTb_->setAutoRaise(true); - serverSyncNotifyTb_->setToolButtonStyle(Qt::ToolButtonTextBesideIcon); - serverSyncNotifyTb_->setIcon(QPixmap(":/viewer/info.svg")); - serverSyncNotifyTb_->setText("Server list updated"); - serverSyncNotifyTb_->setToolTip("Your local copy of the system server list was updated. Click to see the changes."); - statusBar()->addWidget(serverSyncNotifyTb_); - - connect(serverSyncNotifyTb_,SIGNAL(clicked(bool)), - this,SLOT(slotServerSyncNotify(bool))); - } - - //Add notification widget - ChangeNotifyWidget* chw=new ChangeNotifyWidget(this); - statusBar()->addPermanentWidget(chw); - - //Add clock widget - clockWidget_=new ClockWidget(this); - statusBar()->addPermanentWidget(clockWidget_); - - //serverComWidget_=new ServerComLineDisplay(this); - //statusBar()->addPermanentWidget(serverComWidget_); - - //Assigns name to each object - WidgetNameProvider::nameChildren(this); - - //actionSearch->setVisible(false); -} - -MainWindow::~MainWindow() -{ - UiLog().dbg() << "MainWindow --> desctutor"; - delete winTitle_; - serverFilterMenu_->aboutToDestroy(); -} - -void MainWindow::init(MainWindow *win) -{ - nodePanel_->init(); - - if(!win) - return; -} - -void MainWindow::addInfoPanelActions(QToolBar *toolbar) -{ - for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); - it != InfoPanelHandler::instance()->panels().end(); ++it) - { - if((*it)->show().find("toolbar") != std::string::npos) - { - QAction *ac=toolbar->addAction(QString::fromStdString((*it)->label())); - QPixmap pix(":/viewer/" + QString::fromStdString((*it)->icon())); - ac->setObjectName(QString::fromStdString((*it)->label())); - ac->setIcon(QIcon(pix)); - ac->setData(QString::fromStdString((*it)->name())); - ac->setToolTip(QString::fromStdString((*it)->buttonTooltip())); - - connect(ac,SIGNAL(triggered()), - this,SLOT(slotOpenInfoPanel())); - - infoPanelActions_ << ac; - } - } -} - -ServerHandler* MainWindow::selectedServer() const -{ - return(selection_)?(selection_->server()):0; -} - -//============================================================== -// -// File menu -// -//============================================================== - -void MainWindow::on_actionNewTab_triggered() -{ - nodePanel_->slotNewTab(); -} - -void MainWindow::on_actionNewWindow_triggered() -{ - MainWindow::openWindow("",this); -} - -void MainWindow::on_actionClose_triggered() -{ - close(); -} - -void MainWindow::on_actionQuit_triggered() -{ - MainWindow::aboutToQuit(this); -} - -void MainWindow::on_actionRefresh_triggered() -{ - nodePanel_->refreshCurrent(); -} - -void MainWindow::on_actionReset_triggered() -{ - nodePanel_->resetCurrent(); -} - -void MainWindow::on_actionRefreshSelected_triggered() -{ - if(selection_ && selection_.get()) - { - if(ServerHandler* s=selection_->server()) - { - s->refresh(); - } - } -} - -void MainWindow::on_actionResetSelected_triggered() -{ - if(selection_ && selection_.get()) - { - if(ServerHandler* s=selection_->server()) - { - s->reset(); - } - } -} - -void MainWindow::on_actionPreferences_triggered() -{ - startPreferences(this,""); -} - -void MainWindow::on_actionManageSessions_triggered() -{ - QMessageBox::information(0, tr("Manage Sessions"), - tr("To manage sessions, please restart ecFlowUI with the -s command-line option")); -} - -void MainWindow::slotConfigChanged() -{ - configChanged(this); -} - -void MainWindow::on_actionConfigureNodeMenu_triggered() -{ - MenuConfigDialog menuConfigDialog; - - if(menuConfigDialog.exec() == QDialog::Accepted) - { - } -} - -void MainWindow::on_actionSearch_triggered() -{ - //It takes ownership of the dialogue. - nodePanel_->addSearchDialog(); -} - -void MainWindow::on_actionNotification_triggered() -{ - ChangeNotify::showDialog(); -} - -void MainWindow::on_actionCommandOutput_triggered() -{ - CommandOutputDialog::showDialog(); -} - -void MainWindow::on_actionManageServers_triggered() -{ - ServerListDialog dialog(ServerListDialog::SelectionMode,nodePanel_->serverFilter(),this); - dialog.exec(); -} - -void MainWindow::on_actionAddTreeWidget_triggered() -{ - nodePanel_->addToDashboard("tree"); -} - -void MainWindow::on_actionAddTableWidget_triggered() -{ - nodePanel_->addToDashboard("table"); -} - -void MainWindow::on_actionAddInfoPanel_triggered() -{ - nodePanel_->addToDashboard("info"); -} - -void MainWindow::on_actionAbout_triggered() -{ - AboutDialog d; - d.exec(); -} - -void MainWindow::on_actionSaveSessionAs_triggered() -{ - SaveSessionAsDialog d; - d.exec(); -} - -void MainWindow::slotCurrentChangedInPanel() -{ - slotSelectionChanged(nodePanel_->currentSelection()); - - //filterWidget_->reload(nodePanel_->viewFilter()); - - serverFilterMenu_->reload(nodePanel_->serverFilter()); - - //breadcrumbs_->setPath(folderPanel_->currentFolder()); - //slotUpdateNavigationActions(folderPanel_->folderNavigation()); - - //updateIconSizeActionState(); - //updateSearchPanel(); -} - -//The selection changed in one of the views -void MainWindow::slotSelectionChanged(VInfo_ptr info) -{ - selection_=info; - - //Get the set of visible info panel tabs for the selection - std::vector ids; - InfoPanelHandler::instance()->visible(selection_,ids); - - //Set status of the info panel actions in the toolbar accordingly - Q_FOREACH(QAction* ac,infoPanelActions_) - { - ac->setEnabled(false); - - std::string name=ac->data().toString().toStdString(); - - for(std::vector::const_iterator it=ids.begin(); it != ids.end(); ++it) - { - if((*it)->name() == name) - { - ac->setEnabled(true); - break; - } - } - } - - //Update the refres action/info to the selection - updateRefreshActions(); - - //Update the window titlebar - winTitle_->update(); -} - -void MainWindow::updateRefreshActions() -{ - ServerHandler* s=0; - - QString serverName; - if(selection_) - { - s=selection_->server(); - } - - serverComWidget_->setServer(s); - - - bool hasSel=(selection_!= 0); - actionRefreshSelected->setEnabled(hasSel); - actionResetSelected->setEnabled(hasSel); -} - - -void MainWindow::slotOpenInfoPanel() -{ - if(QAction* ac=static_cast(sender())) - { - std::string name=ac->data().toString().toStdString(); - nodePanel_->openDialog(selection_,name); - } -} - -void MainWindow::reloadContents() -{ - nodePanel_->reload(); -} - -//Rerender all the views and breadcrumbs -void MainWindow::rerenderContents() -{ - nodePanel_->rerender(); -} - -void MainWindow::slotContentsChanged() -{ - MainWindow::saveContents(NULL); -} - -bool MainWindow::selectInTreeView(VInfo_ptr info) -{ - return nodePanel_->selectInTreeView(info); -} - -void MainWindow::slotServerSyncNotify(bool) -{ - if(serverSyncNotifyTb_) - { - serverSyncNotifyTb_->hide(); - MainWindow::hideServerSyncNotify(this); - - ServerListDialog dialog(ServerListDialog::SelectionMode,nodePanel_->serverFilter(),this); - dialog.showSysSyncLog(); - dialog.exec(); - } -} - -void MainWindow::hideServerSyncNotify() -{ - if(serverSyncNotifyTb_) - serverSyncNotifyTb_->hide(); -} - -void MainWindow::slotEditServerSettings(ServerHandler* s) -{ - VInfo_ptr info=VInfoServer::create(s); - nodePanel_->openDialog(info,"server_settings"); -} - -//============================================================== -// -// Close and quit -// -//============================================================== - -void MainWindow::closeEvent(QCloseEvent* event) -{ - if(MainWindow::aboutToClose(this)) - { - windows_.removeOne(this); - event->accept(); - } - else - { - event->ignore(); - } -} - -//On quitting we need to call the destructor of all the servers shown in the gui. -//This will guarantee that the server log out is properly done. -//Unfortunately when we quit qt does not call the destructor of the mainwindows. -//We tried to explicitely delete the mainwindows here but it caused a crash on the -//leap42 system. So here we only delete the nodePanel in the mainwindow. This panel -//contains all the tabs. Each tab contains a serverfilter and when the serverfilters -//get deleted in the end the destructors of the servers will be called. -void MainWindow::cleanUpOnQuit() -{ - Q_ASSERT(quitStarted_==true); - serverFilterMenu_->aboutToDestroy(); - delete nodePanel_; -} - -//==================================================== -// -// Read/write settings -// -//==================================================== - -void MainWindow::writeSettings(VComboSettings *vs) -{ - //Qt settings - vs->putQs("geometry",saveGeometry()); - vs->putQs("state",saveState()); - -//See ECFLOW-1090 -#if 0 - vs->putQs("minimized",(windowState() & Qt::WindowMinimized)?1:0); -#endif - - //Other setting - vs->put("infoPanelCount",findChildren().count()); - - //Saves nodePanel - nodePanel_->writeSettings(vs); -} - -void MainWindow::readSettings(VComboSettings *vs) -{ - int cnt=vs->get("infoPanelCount",0); - for(int i=0; i < cnt ; i++) - { - //addInfoPanel(); - } - - nodePanel_->readSettings(vs); - - //See ECFLOW-1090 -#if 0 - if(vs->getQs("minimized").toInt()== 1) - { - setWindowState(windowState() | Qt::WindowMinimized); - } -#endif - - if(vs->containsQs("geometry")) - restoreGeometry(vs->getQs("geometry").toByteArray()); - - if(vs->containsQs("state")) - restoreState(vs->getQs("state").toByteArray()); -} - -//==================================================== -// -// Static methods -// -//==================================================== - -MainWindow* MainWindow::makeWindow(VComboSettings* vs) -{ - MainWindow* win=MainWindow::makeWindow(); - win->readSettings(vs); - return win; -} - -MainWindow* MainWindow::makeWindow() -{ - QStringList idLst; - return MainWindow::makeWindow(idLst); -} - -MainWindow* MainWindow::makeWindow(QString id) -{ - QStringList idLst; - idLst << id; - return MainWindow::makeWindow(idLst); -} - -MainWindow* MainWindow::makeWindow(QStringList idLst) -{ - MainWindow *win=new MainWindow(idLst); - windows_ << win; - return win; -} - -void MainWindow::openWindow(QString id,QWidget *fromW) -{ - MainWindow* win=MainWindow::makeWindow(id); - win->init(findWindow(fromW)); - win->show(); -} - -void MainWindow::openWindow(QStringList idLst,QWidget *fromW) -{ - MainWindow* win=MainWindow::makeWindow(idLst); - win->init(findWindow(fromW)); - win->show(); -} - -void MainWindow::showWindows() -{ - Q_FOREACH(MainWindow *win,windows_) - win->show(); -} - -void MainWindow::configChanged(MainWindow*) -{ - Q_FOREACH(MainWindow *win,windows_) - win->rerenderContents(); -} - -void MainWindow::lookUpInTree(VInfo_ptr info) -{ - Q_FOREACH(MainWindow *win,windows_) - if(win->selectInTreeView(info)) - return; -} - -void MainWindow::hideServerSyncNotify(MainWindow*) -{ - Q_FOREACH(MainWindow *win,windows_) - win->hideServerSyncNotify(); -} - -void MainWindow::cleanUpOnQuit(MainWindow*) -{ - Q_FOREACH(MainWindow *win,windows_) - win->cleanUpOnQuit(); -} - -//Return true if close is allowed, false otherwise -bool MainWindow::aboutToClose(MainWindow* win) -{ - //If quit has already stared we ignore the close signal from - //the main windows. - if(quitStarted_) - { - return false; - } - - //Otherwise - else - { - if(windows_.count() > 1) - { - int tabCnt=win->nodePanel_->count(); - if(tabCnt > 1) - { - if(QMessageBox::question(0,tr("Confirm close"),tr("You are about to close ") + QString::number(tabCnt) + tr(" tabs. Are you sure you want to continue?"), - QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return false; - } - } - } - else if(windows_.count() == 1) - { - return MainWindow::aboutToQuit(win); - } - return true; - } -} - -bool MainWindow::aboutToQuit(MainWindow* topWin) -{ -#if 0 - if(QMessageBox::question(0,tr("Confirm quit"), - tr("Do you want to quit ") + - QString::fromStdString(VConfig::instance()->appName()) + "?", - QMessageBox::Yes | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Yes) - { -#endif - quitStarted_=true; - - //Save browser settings - MainWindow::save(topWin); - - // handle session cleanup - // temporary sessions can be saved or deleted - SessionItem *si = SessionHandler::instance()->current(); - if (si->temporary()) - { - if (si->askToPreserveTemporarySession()) - { - if(QMessageBox::question(0,tr("Delete temporary session?"), - tr("This was a temporary session - would you like to preserve it for future use?"), - QMessageBox::Yes | QMessageBox::No,QMessageBox::No) == QMessageBox::No) - SessionHandler::destroyInstance(); - } - else // if askToPreserveTemporarySession() is false, then we assume we want to delete - { - SessionHandler::destroyInstance(); - } - } - - //Ensure the ServerHandler destructors are called - MainWindow::cleanUpOnQuit(topWin); - - //Exit ecFlowView - QApplication::quit(); -#if 0 - } -#endif - - return false; -} - -void MainWindow::init() -{ - SessionItem* cs=SessionHandler::instance()->current(); - assert(cs); - - VComboSettings vs(cs->sessionFile(),cs->windowFile()); - - //Read configuration. If it fails we create an empty window!! - if(!vs.read()) - { - MainWindow::makeWindow(&vs); - return; - } - - //Get number of windows and topWindow index. - int cnt=vs.get("windowCount",0); - int topWinId=vs.get("topWindowId",-1); - - if(cnt > maxWindowNum_) - { - cnt=maxWindowNum_; - } - - //Create all windows (except the topWindow) in a loop. The - //topWindow should be created last so that it should always appear on top. - std::string winPattern("window_"); - for(int i=0; i < cnt; i++) - { - if(i != topWinId) - { - std::string id=winPattern + boost::lexical_cast(i); - if(vs.contains(id)) - { - vs.beginGroup(id); - MainWindow::makeWindow(&vs); - vs.endGroup(); - } - } - } - - //Create the topwindow - if(topWinId != -1) - { - std::string id=winPattern + boost::lexical_cast(topWinId); - if(vs.contains(id)) - { - vs.beginGroup(id); - MainWindow::makeWindow(&vs); - vs.endGroup(); - } - } - - //If now windows were created we need to create an empty one - if(windows_.count() == 0) - { - MainWindow::makeWindow(&vs); - } -} - -void MainWindow::save(MainWindow *topWin) -{ - MainWindow::saveContents(topWin); - - //Save global config - VConfig::instance()->saveSettings(); - - //Save server list - ServerHandler::saveSettings(); - - //Save icon name list - VIcon::saveLastNames(); -} - -void MainWindow::saveContents(MainWindow *topWin) -{ - SessionItem* cs=SessionHandler::instance()->current(); - assert(cs); - - VComboSettings vs(cs->sessionFile(),cs->windowFile()); - - //We have to clear it so that not to remember all the previous windows - vs.clear(); - - //Add total window number and id of active window - vs.put("windowCount",windows_.count()); - vs.put("topWindowId",windows_.indexOf(topWin)); - - //Save info for all the windows - for(int i=0; i < windows_.count(); i++) - { - std::string id="window_"+boost::lexical_cast(i); - vs.beginGroup(id); - windows_.at(i)->writeSettings(&vs); - vs.endGroup(); - } - - //Write to json - vs.write(); -} - - -void MainWindow::reload() -{ - Q_FOREACH(MainWindow *w,windows_) - { - w->reloadContents(); - } -} - -void MainWindow::saveSession(SessionItem* s) -{ - -} - -MainWindow* MainWindow::findWindow(QWidget *childW) -{ - Q_FOREACH(MainWindow *w,windows_) - { - if(static_cast(w) == childW->window()) - return w; - } - - return 0; -} - -MainWindow* MainWindow::firstWindow() -{ - return (!windows_.isEmpty())?(windows_[0]):NULL; -} - -void MainWindow::startPreferences(QString option) -{ - if(windows_.count() > 0) - startPreferences(windows_.front(),option); -} - -void MainWindow::startPreferences(MainWindow *w,QString option) -{ - Q_ASSERT(w); - - PropertyDialog* d=new PropertyDialog; //belongs to the whole app - - d->showPage(option); - - connect(d,SIGNAL(configChanged()), - w,SLOT(slotConfigChanged())); - - if(d->exec() == QDialog::Accepted) - { - if(d->isConfigChanged()) - { - configChanged(w); - } - } - - delete d; -} - -void MainWindow::updateMenuMode(ServerHandler* sh) -{ - Q_FOREACH(MainWindow *w,windows_) - { - w->winTitle_->update(sh); - } -} - -//-------------------------------------------------------- -// -// MainWindowTitle (class to handle the MainWindow title) -// -//-------------------------------------------------------- - -MainWindowTitleHandler::MainWindowTitleHandler(MainWindow *win) : win_(win) -{ - Q_ASSERT(win_); - std::vector propVec; -} - -MainWindowTitleHandler::~MainWindowTitleHandler() -{ -} - -void MainWindowTitleHandler::update(ServerHandler *sh) -{ - Q_ASSERT(win_); - if(sh == win_->selectedServer()) - update(); -} - -void MainWindowTitleHandler::update() -{ - Q_ASSERT(win_); - - char *userTitle = getenv("ECFUI_TITLE"); - std::string mainTitle = (userTitle != NULL) ? std::string(userTitle) + " (" + ecf::Version::raw() + ")" - : VConfig::instance()->appLongName(); - QString title=QString::fromStdString(mainTitle); - - if(ServerHandler* sh=win_->selectedServer()) - { - QString menuMode=sh->nodeMenuMode(); - if(!menuMode.isEmpty()) - title+=" - (menu: " + menuMode + ")"; - } - - // add the name of the session to the title bar? - QString sessionName = QString::fromStdString(SessionHandler::instance()->current()->name()); - if (sessionName != "default") - title+= " - (session: " + sessionName + ")"; - - win_->setWindowTitle(title); -} diff -Nru ecflow-4.9.0/Viewer/src/MainWindow.hpp ecflow-4.11.1/Viewer/src/MainWindow.hpp --- ecflow-4.9.0/Viewer/src/MainWindow.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MainWindow.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ -#ifndef MAINWINDOW_HPP_ -#define MAINWINDOW_HPP_ - -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include -#include - -#include "ui_MainWindow.h" - -#include "VInfo.hpp" - -#include - -class QActionGroup; -class QLabel; -class QToolButton; -class ClockWidget; -class InfoPanel; -class NodePanel; -class ServerRefreshInfoWidget; -class ServerFilterMenu; -class SessionItem; -class VComboSettings; - -class MainWindow; - -class MainWindowTitleHandler -{ - friend class MainWindow; -public: - MainWindowTitleHandler(MainWindow*); - ~MainWindowTitleHandler(); - - void update(); - -protected: - void update(ServerHandler*); - - MainWindow* win_; -}; - -class MainWindow : public QMainWindow, private Ui::MainWindow -{ - Q_OBJECT - -public: - MainWindow(QStringList,QWidget *parent=0); - ~MainWindow(); - - ServerHandler* selectedServer() const; - - static void init(); - static void showWindows(); - static void openWindow(QString id,QWidget *fromW=0); - static void openWindow(QStringList id,QWidget *fromW=0); - static void reload(); - static void saveSession(SessionItem*); - static void lookUpInTree(VInfo_ptr); - static void startPreferences(QString); - static void updateMenuMode(ServerHandler*); - static MainWindow* firstWindow(); - -protected Q_SLOTS: - void on_actionNewTab_triggered(); - void on_actionNewWindow_triggered(); - void on_actionClose_triggered(); - void on_actionQuit_triggered(); - void on_actionRefresh_triggered(); - void on_actionReset_triggered(); - void on_actionRefreshSelected_triggered(); - void on_actionResetSelected_triggered(); - void on_actionConfigureNodeMenu_triggered(); - void on_actionManageServers_triggered(); - void on_actionAddTreeWidget_triggered(); - void on_actionAddTableWidget_triggered(); - void on_actionAddInfoPanel_triggered(); - void on_actionPreferences_triggered(); - void on_actionSearch_triggered(); - void on_actionNotification_triggered(); - void on_actionCommandOutput_triggered(); - void on_actionAbout_triggered(); - void on_actionSaveSessionAs_triggered(); - void on_actionManageSessions_triggered(); - - void slotCurrentChangedInPanel(); - void slotSelectionChanged(VInfo_ptr); - void slotOpenInfoPanel(); - void slotConfigChanged(); - void slotContentsChanged(); - void slotServerSyncNotify(bool); - void slotEditServerSettings(ServerHandler* s); - -private: - void init(MainWindow*); - void closeEvent(QCloseEvent*); - void addInfoPanelActions(QToolBar *toolbar); - void reloadContents(); - void rerenderContents(); - bool selectInTreeView(VInfo_ptr info); - void updateRefreshActions(); - void hideServerSyncNotify(); - void cleanUpOnQuit(); - - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - - static MainWindow* makeWindow(VComboSettings* vs); - static MainWindow *makeWindow(); - static MainWindow *makeWindow(QString id); - static MainWindow *makeWindow(QStringList idLst); - static bool aboutToClose(MainWindow*); - static bool aboutToQuit(MainWindow*); - static void save(MainWindow *); - static void saveContents(MainWindow *); - static MainWindow* findWindow(QWidget *childW); - static void configChanged(MainWindow *); - static void hideServerSyncNotify(MainWindow*); - static void cleanUpOnQuit(MainWindow *); - static void startPreferences(MainWindow *,QString); - - ServerFilterMenu* serverFilterMenu_; - NodePanel* nodePanel_; - QList infoPanelActions_; - VInfo_ptr selection_; - QToolButton* serverSyncNotifyTb_; - ServerRefreshInfoWidget *serverComWidget_; - MainWindowTitleHandler* winTitle_; - ClockWidget* clockWidget_; - - static bool quitStarted_; - static QList windows_; - static int maxWindowNum_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/MainWindow.ui ecflow-4.11.1/Viewer/src/MainWindow.ui --- ecflow-4.9.0/Viewer/src/MainWindow.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MainWindow.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,350 +0,0 @@ - - - MainWindow - - - - 0 - 0 - 852 - 675 - - - - ecflow_ui - - - false - - - - - - 740 - 640 - 52 - 14 - - - - TextLabel - - - - - - - 0 - 0 - 852 - 24 - - - - false - - - - true - - - &File - - - - - - - - - - - - - - true - - - &Help - - - - - - true - - - Ser&vers - - - - - - - &Panels - - - - - - - - &Tools - - - - - - - - - - &Refresh - - - - - - - - - - - - - - - - - - View toolbar - - - false - - - Qt::AllToolBarAreas - - - TopToolBarArea - - - false - - - - - - - - :/viewer/close.svg:/viewer/close.svg - - - &Close - - - Close window - - - Ctrl+W - - - - - - :/viewer/images/exit.svg:/viewer/images/exit.svg - - - &Quit - - - Ctrl+Q - - - - - - :/viewer/images/configure.svg:/viewer/images/configure.svg - - - &Preferences ... - - - - - New &tab - - - Ctrl+T - - - - - New &window - - - - - Refresh all servers &in tab - - - Refresh <b>all servers</b> in current tab - - - Shift+F5 - - - - - &Reset all servers in tab - - - Reset - - - - - Configure Node &Menu - - - false - - - - - Contents - - - View contents - - - - - - :/viewer/manage_server.svg:/viewer/manage_server.svg - - - &Manage servers ... - - - Manage servers - - - Ctrl+N - - - - - - :/viewer/images/add_info.svg:/viewer/images/add_info.svg - - - Add &info panel - - - - - - :/viewer/images/add_tree.svg:/viewer/images/add_tree.svg - - - Add &tree view panel - - - Add tree widget - - - - - - :/viewer/images/add_table.svg:/viewer/images/add_table.svg - - - Add ta&ble view panel - - - Add table widget - - - - - &About EcflowUI ... - - - - - true - - - &Manage sessions ... - - - true - - - - - - :/viewer/search.svg:/viewer/search.svg - - - &Search ... - - - Start search dialogue - - - - - - :/viewer/reload_green.svg:/viewer/reload_green.svg - - - R&efresh selected server - - - F5 - - - - - Reset &selected server - - - Reset <b>selected</b> server in current tab - - - - - S&ave session as... - - - false - - - - - - :/viewer/notification.svg:/viewer/notification.svg - - - &Notifications ... - - - Start notifications dialogue - - - - - Shell command &output ... - - - Start shell command output dialogue - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ManualItemWidget.cpp ecflow-4.11.1/Viewer/src/ManualItemWidget.cpp --- ecflow-4.9.0/Viewer/src/ManualItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ManualItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -//============================================================================ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ManualItemWidget.hpp" - -#include "Highlighter.hpp" -#include "InfoProvider.hpp" -#include "MessageLabel.hpp" -#include "VConfig.hpp" -#include "VReply.hpp" - -ManualItemWidget::ManualItemWidget(QWidget *parent) : CodeItemWidget(parent) -{ - fileLabel_->hide(); - messageLabel_->setShowTypeTitle(false); - messageLabel_->hide(); - textEdit_->setShowLineNumbers(false); - - //The document becomes the owner of the highlighter - new Highlighter(textEdit_->document(),"manual"); - - infoProvider_=new ManualProvider(this); - - //Editor font - textEdit_->setFontProperty(VConfig::instance()->find("panel.manual.font")); -} - -ManualItemWidget::~ManualItemWidget() -{ -} - -QWidget* ManualItemWidget::realWidget() -{ - return this; -} - -void ManualItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - - info_=info; - messageLabel_->hide(); - - //Info must be a node - if(info_ && info_->isNode() && info_->node()) - { - reloadTb_->setEnabled(false); - infoProvider_->info(info_); - } -} - -void ManualItemWidget::clearContents() -{ - InfoPanelItem::clear(); - textEdit_->clear(); - messageLabel_->hide(); - reloadTb_->setEnabled(true); - clearCurrentFileName(); -} - -void ManualItemWidget::infoReady(VReply* reply) -{ - Q_ASSERT(reply); - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); - - if(reply->hasWarning()) - { - messageLabel_->showWarning(QString::fromStdString(reply->warningText())); - } - else if(reply->hasInfo()) - { - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); - } - else if(s.isEmpty()) - { - messageLabel_->showInfo("Manual is not available"); - } - - fileLabel_->update(reply); - reloadTb_->setEnabled(true); - setCurrentFileName(reply->fileName()); -} - -void ManualItemWidget::infoProgress(VReply* reply) -{ - // QString s=QString::fromStdString(reply->text()); - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); -} - -void ManualItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); - messageLabel_->showError(s); - reloadTb_->setEnabled(true); -} - -void ManualItemWidget::reloadRequested() -{ - reload(info_); -} - -void ManualItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - reloadTb_->setEnabled(false); - } - //Resume - else - { - if(info_ && info_->node()) - { - reloadTb_->setEnabled(true); - } - else - { - clearContents(); - } - } - } -} - - -static InfoPanelItemMaker maker1("manual"); - diff -Nru ecflow-4.9.0/Viewer/src/ManualItemWidget.hpp ecflow-4.11.1/Viewer/src/ManualItemWidget.hpp --- ecflow-4.9.0/Viewer/src/ManualItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ManualItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef MANUALITEMWIDGET_HPP_ -#define MANUALITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" -#include "CodeItemWidget.hpp" - -class ManualItemWidget : public CodeItemWidget, public InfoPanelItem -{ -public: - explicit ManualItemWidget(QWidget *parent=0); - ~ManualItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected: - void updateState(const ChangeFlags&); - void reloadRequested(); -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/MenuConfigDialog.cpp ecflow-4.11.1/Viewer/src/MenuConfigDialog.cpp --- ecflow-4.9.0/Viewer/src/MenuConfigDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MenuConfigDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,114 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "MenuHandler.hpp" -#include "MenuConfigDialog.hpp" - - -MenuConfigDialog::MenuConfigDialog(QWidget *parent) : QDialog(parent) -{ - setupUi(this); - //connect (insertPushButton_, SIGNAL(clicked()), this, SLOT(insertCurrentText())); - connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); - - - - // load up the user menu file if it exists XXXX - - if (0) - { - } - else - { - // otherwise create a dummy menu to start with - - Menu *menu = new Menu("UserTemp"); - MenuHandler::addMenu(menu); - - MenuItem *item = new MenuItem("UserTemp"); - item->setAsSubMenu(); - item->setCommand("NoCommand"); - - MenuHandler::addItemToMenu(item, "Node"); - - - //MenuItem *item2 = new MenuItem("Com"); - //item2->setCommand("ecflow --whatever"); - //MenuHandler::addItemToMenu(item2, "UserTemp"); - - //updateMenuTree(menu); - updateMenuTree(MenuHandler::findMenu("Node")); - } - - -} - - -void MenuConfigDialog::updateMenuTree(Menu *menu) -{ - menuTreeWidget_->clear(); - menuTreeWidget_->setColumnCount(1); - - - QTreeWidgetItem *topLevelItem = new QTreeWidgetItem(menuTreeWidget_); - topLevelItem->setText(0, QString::fromStdString(menu->name())); - //topLevelItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); - - addChildrenToMenuTree(menu, topLevelItem); - -} - - -void MenuConfigDialog::addChildrenToMenuTree(Menu *menu, QTreeWidgetItem *parent) -{ - if(!menu) - return; - - std::vector&items = menu->items(); - - for (std::vector::iterator itItems = items.begin(); itItems != items.end(); ++itItems) - { - QTreeWidgetItem *item = new QTreeWidgetItem(parent); - item->setText(0, QString::fromStdString((*itItems)->name())); - item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); - - if ((*itItems)->isSubMenu()) - { - Menu *submenu = MenuHandler::findMenu((*itItems)->name()); - if(menu) - { - addChildrenToMenuTree(submenu, item); - } - } - else if ((*itItems)->isDivider()) - { - //qmenu->addSeparator(); - } - else - { - //QAction *action = (*itItems)->action(); - //qmenu->addAction(action); - //action->setParent(parent); - //action->setEnabled(compatible); - } - } -} - - - -//void CommandDesignerWidget::insertCurrentText() -//{ -// //commandLineEdit_->setText("Silly"); -// commandLineEdit_->insert(componentsComboBox_->currentText() + " "); -//} - -void MenuConfigDialog::reject() -{ - QDialog::reject(); -} diff -Nru ecflow-4.9.0/Viewer/src/MenuConfigDialog.hpp ecflow-4.11.1/Viewer/src/MenuConfigDialog.hpp --- ecflow-4.9.0/Viewer/src/MenuConfigDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MenuConfigDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,88 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - - -#ifndef MENUCONFIGDIALOG_HPP_ -#define MENUCONFIGDIALOG_HPP_ - -#include -#include -#include - -#include "MenuHandler.hpp" - -class ConfigTreeWidget : public QTreeWidget -{ -public: - ConfigTreeWidget() - { - } - - explicit ConfigTreeWidget(QSplitter*s) : QTreeWidget(s) - { - resize(200, 300); - - setSelectionMode(QAbstractItemView::SingleSelection); - setDragEnabled(true); - viewport()->setAcceptDrops(true); - setDropIndicatorShown(true); - setDragDropMode(QAbstractItemView::InternalMove); -/* - QTreeWidgetItem* parentItem = new QTreeWidgetItem(this); - parentItem->setText(0, "Test"); - parentItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDropEnabled); - - for(int i = 0; i < 10; ++i) - { - QTreeWidgetItem* pItem = new QTreeWidgetItem(parentItem); - pItem->setText(0, QString("Number %1").arg(i) ); - pItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled); - pItem->addChild(pItem); - }*/ - }; - -/*private: - virtual void dropEvent(QDropEvent * event) - { - QModelIndex droppedIndex = indexAt( event->pos() ); - - if( !droppedIndex.isValid() ) - return; - - QTreeWidget::dropEvent(event); - }*/ -}; - - -#include "ui_MenuConfigDialog.h" - -class MenuConfigDialog : public QDialog, private Ui::MenuConfigDialog -{ - Q_OBJECT - -public: - explicit MenuConfigDialog(QWidget *parent = 0); - ~MenuConfigDialog() {}; - - void updateMenuTree(Menu *menu); - - void reject(); - - -//public Q_SLOTS: -// void insertCurrentText(); - - -private: - void addChildrenToMenuTree(Menu *menu, QTreeWidgetItem *parent); - -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/MenuConfigDialog.ui ecflow-4.11.1/Viewer/src/MenuConfigDialog.ui --- ecflow-4.9.0/Viewer/src/MenuConfigDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MenuConfigDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,256 +0,0 @@ - - - MenuConfigDialog - - - - 0 - 0 - 882 - 627 - - - - Dialog - - - - - - - 16777215 - 64 - - - - QFrame::StyledPanel - - - QFrame::Raised - - - - - - Qt::Horizontal - - - - 553 - 20 - - - - - - - - &New Item - - - - - - - &Duplicate Item - - - - - - - New &Submenu - - - - - - - - - - Qt::Horizontal - - - - - 1 - - - - - - - QFormLayout::ExpandingFieldsGrow - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - Name: - - - - - - - - - - Valid Node Types: - - - - - - - - - All - - - - - - - Task - - - - - - - Alias - - - - - - - Family - - - - - - - Suite - - - - - - - Server - - - - - - - Unselect all - - - - - - - - - Valid States: - - - - - - - - - Unknown - - - - - - - Queued - - - - - - - Suspended - - - - - - - Aborted - - - - - - - Unselect all - - - - - - - - - Valid Attributes: - - - - - - - CheckBox - - - - - - - Command: - - - - - - - - - - - - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - CommandDesignerWidget - QLineEdit -
        CommandDesignerWidget.hpp
        -
        - - ConfigTreeWidget - QTreeWidget -
        MenuConfigDialog.hpp
        -
        -
        - - -
        diff -Nru ecflow-4.9.0/Viewer/src/MenuHandler.cpp ecflow-4.11.1/Viewer/src/MenuHandler.cpp --- ecflow-4.9.0/Viewer/src/MenuHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MenuHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,920 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - - -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "Str.hpp" -#include "MenuHandler.hpp" -#include "ServerHandler.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "VConfig.hpp" -#include "VNode.hpp" -#include "VProperty.hpp" -#include "CustomCommandHandler.hpp" - -int MenuItem::idCnt_=0; - -std::vector MenuHandler::menus_; -MenuHandler::ConfirmationMap MenuHandler::commandsWhichRequireConfirmation_; -TrueNodeCondition MenuHandler::trueCond_; -FalseNodeCondition MenuHandler::falseCond_; - - -MenuHandler::MenuHandler() -{ - menus_.clear(); -} - - -// --------------------------------------------------------- -// MenuHandler::readMenuConfigFile -// Read the given config file and store the resulting menus -// internally. -// --------------------------------------------------------- - -bool MenuHandler::readMenuConfigFile(const std::string &configFile) -{ - // parse the response using the boost JSON property tree parser - - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(configFile, pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse JSON menu file : " + errorMessage)); - return false; - } - - - - // iterate over the top level of the tree - - for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) - { - // parse the menu definitions? - - if (itTopLevel->first == "menus") - { - UiLog().dbg() << "Menus:"; - - ptree const &menusDef = itTopLevel->second; - - // iterate through all the menus - - for (ptree::const_iterator itMenus = menusDef.begin(); itMenus != menusDef.end(); ++itMenus) - { - ptree const &menuDef = itMenus->second; - - std::string cname = menuDef.get("name", "NoName"); - UiLog().dbg() << " " << cname; - Menu *menu = new Menu(cname); - - //ptree const &menuModesDef = menuDef.get_child("modes"); - - //for (ptree::const_iterator itMenuModes = menuModesDef.begin(); itMenuModes != menuModesDef.end(); ++itMenuModes) - //{ - // std::cout << " +" << itMenuModes->second.data() << std::endl; - //} - - std::string parentMenuName = menuDef.get("parent", "None"); - - if (parentMenuName != "None") - { - } - - addMenu(menu); // add to our list of available menus - - } - } - - // parse the menu items? - - else if (itTopLevel->first == "menu_items") - { - UiLog().dbg() << "Menu items:"; - - ptree const &itemsDef = itTopLevel->second; - - // iterate through all the items - - for (ptree::const_iterator itItems = itemsDef.begin(); itItems != itemsDef.end(); ++itItems) - { - ptree const &ItemDef = itItems->second; - - std::string name = ItemDef.get("name", "NoName"); - std::string menuName = ItemDef.get("menu", "NoMenu"); - std::string command = ItemDef.get("command", "NoCommand"); - std::string type = ItemDef.get("type", "Command"); - std::string enabled = ItemDef.get("enabled_for", ""); - std::string visible = ItemDef.get("visible_for", ""); - std::string questFor = ItemDef.get("question_for",""); - std::string question = ItemDef.get("question", ""); - std::string questionControl = ItemDef.get("question_control", ""); - std::string warning = ItemDef.get("warning", ""); - std::string handler = ItemDef.get("handler", ""); - std::string views = ItemDef.get("view", ""); - std::string icon = ItemDef.get("icon", ""); - std::string hidden = ItemDef.get("hidden", "false"); - std::string multiSelect = ItemDef.get("multi", "true"); - std::string statustip = ItemDef.get("status_tip", ""); - - //std::cout << " " << name << " :" << menuName << std::endl; - - UiLog().dbg() << " " << name; - MenuItem *item = new MenuItem(name); - item->setCommand(command); - - - BaseNodeCondition *enabledCond = NodeExpressionParser::instance()->parseWholeExpression(enabled); - if (enabledCond == NULL) - { - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse enabled condition: " + enabled)); - enabledCond = new FalseNodeCondition(); - } - item->setEnabledCondition(enabledCond); - - - BaseNodeCondition *visibleCond = NodeExpressionParser::instance()->parseWholeExpression(visible); - if (visibleCond == NULL) - { - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse visible condition: " + visible)); - visibleCond = new FalseNodeCondition(); - } - item->setVisibleCondition(visibleCond); - - BaseNodeCondition *questionCond = NodeExpressionParser::instance()->parseWholeExpression(questFor); - if (questionCond == NULL) - { - UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse question condition: " + questFor)); - questionCond = new FalseNodeCondition(); - } - item->setQuestionCondition(questionCond); - - item->setQuestion(question); - item->setQuestionControl(questionControl); - item->setWarning(warning); - item->setHandler(handler); - item->setIcon(icon); - item->setStatustip(statustip); - - if(!views.empty()) - { - std::vector viewsVec; - QStringList vLst=QString::fromStdString(views).split("/"); - for(int i=0; i < vLst.count(); i++) - { - viewsVec.push_back(vLst[i].toStdString()); - } - - item->setViews(viewsVec); - } - - - item->setHidden((hidden == "true")?1:0); - item->setMultiSelect((multiSelect == "true")?1:0); - - if (type == "Submenu") - item->setAsSubMenu(); - - addItemToMenu(item, menuName); - //std::cout << " added" << std::endl; - - - // parse the valid node types/states for this menu item - - //if( ItemDef.count("valid_types") > 0 ) // does this node exist on the tree? - //{ - // ptree ptValidTypes = ItemDef.get_child("valid_types"); - // for (ptree::const_iterator itTypes = ptValidTypes.begin(); itTypes != ptValidTypes.end(); ++itTypes) - // { - // std::string type(itTypes->second.data()); - // //item->addValidType(type); - // } - //} - //else - //{ - // //item->addValidType("all"); - //} - - - if( ItemDef.count("valid_states") > 0 ) // does this node exist on the tree? - { - ptree ptValidStates = ItemDef.get_child("valid_states"); - for (ptree::const_iterator itStates = ptValidStates.begin(); itStates != ptValidStates.end(); ++itStates) - { - std::string state(itStates->second.data()); - //item->addValidState(state); - } - } - else - { - //item->addValidState("all"); - } - } - } - } - - - //ptree ptMenus = pt.get_child("menus"); - - - - - //for (ptree::const_iterator itTopLevel = pt.begin(); itTopLevel != pt.end(); ++itTopLevel) - //{ - // if (itTopLevel->first == "menus") - - //} - - - - - return true; -} - - -// --------------------------------------------------------- -// MenuHandler::addCustomMenuCommands -// Obtains the current list of custom commands and adds them -// to the list of custom menu items. -// --------------------------------------------------------- - -void MenuHandler::refreshCustomMenuCommands() -{ - CustomCommandHistoryHandler *customRecentCmds = CustomCommandHistoryHandler::instance(); - CustomSavedCommandHandler *customSavedCmds = CustomSavedCommandHandler::instance(); - - Menu *menu = findMenu("User defined"); - if(menu) - { - menu->clearFixedList(); - - // create the 'compulsary' menu items - MenuItem *item1 = new MenuItem("Manage commands..."); - item1->setCommand("custom"); - menu->addItemToFixedList(item1); - item1->setEnabledCondition(&trueCond_); - item1->setVisibleCondition(&trueCond_); - item1->setQuestionCondition(&falseCond_); - item1->setIcon("configure.svg"); - - // Saved commands - MenuItem *item2 = new MenuItem("-"); - menu->addItemToFixedList(item2); - item2->setEnabledCondition(&trueCond_); - item2->setVisibleCondition(&trueCond_); - item2->setQuestionCondition(&falseCond_); - - int numSavedCommands = customSavedCmds->numCommands(); - - for (int i = 0; i < numSavedCommands; i++) - { - CustomCommand *cmd = customSavedCmds->commandFromIndex(i); - if (cmd->inContextMenu()) - { - MenuItem *item = new MenuItem(cmd->name()); - item->setCommand(cmd->command()); - item->setEnabledCondition(&trueCond_); - item->setVisibleCondition(&trueCond_); - item->setQuestionCondition(&trueCond_); - item->setCustom(true); - item->setStatustip("__cmd__"); - menu->addItemToFixedList(item); - } - } - - - // Recently executed commands - MenuItem *item3 = new MenuItem("-"); - menu->addItemToFixedList(item3); - item3->setEnabledCondition(&trueCond_); - item3->setVisibleCondition(&trueCond_); - item3->setQuestionCondition(&falseCond_); - - MenuItem *item4 = new MenuItem("Recent"); - menu->addItemToFixedList(item4); - item4->setEnabledCondition(&falseCond_); - item4->setVisibleCondition(&trueCond_); - item4->setQuestionCondition(&falseCond_); - - int numRecentCommands = customRecentCmds->numCommands(); - - for (int i = 0; i < numRecentCommands; i++) - { - CustomCommand *cmd = customRecentCmds->commandFromIndex(i); - - MenuItem *item = new MenuItem(cmd->name()); - item->setCommand(cmd->command()); - item->setEnabledCondition(&trueCond_); - item->setVisibleCondition(&trueCond_); - item->setQuestionCondition(&trueCond_); - item->setCustom(true); - item->setStatustip("__cmd__"); - menu->addItemToFixedList(item); - } - } -} - - -Menu *MenuHandler::findMenu(const std::string &name) -{ - for (std::vector::iterator itMenus = menus_.begin(); itMenus != menus_.end(); ++itMenus) - { - if ((*itMenus)->name() == name) - { - return (*itMenus); - } - } - - return NULL; // if we got to here, then the menu was not found -} - -MenuItem* MenuHandler::findItem(QAction* ac) -{ - // ac could be NULL, e.g. if the user clicked on a separator instead of a menu item - if (ac) - { - for(std::vector::iterator itMenus = menus_.begin(); itMenus != menus_.end(); ++itMenus) - { - for(std::vector::iterator it=(*itMenus)->items().begin(); it!=(*itMenus)->items().end(); ++it) - { - if((*it)->id() == ac->data().toInt()) - { - return *it; - } - } - } - } - - return NULL; -} - -MenuItem* MenuHandler::newItem(const std::string &name) -{ - return NULL; -} - -bool MenuHandler::addItemToMenu(MenuItem *item, const std::string &menuName) -{ - Menu *menu = findMenu(menuName); - // items_.push_back(item); // add to our global list of menu items - - if (menu) - { - menu->addItemToFixedList(item); - return true; - } - else - { - UiLog().err() << "Could not find menu called " << - menuName << " to add item " << item->name() << " to."; - return false; - } - - return false; -} - - -MenuItem *MenuHandler::invokeMenu(const std::string &menuName, std::vector nodes, QPoint pos, QWidget *parent,const std::string& view) -{ - MenuItem *selectedItem = NULL; - Menu *menu = findMenu(menuName); - - if (menu) - { - QList acLst; - - //While create the menus we collect all the actions created with "parent" as the parent. - //QMenu does not take ownership of these actions so we need to delete them. - QMenu *qMenu = menu->generateMenu(nodes, parent, NULL, view,acLst); - - if (qMenu) - { - QAction* selectedAction = qMenu->exec(pos); - selectedItem=MenuHandler::findItem(selectedAction); - - delete qMenu; - - //Delete all the actions with "parent" as the parent; - Q_FOREACH(QAction *ac,acLst) - { - assert(parent == ac->parent()); - delete ac; - } - } - } - - return selectedItem; -} - -MenuHandler::ConfirmationMap &MenuHandler::getCommandsThatRequireConfirmation() -{ - // populate the list only the first time this function is called - if (commandsWhichRequireConfirmation_.empty()) - { - // list the commands which require a prompt: - commandsWhichRequireConfirmation_["delete"] = "Do you really want to delete ?"; - commandsWhichRequireConfirmation_["terminate"] = "Do you really want to terminate ?"; - commandsWhichRequireConfirmation_["halt"] = "Do you really want to halt ?"; - } - return commandsWhichRequireConfirmation_; -} - - - -// some commands, such as --delete, prompt the user for confirmation on the command line, which -// causes the application to hang. The way we get around this is to intercept these commands and, -// where possible, add a "yes" argument, which will bypass the prompt. -void MenuHandler::interceptCommandsThatNeedConfirmation(MenuItem *item) -{ - std::string command = item->command(); - QString wholeCmd = QString::fromStdString(command); - - // find the verb in the command - //QRegExp rx("ecflow_client\\s+--(\\S+).*"); // \s=whitespace, \S=non-whitespace - QRegExp rx("ecflow_client\\s+--([a-zA-Z]+).*"); // \s=whitespace, \S=non-whitespace - int i = rx.indexIn(wholeCmd); - if (i != -1) // a command was found - { - QString commandName = rx.cap(1); - std::string cmdName = commandName.toStdString(); - - // is this command one of the ones that requires a prompt? - MenuHandler::ConfirmationMap &list = getCommandsThatRequireConfirmation(); - MenuHandler::ConfirmationMap::iterator it=list.find(cmdName); - if(it != list.end()) - { - // does the command already have a 'yes'? - QRegExp rx2(".*\\byes\\b.*"); // \b=word boundary - int j = rx2.indexIn(wholeCmd); - if (j == -1) // no - { - item->setQuestion((*it).second); // note that we need to ask the user - - // fix the command so that it has "yes" in it - std::string minusCmd = std::string("--") + cmdName; - std::string cmdEquals = minusCmd + "="; - std::string cmdEqualsYes = cmdEquals + "yes "; - std::string cmdYes = minusCmd + " yes "; - if (!ecf::Str::replace(command, cmdEquals, cmdEqualsYes)) // --command=foo -> --command=yes foo - { - ecf::Str::replace(command, minusCmd, cmdYes); // --command foo -> --command yes foo - } - item->setCommand(command); - } - } - } -} - -// ----------------------------------------------------------------- - - -/////////////////////////////////////////////////////////// - -// -------------------- -// Menu class functions -// -------------------- - - - -Menu::Menu(const std::string &name) : name_(name) -{ -} - - -Menu::~Menu() -{ - for (std::vector::iterator itItems = itemsCombined_.begin(); itItems != itemsCombined_.end(); ++itItems) - { - if (*itItems) - delete (*itItems); - } -} - - -QMenu *Menu::generateMenu(std::vector nodes, QWidget *parent,QMenu* parentMenu,const std::string& view,QList& acLst) -{ - QMenu *qmenu=NULL; - if(parentMenu) - { - qmenu=parentMenu->addMenu(QString::fromStdString(name())); - } - else - { - qmenu=new QMenu(parent); - qmenu->setObjectName("cm"); - qmenu->setTitle(QString::fromStdString(name())); - } - - if (nodes.empty()) - return NULL; - - //qmenu->setWindowFlags(Qt::Tool); - //qmenu->setWindowTitle("my title"); - - //TypeNodeCondition typeCondFamily (MenuItem::FAMILY); - //TypeNodeCondition typeCondTask (MenuItem::TASK); - //StateNodeCondition stateCondUnknown ("unknown"); - //OrNodeCondition orCond (&typeCondFamily, &typeCondTask); - //AndNodeCondition andCond (&orCond, &stateCondUnknown); - - //std::string condString("not task"); - //BaseNodeCondition *nodeCond = NodeExpressionParser::parseWholeExpression(condString); - - //if (nodeCond == NULL) - //{ - // UserMessage::message(UserMessage::ERROR, true, std::string("Error, unable to parse condition: " + condString)); - //} - - // add an inactive action(!) to the top of the menu in order to show which - // node has been selected - - buildMenuTitle(nodes, qmenu); - - // if multiple attributes are selected, then tell the user we can't help them - // NOTE that ActionHandler.cpp ensures that we cannot have a mix of attr and non-attr nodes - if (nodes[0]->isAttribute() && nodes.size() > 1) - { - QAction *noAction = new QAction("No action for multiple attributes", parent); - noAction->setEnabled(false); - qmenu->addAction(noAction); - return qmenu; - } - - // merge the fixed menu items (from the config file) with the dynamic ones - itemsCombined_ = itemsFixed_; - itemsCombined_.insert(itemsCombined_.end(), itemsCustom_.begin(), itemsCustom_.end()); - - for (std::vector::iterator itItems = itemsCombined_.begin(); itItems != itemsCombined_.end(); ++itItems) - { - // is this item valid for the current selection? - - if((*itItems)->hidden()) - continue; - - if(!(*itItems)->isValidView(view)) - continue; - - bool visible = true; - - for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) - { - //compatible = compatible && (*itItems)->compatibleWithNode(*itNodes); - //compatible = compatible && (nodeCond != NULL && nodeCond->execute(*itNodes)); - visible = visible && (*itItems)->visibleCondition()->execute(*itNodes); - } - - if (visible) - { - bool enabled = true; - - for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) - { - enabled = enabled && (*itItems)->enabledCondition()->execute(*itNodes); - } - - //Check multiple selection - if(nodes.size() > 1 && !(*itItems)->multiSelect()) - enabled = false; - - if ((*itItems)->isSubMenu()) - { - Menu *menu = MenuHandler::findMenu((*itItems)->name()); - if (menu) - { - //The submenu will be added to qmenu and it will take ownership of it. - QMenu *subMenu = menu->generateMenu(nodes, parent, qmenu, view, acLst); - subMenu->setEnabled(enabled); - } - } - else if ((*itItems)->isDivider()) - { - qmenu->addSeparator(); - } - else - { - //When we add the action to the menu its parent (NULL e.i. the QApplication) does not change. - //So when the menu is deleted the action is not deleted. - //At least this is the behaviour with Qt 4.8. and 5.5. - //QAction *action = (*itItems)->action(); - //action->setParent(parent); - - //These actions will have "parent" as the parent, otherwise the statustip would not work - //on qmainwindows. The downside is that we need to delete these actions separately when the qmenu is deleted. - //In theory the parent of the actions could be the qmenu as well, but in this case the statustip does not work! - QAction* action=(*itItems)->createAction(parent); - qmenu->addAction(action); - action->setEnabled(enabled); - acLst << action; - } - } - } - - return qmenu; -} - -/* -void Menu::addSubHeading(std::string &name) -{ - QLabel *nodeLabel = new QLabel(name); - - QFont menuTitleFont; - menuTitleFont.setBold(true); - menuTitleFont.setItalic(true); - nodeLabel->setFont(menuTitleFont); - nodeLabel->setAlignment(Qt::AlignHCenter); - nodeLabel->setObjectName("nodeLabel"); - - QWidget* titleW=new QWidget(qmenu); - QVBoxLayout *titleLayout=new QVBoxLayout(titleW); - titleLayout->setContentsMargins(2,2,2,2); - titleLayout->addWidget(nodeLabel); - nodeLabel->setParent(titleW); - - QWidgetAction *wAction = new QWidgetAction(qmenu); - //Qt doc says: the ownership of the widget is passed to the widgetaction. - //So when the action is deleted it will be deleted as well. - wAction->setDefaultWidget(titleW); - //wAction->setEnabled(false); - qmenu->addAction(wAction); - -} -*/ -void Menu::buildMenuTitle(std::vector nodes, QMenu* qmenu) -{ - QLabel *nodeLabel = NULL; - - - // we will only create a multiple-entry context menu if we have multiple non-attribute nodes - // it is already ensured that if we have multiple nodes, they will be non-attribute nodes - bool multiple = (nodes.size() > 1); - - if (!multiple) - { - VNode *node=nodes.at(0)->node(); - - if(!node) - return; - - //single node selected put a label with the node name + colour - nodeLabel = new QLabel(node->name()); - - QBrush bgBrush(node->stateColour()); - - if(VProperty* p=VConfig::instance()->find("view.common.node_gradient")) - { - if(p->value().toBool()) - { - int lighter=150; - QColor bg=bgBrush.color(); - QColor bgLight=bg.lighter(lighter); - - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(0,1); - - grad.setColorAt(0,bgLight); - grad.setColorAt(1,bg); - bgBrush=QBrush(grad); - } - } - - QPalette labelPalette; - labelPalette.setBrush(QPalette::Window,bgBrush); - labelPalette.setColor(QPalette::WindowText,node->stateFontColour());//QColor(96,96,96)); - nodeLabel->setAutoFillBackground(true); - nodeLabel->setPalette(labelPalette); - - QString titleQss="QLabel {padding: 2px;}"; - nodeLabel->setStyleSheet(titleQss); - } - else - { - // multiple nodes selected - say how many - nodeLabel = new QLabel(QObject::tr("%1 nodes selected").arg(nodes.size())); - } - - QFont menuTitleFont; - menuTitleFont.setBold(true); - menuTitleFont.setItalic(true); - nodeLabel->setFont(menuTitleFont); - nodeLabel->setAlignment(Qt::AlignHCenter); - nodeLabel->setObjectName("nodeLabel"); - - QWidget* titleW=new QWidget(qmenu); - QVBoxLayout *titleLayout=new QVBoxLayout(titleW); - titleLayout->setContentsMargins(2,2,2,2); - titleLayout->addWidget(nodeLabel); - nodeLabel->setParent(titleW); - - QWidgetAction *wAction = new QWidgetAction(qmenu); - wAction->setObjectName("title"); - //Qt doc says: the ownership of the widget is passed to the widgetaction. - //So when the action is deleted it will be deleted as well. - wAction->setDefaultWidget(titleW); - wAction->setEnabled(false); - qmenu->addAction(wAction); -} - - -// ------------------------ -// MenuItem class functions -// ------------------------ - -MenuItem::MenuItem(const std::string &name) : - name_(name), - id_(idCnt_++), - hidden_(false), - multiSelect_(true), - visibleCondition_(NULL), - enabledCondition_(NULL), - questionCondition_(NULL), - isSubMenu_(false), - isDivider_(false), - isCustom_(false) -{ - if (name == "-") - { - isDivider_ = true; - } -} - -MenuItem::~MenuItem() -{ -} - -void MenuItem::setCommand(const std::string &command) -{ - command_ = command; - - //if (action_) - // action_->setStatusTip(QString(command.c_str())); // so we see the command in the status bar -} - -void MenuItem::setHandler(const std::string& handler) -{ - handler_ = handler; -} - -void MenuItem::setIcon(const std::string& icon) -{ - if(!icon.empty()) - { - icon_=QIcon(QPixmap(":/viewer/" + QString::fromStdString(icon))); - } -} - -bool MenuItem::shouldAskQuestion(std::vector &nodes) -{ - bool askQuestion = false; - - // ask the question if any of the nodes require it - for (std::vector::iterator itNodes = nodes.begin(); itNodes != nodes.end(); ++itNodes) - { - askQuestion = askQuestion || questionCondition()->execute(*itNodes); - } - - return askQuestion; -} - -bool MenuItem::isValidView(const std::string& view) const -{ - if(views_.empty()) - return true; - - return (std::find(views_.begin(),views_.end(),view) != views_.end()); -} - -QAction* MenuItem::createAction(QWidget* parent) -{ - QAction *ac=new QAction(parent); - ac->setObjectName(QString::fromStdString(name_)); - ac->setText(QString::fromStdString(name_)); - ac->setIcon(icon_); - - if(!statustip_.empty()) - { - if(statustip_ == "__cmd__") - ac->setStatusTip(QString::fromStdString(command_)); // so we see the command in the status bar - else - ac->setStatusTip(QString::fromStdString(statustip_)); - } - ac->setData(id_); - return ac; - -} - - -// // adds an entry to the list of valid node types for this menu item(*itItems) -// void MenuItem::addValidType(std::string type) -// { -// static NodeType all[] = {TASK, FAMILY, SUITE, SERVER, ALIAS}; -// -// if (type == "server") -// validNodeTypes_.push_back(SERVER); -// else if(type == "suite") -// validNodeTypes_.push_back(SUITE); -// else if(type == "task") -// validNodeTypes_.push_back(TASK); -// else if(type == "family") -// validNodeTypes_.push_back(FAMILY); -// else if(type == "alias") -// validNodeTypes_.push_back(ALIAS); -// else if(type == "all") -// validNodeTypes_.insert(validNodeTypes_.begin(), all, all+5); -// } -// -// -// // adds an entry to the list of valid node types for this menu item -// void MenuItem::addValidState(std::string state) -// { -// DState::State dstate; -// -// if (DState::isValid(state)) -// { -// dstate = DState::toState(state); -// validNodeStates_.push_back(dstate); -// } -// else if (state == "all") -// { -// // add the list of all states -// std::vector allDstates = DState::states(); -// validNodeStates_.insert(validNodeStates_.end(), allDstates.begin(), allDstates.end()); -// } -// else -// { -// UserMessage::message(UserMessage::ERROR, false, std::string("Bad node state in menu file: " + state)); -// } -// } - - -// bool MenuItem::isNodeTypeValidForMenuItem(NodeType type) -// { -// if(std::find(validNodeTypes_.begin(), validNodeTypes_.end(), type) == validNodeTypes_.end()) -// return false; -// else -// return true; -// } -// -// -// bool MenuItem::compatibleWithNode(VInfo_ptr nodeInfo) -// { -// // check each node type and return false if we don't match -// -// if(nodeInfo->isServer()) -// if(std::find(validNodeTypes_.begin(), validNodeTypes_.end(), SERVER) == validNodeTypes_.end()) -// return false; -// -// if(nodeInfo->isNode()) -// { -// Node *node = nodeInfo->node()->node(); -// -// if(node->isSuite()) -// if (!isNodeTypeValidForMenuItem(SUITE)) -// return false; -// -// if(node->isTask()) -// if (!isNodeTypeValidForMenuItem(TASK)) -// return false; -// -// if(node->isAlias()) -// if (!isNodeTypeValidForMenuItem(ALIAS)) -// return false; -// -// if(node->isFamily()) -// if (!isNodeTypeValidForMenuItem(FAMILY)) -// return false; -// } -// -// return true; -// } diff -Nru ecflow-4.9.0/Viewer/src/MenuHandler.hpp ecflow-4.11.1/Viewer/src/MenuHandler.hpp --- ecflow-4.9.0/Viewer/src/MenuHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MenuHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,186 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef MENUHANDLER_HPP_ -#define MENUHANDLER_HPP_ - -#include -#include -#include -#include -#include -#include - -#include "VInfo.hpp" -#include "NodeExpression.hpp" - -class QMenu; -class QAction; -class QWidget; -class Node; -class BaseNodeCondition; - - - -// ------------------------- -// MenuItem -// A single item in a menu -// ------------------------- - -class MenuItem -{ -public: - explicit MenuItem(const std::string &name); - ~MenuItem(); - - void setCommand(const std::string &command); - //bool compatibleWithNode(VInfo_ptr nodeInfo); - //void addValidType(std::string type); - //void addValidState(std::string type); - void setHandler(const std::string &handler); - void setViews(const std::vector &views) {views_=views;} - void setQuestion(const std::string &question) {question_=question;} - void setQuestionControl(const std::string &questionControl) {questionControl_=questionControl;} - void setWarning(const std::string &warning) {warning_=warning;} - void setIcon(const std::string &icon); - void setStatustip(const std::string &statustip) {statustip_=statustip;} - void setHidden(bool b) {hidden_=b;} - void setMultiSelect(bool b) {multiSelect_=b;} - void setAsSubMenu() {isSubMenu_ = true;} - void setVisibleCondition(BaseNodeCondition *cond) {visibleCondition_ = cond;} - void setEnabledCondition(BaseNodeCondition *cond) {enabledCondition_ = cond;} - void setQuestionCondition(BaseNodeCondition *cond) {questionCondition_ = cond;} - void setCustom(bool b) {isCustom_ = b;} - BaseNodeCondition *visibleCondition() const {return visibleCondition_;} - BaseNodeCondition *enabledCondition() const {return enabledCondition_;} - BaseNodeCondition *questionCondition() const {return questionCondition_;} - bool shouldAskQuestion(std::vector &nodes); - bool isSubMenu() const {return isSubMenu_;} - bool isDivider() const {return isDivider_;} - bool isCustom() const {return isCustom_;} - const std::string& name() const {return name_;} - const std::string& handler() const {return handler_;} - bool isValidView(const std::string&) const; - const std::string& command() const {return command_;} - const std::string& question() const {return question_;} - const std::string& questionControl() const {return questionControl_;} - const std::string& warning() const {return warning_;} - bool hidden() const {return hidden_;} - bool multiSelect() const {return multiSelect_;} - int id() const {return id_;} - QAction* createAction(QWidget* parent); - -private: - //No copy allowed - MenuItem(const MenuItem&); - MenuItem& operator=(const MenuItem&); - - //bool isNodeTypeValidForMenuItem(NodeType type); - - std::string name_; - int id_; - std::string tooltip_; - std::string command_; - std::string statustip_; - std::string question_; - std::string questionControl_; - std::string defaultAnswer_; - std::string warning_; - std::string handler_; - std::vector views_; - bool hidden_; - bool multiSelect_; //multiple selecttion - - //std::vector validNodeTypes_; - //std::vector validNodeStates_; - - - BaseNodeCondition *visibleCondition_; - BaseNodeCondition *enabledCondition_; - BaseNodeCondition *questionCondition_; - - bool isSubMenu_; - bool isDivider_; - bool isCustom_; - - QIcon icon_; - - static int idCnt_; -}; - - - -// ------------------------------------------------------------- -// Menu -// Contains all the possible items for a given menu. These will -// be filtered at run-time according to the state of -// the given item which has been clicked. -// ------------------------------------------------------------- - -class Menu -{ -public: - explicit Menu(const std::string &name); - ~Menu(); - QString exec(std::vector nodes); - std::string &name() {return name_;}; - void addItemToFixedList(MenuItem *item) {itemsFixed_.push_back(item);}; - void addItemToCustomList(MenuItem *item) {itemsCustom_.push_back(item);}; - void clearFixedList() {itemsFixed_.clear();} - QMenu *generateMenu(std::vector nodes, QWidget *parent,QMenu* parentMenu,const std::string& view,QList&); - std::vector& items() {return itemsCombined_;}; - -private: - void buildMenuTitle(std::vector nodes, QMenu* qmenu); - - std::string name_; - std::vector itemsFixed_; - std::vector itemsCustom_; - std::vector itemsCombined_; // items from config file plus custom commands - -}; - - -// -------------------------------------------------------------- -// MenuHandler -// Responsible for creating menus (read from configuration files) -// and generating 'actual' (i.e. context-dependent filtered) -// menus at run-time. -// -------------------------------------------------------------- - -class MenuHandler -{ -public: - MenuHandler(); - - //Menu *createMenu(QString &name); - static bool readMenuConfigFile(const std::string &configFile); - static MenuItem *invokeMenu(const std::string &menuName, std::vector nodes, QPoint pos, QWidget *parent,const std::string& view); - static bool addItemToMenu(MenuItem *item, const std::string &menuName); - static Menu *findMenu(const std::string &name); - static MenuItem* newItem(const std::string &name); - static void addMenu(Menu *menu) {menus_.push_back(menu);} - static void interceptCommandsThatNeedConfirmation(MenuItem *item); - static void refreshCustomMenuCommands(); - -private: - typedef std::map ConfirmationMap; - static MenuItem* findItem(QAction*); - static ConfirmationMap &getCommandsThatRequireConfirmation(); - - static std::vector menus_; - static ConfirmationMap commandsWhichRequireConfirmation_; - static TrueNodeCondition trueCond_; - static FalseNodeCondition falseCond_; - //static std::vector items_; - -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/MessageItemWidget.cpp ecflow-4.11.1/Viewer/src/MessageItemWidget.cpp --- ecflow-4.9.0/Viewer/src/MessageItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MessageItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "MessageItemWidget.hpp" - -#include -#include -#include - -#include "InfoProvider.hpp" -#include "VReply.hpp" - -#include "LogModel.hpp" - -static bool firstRun=true; - -MessageItemWidget::MessageItemWidget(QWidget *parent) : QWidget(parent) -{ - setupUi(this); - - searchTb_->setEnabled(false); - searchTb_->setVisible(false); - searchLine_->setVisible(false); - - infoProvider_=new MessageProvider(this); - - model_=new LogModel(this); - - treeView_->setProperty("log","1"); - treeView_->setModel(model_); - treeView_->setItemDelegate(new LogDelegate(this)); - treeView_->setContextMenuPolicy(Qt::ActionsContextMenu); - - syncTb_->hide(); - - //Define context menu - treeView_->addAction(actionCopyEntry_); - treeView_->addAction(actionCopyRow_); -} - -QWidget* MessageItemWidget::realWidget() -{ - return this; -} - -void MessageItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - info_=info; - - if(info_) - { - infoProvider_->info(info_); - } -} - -void MessageItemWidget::clearContents() -{ - InfoPanelItem::clear(); - model_->clearData(); -} - -void MessageItemWidget::infoReady(VReply* reply) -{ - model_->setData(reply->textVec()); - - //Adjust column size if it is the first run - if(firstRun && model_->hasData()) - { - firstRun=false; - for(int i=0; i < model_->columnCount()-1; i++) - { - treeView_->resizeColumnToContents(i); - } - } -} - -void MessageItemWidget::infoProgress(VReply* reply) -{ - QString s=QString::fromStdString(reply->text()); -} - -void MessageItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); -} - -void MessageItemWidget::on_actionCopyEntry__triggered() -{ - toClipboard(model_->entryText(treeView_->currentIndex())); -} - -void MessageItemWidget::on_actionCopyRow__triggered() -{ - toClipboard(model_->fullText(treeView_->currentIndex())); -} - -void MessageItemWidget::toClipboard(QString txt) const -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QClipboard* cb=QGuiApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#else - QClipboard* cb=QApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#endif -} - -static InfoPanelItemMaker maker1("message"); diff -Nru ecflow-4.9.0/Viewer/src/MessageItemWidget.hpp ecflow-4.11.1/Viewer/src/MessageItemWidget.hpp --- ecflow-4.9.0/Viewer/src/MessageItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MessageItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef MESSAGEITEMWIDGET_HPP_ -#define MESSAGEITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" - -#include "ui_MessageItemWidget.h" - -class LogModel; - -class MessageItemWidget : public QWidget, public InfoPanelItem, protected Ui::MessageItemWidget -{ - Q_OBJECT -public: - explicit MessageItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected Q_SLOTS: - void on_actionCopyEntry__triggered(); - void on_actionCopyRow__triggered(); - -protected: - void updateState(const ChangeFlags&) {} - void toClipboard(QString txt) const; - - LogModel* model_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/MessageItemWidget.ui ecflow-4.11.1/Viewer/src/MessageItemWidget.ui --- ecflow-4.9.0/Viewer/src/MessageItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MessageItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ - - - MessageItemWidget - - - - 0 - 0 - 510 - 465 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show search bar - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - Refresh messages - - - ... - - - - :/viewer/sync.svg:/viewer/sync.svg - - - true - - - - - - - - - - - - QAbstractItemView::ExtendedSelection - - - false - - - true - - - true - - - - - - - - - - Copy text of &Entry - - - Copy text of the log entry - - - Ctrl+C - - - - - Copy text of full &row - - - Copy text of full row - - - - - - MessageLabel - QWidget -
        MessageLabel.hpp
        - 1 -
        - - PlainTextSearchLine - QWidget -
        PlainTextSearchLine.hpp
        - 1 -
        - - TreeView - QTreeView -
        TreeView.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/MessageLabel.cpp ecflow-4.11.1/Viewer/src/MessageLabel.cpp --- ecflow-4.9.0/Viewer/src/MessageLabel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MessageLabel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,281 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "MessageLabel.hpp" - -#include -#include -#include -#include -#include -#include - -#include "IconProvider.hpp" -#include "UiLog.hpp" - -#include -#include - -class MessageLabelData { -public: - MessageLabelData(QString iconPath,QString title,QColor bg, QColor bgLight,QColor border) : - title_(title), bg_(bg.name()), border_(border.name()) - { - int id=IconProvider::add(iconPath,iconPath); - pix_=IconProvider::pixmap(id,16); - pixSmall_=IconProvider::pixmap(id,12); - - bg_="qlineargradient(x1: 0, y1: 0, x2: 0, y2: 1, stop: 0 " + bg.name() +", stop: 1 " + bgLight.name() + ")"; - } - - MessageLabelData() {} - - QPixmap pix_; - QPixmap pixSmall_; - QString title_; - QString bg_; - QString border_; - -}; - -static std::map typeData; - -MessageLabel::MessageLabel(QWidget *parent) : - QWidget(parent), - showTypeTitle_(true), - narrowMode_(false), - currentType_(NoType) -{ - setProperty("base","1"); - - if(typeData.empty()) - { - QColor bg(236,246,252); - QColor bgLight=bg.lighter(105); - typeData[InfoType]=MessageLabelData(":/viewer/info.svg","Info",bg,bgLight,QColor(180,194,230)); - - bg=QColor(234,215,150); - bgLight=bg.lighter(112); - typeData[WarningType]=MessageLabelData(":/viewer/warning.svg","Warning",bg,bgLight,QColor(226,170,91)); //QColor(226,195,110)); //226,170,91 - - bg=QColor(255,231,231); - bgLight=bg.lighter(105); - typeData[ErrorType]=MessageLabelData(":/viewer/error.svg","Error",bg,bgLight,QColor(223,152,152)); - - bg=QColor(232,249,236); - bgLight=bg.lighter(105); - typeData[TipType]=MessageLabelData(":/viewer/tip.svg","Tip",bg,bgLight,QColor(190,220,190)); - } - - pixLabel_=new QLabel(this); - pixLabel_->setAlignment(Qt::AlignCenter); - - msgLabel_=new QLabel(this); - msgLabel_->setWordWrap(true); - msgLabel_->setTextInteractionFlags(Qt::LinksAccessibleByMouse|Qt::TextSelectableByKeyboard|Qt::TextSelectableByMouse); - - loadLabel_=new QLabel(this); - - //progress widget - progLabel_=new QLabel(this); - progBar_=new QProgressBar(this); - progWidget_=new QWidget(this); - QHBoxLayout* progLayout=new QHBoxLayout(progWidget_); - progLayout->addWidget(progBar_); - progLayout->addWidget(progLabel_); - - layout_=new QHBoxLayout(this); - layout_->setContentsMargins(2,2,2,2); - - QVBoxLayout *loadLayout=new QVBoxLayout(); - loadLayout->addWidget(loadLabel_); - loadLayout->addStretch(1); - layout_->addLayout(loadLayout); - - QVBoxLayout *pixLayout=new QVBoxLayout(); - pixLayout->addWidget(pixLabel_); - pixLayout->addStretch(1); - layout_->addLayout(pixLayout); - - QVBoxLayout* rightVb=new QVBoxLayout; - rightVb->addWidget(msgLabel_); - rightVb->addWidget(progWidget_); - rightVb->addStretch(1); - layout_->addLayout(rightVb,1); - - //layout_->addWidget(msgLabel_,1); - - stopLoadLabel(); - stopProgress(); - - hide(); -} - -void MessageLabel::clear() -{ - msgLabel_->setText(""); - message_.clear(); - stopLoadLabel(); - stopProgress(); -} - -void MessageLabel::showInfo(QString msg) -{ - showMessage(InfoType,msg); -} - -void MessageLabel::showWarning(QString msg) -{ - showMessage(WarningType,msg); -} - -void MessageLabel::showError(QString msg) -{ - showMessage(ErrorType,msg); -} - -void MessageLabel::showTip(QString msg) -{ - showMessage(TipType,msg); -} - -void MessageLabel::appendInfo(QString msg) -{ - appendMessage(InfoType,msg); -} - -void MessageLabel::appendWarning(QString msg) -{ - appendMessage(WarningType,msg); -} - -void MessageLabel::appendError(QString msg) -{ - appendMessage(ErrorType,msg); -} - -void MessageLabel::appendTip(QString msg) -{ - appendMessage(TipType,msg); -} - -void MessageLabel::showMessage(const Type& type,QString msg) -{ - message_=msg; - std::map::const_iterator it=typeData.find(type); - assert(it != typeData.end()); - - if(type != currentType_) - { - QString sh="QWidget[base=\"1\"] { \ - background: " + it->second.bg_ + "; \ - border: 1px solid " + it->second.border_ + "; \ - border-radius: 0px;}"; - - setStyleSheet(sh); - - pixLabel_->setPixmap(((!narrowMode_)?it->second.pix_:it->second.pixSmall_)); - - currentType_=type; - message_.clear(); - } - - message_=msg; - - QString s=message_; - s.replace("\n","
        "); - if(showTypeTitle_) - s="" + it->second.title_ + ": " + s; - - msgLabel_->setText(s); - - show(); -} - -void MessageLabel::appendMessage(const Type& type,QString msg) -{ - message_+=msg; - showMessage(type,message_); -} - -void MessageLabel::startLoadLabel() -{ - if(!loadLabel_->movie()) - { - QMovie *movie = new QMovie(":viewer/spinning_wheel.gif", QByteArray(), loadLabel_); - loadLabel_->setMovie(movie); - } - loadLabel_->show(); - loadLabel_->movie()->start(); -} - - -void MessageLabel::stopLoadLabel() -{ - if(loadLabel_->movie()) - { - loadLabel_->movie()->stop(); - } - - loadLabel_->hide(); -} - -void MessageLabel::startProgress(int max) -{ - Q_ASSERT(max >=0 && max <=100); - progBar_->setRange(0,max); - progWidget_->show(); -} - -void MessageLabel::stopProgress() -{ - progWidget_->hide(); - progLabel_->setText(""); - progBar_->setRange(0,0); -} - -void MessageLabel::progress(QString text,int value) -{ - Q_ASSERT(value >=0 && value <=100); - - if(progBar_->maximum() == 0) - progBar_->setMaximum(100); - - progBar_->setValue(value); - progLabel_->setText(text); - - //UiLog().dbg() << "MessageLabel::progress --> " << value << "%"; -} - -void MessageLabel::setShowTypeTitle(bool b) -{ - if(showTypeTitle_ != b) - { - showTypeTitle_=b; - } -} - -void MessageLabel::setNarrowMode(bool b) -{ - if(b==narrowMode_) - return; - - narrowMode_=b; - - /*if(!narrowMode_) - { - layout_->setContentsMargins(2,2,2,2); - } - else - { - layout_->setContentsMargins(2,0,2,0); - }*/ -} - diff -Nru ecflow-4.9.0/Viewer/src/MessageLabel.hpp ecflow-4.11.1/Viewer/src/MessageLabel.hpp --- ecflow-4.9.0/Viewer/src/MessageLabel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MessageLabel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef MESSAGELABEL_HPP_ -#define MESSAGELABEL_HPP_ - -#include - -class QHBoxLayout; -class QLabel; -class QProgressBar; - -class MessageLabel : public QWidget -{ -public: - explicit MessageLabel(QWidget *parent=0); - - enum Type {NoType,InfoType,WarningType,ErrorType,TipType}; - - void showInfo(QString); - void showWarning(QString); - void showError(QString); - void showTip(QString); - void appendInfo(QString); - void appendWarning(QString); - void appendError(QString); - void appendTip(QString); - void startLoadLabel(); - void stopLoadLabel(); - void startProgress(int max=0); - void stopProgress(); - void progress(QString text,int value); - void setShowTypeTitle(bool); - void clear(); - void setNarrowMode(bool); - -private: - void showMessage(const Type&,QString); - void appendMessage(const Type&,QString); - - bool showTypeTitle_; - bool narrowMode_; - Type currentType_; - QLabel *pixLabel_; - QLabel* msgLabel_; - QLabel* loadLabel_; - QHBoxLayout* layout_; - QWidget* progWidget_; - QLabel* progLabel_; - QProgressBar* progBar_; - QString message_; - -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/MeterEditor.cpp ecflow-4.11.1/Viewer/src/MeterEditor.cpp --- ecflow-4.9.0/Viewer/src/MeterEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MeterEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,135 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "MeterEditor.hpp" - -#include -#include - -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "SessionHandler.hpp" - -MeterEditorWidget::MeterEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); -} - -MeterEditor::MeterEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"meter",parent), oriVal_(0) -{ - w_=new MeterEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "meter"); - - if(a->data().count() < 5) - return; - - oriVal_=a->data().at(2).toInt(); - int min=a->data().at(3).toInt(); - int max=a->data().at(4).toInt(); - - if(min > max || oriVal_ < min || oriVal_ > max) - return; - - w_->nameLabel_->setText(a->data().at(1)); - w_->valueSpin_->setRange(min,max); - w_->valueSpin_->setValue(oriVal_); - - w_->minLabel_->setText(a->data().at(3)); - w_->maxLabel_->setText(a->data().at(4)); - w_->thresholdLabel_->setText(a->data().at(5)); - - w_->valueSpin_->setFocus(); - - header_->setInfo(QString::fromStdString(info_->path()),"Meter"); - - connect(w_->valueSpin_,SIGNAL(valueChanged(int)), - this,SLOT(slotValueChanged(int))); - - checkButtonStatus(); - - readSettings(); -} - -MeterEditor::~MeterEditor() -{ - writeSettings(); -} - -void MeterEditor::apply() -{ - std::string val=QString::number(w_->valueSpin_->value()).toStdString(); - std::string name=w_->nameLabel_->text().toStdString(); - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","meter",name,val); - CommandHandler::run(info_,cmd); -} - -void MeterEditor::resetValue() -{ - w_->valueSpin_->setValue(oriVal_); - checkButtonStatus(); -} - -void MeterEditor::slotValueChanged(int) -{ - checkButtonStatus(); -} - -bool MeterEditor::isValueChanged() -{ - return (oriVal_ != w_->valueSpin_->value()); -} - -void MeterEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("MeterEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void MeterEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("MeterEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(310,200)); - } - - settings.endGroup(); -} - -static AttributeEditorMaker makerStr("meter"); - diff -Nru ecflow-4.9.0/Viewer/src/MeterEditor.hpp ecflow-4.11.1/Viewer/src/MeterEditor.hpp --- ecflow-4.9.0/Viewer/src/MeterEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MeterEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef METEREDITOR_HPP -#define METEREDITOR_HPP - -#include "ui_MeterEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -class MeterEditor; - -class MeterEditorWidget : public QWidget, protected Ui::MeterEditorWidget -{ -friend class MeterEditor; -public: - MeterEditorWidget(QWidget *parent=0); -}; - -class MeterEditor : public AttributeEditor -{ -Q_OBJECT - -public: - MeterEditor(VInfo_ptr,QWidget* parent=0); - ~MeterEditor(); - -protected Q_SLOTS: - void slotValueChanged(int); - -protected: - void apply(); - void resetValue(); - bool isValueChanged(); - void readSettings(); - void writeSettings(); - - MeterEditorWidget* w_; - int oriVal_; -}; - -#endif // METEREDITOR_HPP - - diff -Nru ecflow-4.9.0/Viewer/src/MeterEditorWidget.ui ecflow-4.11.1/Viewer/src/MeterEditorWidget.ui --- ecflow-4.9.0/Viewer/src/MeterEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/MeterEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ - - - MeterEditorWidget - - - - 0 - 0 - 329 - 227 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Name: - - - - - - - TextLabel - - - - - - - Value: - - - - - - - - - - Minimum: - - - - - - - TextLabel - - - - - - - Maximum: - - - - - - - TextLabel - - - - - - - Threshold: - - - - - - - TextLabel - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ModelColumn.cpp ecflow-4.11.1/Viewer/src/ModelColumn.cpp --- ecflow-4.9.0/Viewer/src/ModelColumn.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ModelColumn.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,74 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ModelColumn.hpp" - -#include - -#include - -#include "VConfigLoader.hpp" -#include "VProperty.hpp" - -static std::map defs; - -ModelColumn::ModelColumn(const std::string& id) : id_(id) -{ - defs[id_]=this; -} - -ModelColumn* ModelColumn::def(const std::string& id) -{ - std::map::const_iterator it=defs.find(id); - if(it != defs.end()) - return it->second; - return NULL; -} - - -int ModelColumn::indexOf(const std::string& id) const -{ - QString idStr=QString::fromStdString(id); - for(int i=0; i< items_.count(); i++) - { - if(items_.at(i)->id_ == idStr) - return i; - } - return -1; -} - -void ModelColumn::load(VProperty* group) -{ - ModelColumn* m=new ModelColumn(group->strName()); - - for(int i=0; i < group->children().size(); i++) - { - VProperty *p=group->children().at(i); - - ModelColumnItem* obj=new ModelColumnItem(p->strName()); - obj->label_=p->param("label"); - obj->tooltip_=p->param("tooltip"); - obj->icon_=p->param("icon"); - obj->index_=i; - - m->items_ << obj; - - } -} - -ModelColumnItem::ModelColumnItem(const std::string& id) : index_(-1) -{ - id_=QString::fromStdString(id); -} - -static SimpleLoader loaderQuery("query_columns"); -static SimpleLoader loaderTable("table_columns"); -static SimpleLoader loaderZombie("zombie_columns"); -static SimpleLoader loaderTriggerGraph("trigger_graph_columns"); -static SimpleLoader loaderOutput("output_columns"); diff -Nru ecflow-4.9.0/Viewer/src/ModelColumn.hpp ecflow-4.11.1/Viewer/src/ModelColumn.hpp --- ecflow-4.9.0/Viewer/src/ModelColumn.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ModelColumn.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef MODELCOLUMN_H -#define MODELCOLUMN_H - -class VProperty; - -#include - -#include -#include - - -class ModelColumnItem -{ -friend class ModelColumn; -public: - explicit ModelColumnItem(const std::string& id); - -protected: - QString label_; - QString id_; - int index_; - QString icon_; - QString tooltip_; -}; - - -class ModelColumn -{ -public: - explicit ModelColumn(const std::string& id); - - int count() const {return items_.size();} - int indexOf(const std::string&) const; - QString id(int i) const {assert(i>=0 && i < count()); return items_.at(i)->id_;} - QString label(int i) const {assert(i>=0 && i < count()); return items_.at(i)->label_;} - QString tooltip(int i) const {assert(i>=0 && i < count()); return items_.at(i)->tooltip_;} - - static ModelColumn* def(const std::string& id); - - //Called from VConfigLoader - static void load(VProperty* group); - -protected: - std::string id_; - QList items_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeExpression.cpp ecflow-4.11.1/Viewer/src/NodeExpression.cpp --- ecflow-4.9.0/Viewer/src/NodeExpression.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeExpression.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1064 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include - -#include - -#include "Str.hpp" -#include "Node.hpp" -#include "Submittable.hpp" - -#include "NodeExpression.hpp" -#include "MenuHandler.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "UIDebug.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" -#include "VNodeMover.hpp" - -//#define _UI_NODEXPRESSIONPARSEER_DEBUG - -// ------------------------- -// Expression parser classes -// ------------------------- - -NodeExpressionParser* NodeExpressionParser::instance_=NULL; - - -NodeExpressionParser* NodeExpressionParser::instance() -{ - if(!instance_) - instance_=new NodeExpressionParser; - - return instance_; -} - -NodeExpressionParser::NodeExpressionParser() -{ - nameToNodeType_["server"]=SERVER; - nameToNodeType_["suite"]=SUITE; - nameToNodeType_["family"]=FAMILY; - nameToNodeType_["task"]=TASK; - nameToNodeType_["alias"]=ALIAS; - nameToNodeType_["node"]=NODE; - - for(std::map::const_iterator it=nameToNodeType_.begin(); it != nameToNodeType_.end(); ++it) - { - nodeTypeToName_[it->second]=it->first; - } - - QStringList attrNames; - attrNames << "meter" << "event" << "repeat" << "trigger" << "label" << "time" << "date" << "late" << "limit" << - "limit" << "limiter" << "var" << "genvar"; - Q_FOREACH(QString s,attrNames) - { - VAttributeType *t=VAttributeType::find(s.toStdString()); - Q_ASSERT(t); - nameToAttrType_[s.toStdString()]=t; - } - - badTypeStr_="BAD"; - badAttributeStr_="BAD"; -} - -NodeExpressionParser::NodeType NodeExpressionParser::nodeType(const std::string &name) const -{ - std::map::const_iterator it=nameToNodeType_.find(name); - if(it != nameToNodeType_.end()) - return it->second; - - return BAD; -} - -const std::string& NodeExpressionParser::typeName(const NodeType& type) const -{ - std::map::const_iterator it=nodeTypeToName_.find(type); - if(it != nodeTypeToName_.end()) - return it->second; - - return badTypeStr_; -} - -VAttributeType* NodeExpressionParser::toAttrType(const std::string &name) const -{ - std::map::const_iterator it=nameToAttrType_.find(name); - if(it != nameToAttrType_.end()) - return it->second; - - return NULL; -} - -bool NodeExpressionParser::isMenuMode(const std::string &str) const -{ - if (str == "oper" || str == "admin") - return true; - - return false; -} - -bool NodeExpressionParser::isNodeHasAttribute(const std::string &str) const -{ - if (str == "has_triggers" || str == "has_time" || str == "has_date" || str == "locked") - return true; - - return false; -} - -bool NodeExpressionParser::isNodeFlag(const std::string &str) const -{ - if (str == "is_late" || str == "has_message" || - str == "is_rerun" || str == "is_waiting" || str == "is_zombie" || str == "is_migrated" || - str == "is_ecfcmd_failed" || str == "is_killed") - return true; - - return false; -} - - -bool NodeExpressionParser::isWhatToSearchIn(const std::string &str, bool &isAttr) const -{ - // list of non-attribute items that we can search in - if (str == "node_name" || str == "node_path") - { - isAttr = false; - return true; - } - - // list of attributes that we can search in - else if (str == "var_name" || str =="var_value" || str =="var_type" || - str == "label_name" || str == "label_value" || - str == "meter_name" || str == "meter_value" || - str == "event_name" || str == "event_value" || - str == "date_name" || str == "time_name" || - str == "limit_name" || str == "limit_value" || str == "limit_max" || - str == "limiter_name" || - str == "repeat_name" || str == "repeat_value" || - str == "trigger_expression" ) - { - isAttr = true; - return true; - } - - return false; -} - - -bool NodeExpressionParser::isAttributeState(const std::string &str) const -{ - return (str == "event_set" || str == "event_clear" || - str == "repeat_date" || str == "repeat_int" || str == "repeat_string" || str == "repeat_enum" || - str == "repeat_day"); -} - -bool NodeExpressionParser::isIsoDate(const std::string &str) const -{ - if(str.size() == 19) - { - QDateTime d=QDateTime::fromString(QString::fromStdString(str),Qt::ISODate); - return d.isValid(); - } - return false; -} - -// NodeExpressionParser::popLastNOperands -// - utility function to remove and return the last n operands from -// - the stack -std::vector NodeExpressionParser::popLastNOperands(std::vector &inOperands, int n) -{ - std::vector resultVec; - - for (int i=0; i tokens; - char delimiter = ' '; - char insideQuote = '\0'; // \0 if not inside a quote, \' if we are inside a quote - // will not handle the case of nested quotes! - - UiLog().dbg() << "parseWholeExpression: " << expr; - - ecf::Str::replace_all(expr, std::string("("), std::string(" ( ")); - ecf::Str::replace_all(expr, std::string(")"), std::string(" ) ")); - - //boost::algorithm::to_lower(expr); // convert to lowercase - - int index = 0; - int length = expr.length(); - std::string token = ""; - - // loop through each character in the string - - while (index < length) - { - char c = expr[index]; - - if (c == '\'') // a quote character? - { - if (insideQuote == '\'') // this is the closing quote - insideQuote = '\0'; // note that we are no longer inside a quote - else - insideQuote = '\''; // this is an opening quote - } - else if (c == delimiter && insideQuote == '\0') // a delimeter but not inside a quote? - { - if (token.length()>0) - tokens.push_back(token); - token =""; - } - else - token += c; - - index++; - } - - if(token.length()>0) - tokens.push_back(token); - - - setTokens(tokens); - - return parseExpression(caseSensitiveStringMatch); -} - - -BaseNodeCondition *NodeExpressionParser::parseExpression(bool caseSensitiveStringMatch) -{ - bool returnEarly = false; - BaseNodeCondition *result = NULL; - - std::vector funcStack; - std::vector operandStack; - - - // short-circuit - if empty, then return a True condition - - if(tokens_.size() == 0) - { - result=new TrueNodeCondition(); - } - - while (!returnEarly && i_ != tokens_.end()) - { - bool tokenOk = true; - bool updatedOperands = false; - - if (i_ != tokens_.end()) - { - - // are we expecting an arbitrary string? - if ((funcStack.size() > 0) && (funcStack.back()->operand2IsArbitraryString())) - { - WhatToSearchForOperand *whatToSearchFor = new WhatToSearchForOperand(*i_); - operandStack.push_back(whatToSearchFor); - result = whatToSearchFor; - updatedOperands = true; - } - else - { - bool attr = false; - VAttributeType* attrType=NULL; - //NodeExpressionParser::AttributeType attrType=NodeExpressionParser::BADATTRIBUTE; - - // node types - NodeExpressionParser::NodeType type = nodeType(*i_); - if (type != BAD) - { - TypeNodeCondition *typeCond = new TypeNodeCondition(type); - operandStack.push_back(typeCond); - result = typeCond; - updatedOperands = true; - } - - // node/server states - else if (DState::isValid(*i_) || VSState::find(*i_)) - { - StateNodeCondition *stateCond = new StateNodeCondition(QString::fromStdString(*i_)); - operandStack.push_back(stateCond); - result = stateCond; - updatedOperands = true; - } - - // node mneu mode - else if (isMenuMode(*i_)) - { - NodeMenuModeCondition *userCond = new NodeMenuModeCondition(QString::fromStdString(*i_)); - operandStack.push_back(userCond); - result = userCond; - updatedOperands = true; - } - - // node has attribute - else if (isNodeHasAttribute(*i_)) - { - NodeAttributeCondition *attrCond = new NodeAttributeCondition(QString::fromStdString(*i_)); - operandStack.push_back(attrCond); - result = attrCond; - updatedOperands = true; - } - // node flag - else if (isNodeFlag(*i_)) - { - NodeFlagCondition *flagCond = new NodeFlagCondition(QString::fromStdString(*i_)); - operandStack.push_back(flagCond); - result = flagCond; - updatedOperands = true; - } - // node status change date - else if (*i_ == "status_change_time") - { - NodeStatusChangeDateCondition *chDateCond = new NodeStatusChangeDateCondition(); - operandStack.push_back(chDateCond); - result = chDateCond; - updatedOperands = true; - } - // node attribute type - //else if ((attrType = toAttrType(*i_)) != NodeExpressionParser::BADATTRIBUTE) - else if ((attrType = toAttrType(*i_)) != NULL) - { - AttributeCondition *attrCond = new AttributeCondition(attrType); - operandStack.push_back(attrCond); - result = attrCond; - updatedOperands = true; - } - - // node attribute state - else if (isAttributeState(*i_)) - { - AttributeStateCondition *attrStateCond = new AttributeStateCondition(QString::fromStdString(*i_)); - operandStack.push_back(attrStateCond); - result = attrStateCond; - updatedOperands = true; - } - - else if (isWhatToSearchIn(*i_, attr)) - { - WhatToSearchInOperand *searchCond = new WhatToSearchInOperand(*i_, attr); - operandStack.push_back(searchCond); - result = searchCond; - updatedOperands = true; - } - - // isoDate - else if (isIsoDate(*i_)) - { - IsoDateCondition *dateCond = new IsoDateCondition(QString::fromStdString(*i_)); - operandStack.push_back(dateCond); - result = dateCond; - updatedOperands = true; - } - - //iso date operator - else if (*i_ == "date::<=") - { - IsoDateLessThanEqualCondition *dateCond = new IsoDateLessThanEqualCondition(); - funcStack.push_back(dateCond); - result = dateCond; - } - - //iso date operator - else if (*i_ == "date::>=") - { - IsoDateGreaterThanEqualCondition *dateCond = new IsoDateGreaterThanEqualCondition(); - funcStack.push_back(dateCond); - result = dateCond; - } - - else if (*i_ == "marked") - { - UIStateCondition *uiCond = new UIStateCondition(*i_); - operandStack.push_back(uiCond); - result = uiCond; - updatedOperands = true; - } - - // logical operators - else if (*i_ == "and") - { - AndNodeCondition *andCond = new AndNodeCondition(); - funcStack.push_back(andCond); - result = andCond; - } - - else if (*i_ == "or") - { - OrNodeCondition *orCond = new OrNodeCondition(); - funcStack.push_back(orCond); - result = orCond; - } - - else if (*i_ == "not") - { - NotNodeCondition *notCond = new NotNodeCondition(); - funcStack.push_back(notCond); - result = notCond; - } - - else if(StringMatchMode::operToMode(*i_) != StringMatchMode::InvalidMatch) - { - StringMatchCondition *stringMatchCond = new StringMatchCondition(StringMatchMode::operToMode(*i_), caseSensitiveStringMatch); - funcStack.push_back(stringMatchCond); - result = stringMatchCond; - } - - else if (*i_ == "(") - { - ++i_; - result = parseExpression(caseSensitiveStringMatch); - operandStack.push_back(result); - } - else if (*i_ == ")") - { - returnEarly = true; - } - - else - { - tokenOk = false; - } - } - } - - else - { - // got to the end of the tokens, but we may still need to - // update the condition stacks - updatedOperands = true; - } - - - if (tokenOk) - { - // if there are enough operands on the stack for the last - // function, pop them off and create a small tree for that function - // but do not do this if the last function asks to delay this process - if (!funcStack.empty() && !funcStack.back()->delayUnwinding()) - { - if(updatedOperands && (static_cast(operandStack.size()) >= funcStack.back()->numOperands())) - { - std::vector operands; - result = funcStack.back(); // last function is the current result - operands = popLastNOperands(operandStack, result->numOperands()); // pop its operands off the stack - result->setOperands(operands); - funcStack.pop_back(); // remove the last function from the stack - operandStack.push_back(result); // store the current result - } - } - } - else - { - UiLog().err() << "Error parsing expression " << *i_; - result = new FalseNodeCondition(); - return result; - } - - if (i_ != tokens_.end() && !returnEarly) - ++i_; // move onto the next token - } - - - int iterCnt=0; //to avoid infinite loop we use this counter - - // final unwinding of the stack - while (!funcStack.empty()) - { - if(static_cast(operandStack.size()) >= funcStack.back()->numOperands()) - { - std::vector operands; - result = funcStack.back(); // last function is the current result - operands = popLastNOperands(operandStack, result->numOperands()); // pop its operands off the stack - result->setOperands(operands); - funcStack.pop_back(); // remove the last function from the stack - operandStack.push_back(result); // store the current result - iterCnt=0; - } - else - { - iterCnt++; - if(iterCnt > 10) - { - if(result) - { - delete result; - result=NULL; - } - break; - } - } - } - - if(result) - UiLog().dbg() << " " << result->print(); - - return result; -} - - -bool BaseNodeCondition::execute(VInfo_ptr nodeInfo) -{ - if(!nodeInfo) - return true; - - if(nodeInfo->isServer()) - return execute(nodeInfo->server()->vRoot()); - else if(nodeInfo->isNode()) - return execute(nodeInfo->node()); - else if(nodeInfo->isAttribute()) - return execute(nodeInfo->attribute()); - - return false; -} - -// ----------------------------------------------------------------- - -bool BaseNodeCondition::containsAttributeSearch() -{ - bool contains = false; - - // check child condition nodes - for(std::size_t i = 0; i < operands_.size(); i++) - { - contains = contains | operands_[i]->containsAttributeSearch(); - } - - // check this condition node - contains = contains | searchInAttributes(); - - return contains; -} - -//========================================================================= -// -// AndNodeCondition -// -//========================================================================= - -bool AndNodeCondition::execute(VItem* node) -{ - return operands_[0]->execute(node) && operands_[1]->execute(node); -} - -//========================================================================= -// -// OrNodeCondition -// -//========================================================================= - -bool OrNodeCondition::execute(VItem* node) -{ -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << "OrNodeCondition::execute --->"; - UiLog().dbg() << operands_[0]->execute(node) << " " << operands_[1]->execute(node); -#endif - return operands_[0]->execute(node) || operands_[1]->execute(node); -} - - -//========================================================================= -// -// NotNodeCondition -// -//========================================================================= - -bool NotNodeCondition::execute(VItem* node) -{ - return !(operands_[0]->execute(node)); -} - -//========================================================================= -// -// TypeNodeCondition -// -//========================================================================= - -bool TypeNodeCondition::execute(VItem* item) -{ - if (type_ == NodeExpressionParser::SERVER) - { - return (item->isServer() != NULL); - } - - else if(item->isNode()) - { -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << "TypeNodeCondition::execute --> " << NodeExpressionParser::instance()->typeName(type_); - UiLog().dbg() << item->isNode() << " " << item->isSuite() << " " << item->isFamily() << - " " << item->isTask() << " " << item->isAlias(); -#endif - switch(type_) - { - case NodeExpressionParser::NODE: -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << " NODE"; -#endif - return true; - break; - case NodeExpressionParser::SUITE: -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << " SUITE"; -#endif - return (item->isSuite() != NULL); - break; - case NodeExpressionParser::TASK: -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << " TASK"; -#endif - return (item->isTask() != NULL); - break; - case NodeExpressionParser::FAMILY: -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << " FAMILY"; -#endif - return (item->isFamily() != NULL); - break; - case NodeExpressionParser::ALIAS: -#ifdef _UI_NODEXPRESSIONPARSEER_DEBUG - UiLog().dbg() << " ALIAS"; -#endif - return (item->isAlias() != NULL); - break; - default: - break; - } - } - - return false; -} - -//========================================================================= -// -// StateNodeCondition -// -//========================================================================= - -bool StateNodeCondition::execute(VItem* item) -{ - if(item->isServer()) - { - VServer* s=static_cast(item); - assert(s); - return (s->serverStateName() == stateName_); - } - else - { - VNode* n=static_cast(item); - assert(n); - return (n->stateName() == stateName_); - } - return false; -} - -//========================================================================= -// -// UserMenuModeCondition -// -//========================================================================= - -bool NodeMenuModeCondition::execute(VItem* item) -{ - if(item) - { - return (item->nodeMenuMode() == menuModeName_); - } - return false; -} - - -//========================================================================= -// -// UIStateCondition -// -//========================================================================= - -bool UIStateCondition::execute(VItem*) -{ - if (uiStateName_ == "marked") - { - return VNodeMover::hasMarkedForMove(); - } - - return false; -} - - -//========================================================================= -// -// String match utility functions -// -//========================================================================= - -bool StringMatchExact::match(std::string searchFor, std::string searchIn) -{ - return searchFor == searchIn; -} - -bool StringMatchContains::match(std::string searchFor, std::string searchIn) -{ - Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; - QRegExp regexp(QString::fromStdString(searchFor), cs); - int index = regexp.indexIn(QString::fromStdString(searchIn)); - return (index != -1); // -1 means no match -} - -bool StringMatchWildcard::match(std::string searchFor, std::string searchIn) -{ - Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; - QRegExp regexp(QString::fromStdString(searchFor), cs); - regexp.setPatternSyntax(QRegExp::Wildcard); - return regexp.exactMatch(QString::fromStdString(searchIn)); -} - -bool StringMatchRegexp::match(std::string searchFor, std::string searchIn) -{ - Qt::CaseSensitivity cs = (caseSensitive_) ? Qt::CaseSensitive : Qt::CaseInsensitive; - QRegExp regexp(QString::fromStdString(searchFor), cs); - return regexp.exactMatch(QString::fromStdString(searchIn)); -} - -//========================================================================= -// -// String match condition -// -//========================================================================= - -StringMatchCondition::StringMatchCondition(StringMatchMode::Mode matchMode, bool caseSensitive) -{ - switch (matchMode) - { - case StringMatchMode::ContainsMatch: - matcher_ = new StringMatchContains(caseSensitive); - break; - case StringMatchMode::WildcardMatch: - matcher_ = new StringMatchWildcard(caseSensitive); - break; - case StringMatchMode::RegexpMatch: - matcher_ = new StringMatchRegexp(caseSensitive); - break; - default: - UiLog().dbg() << "StringMatchCondition: bad matchMode"; - matcher_ = new StringMatchExact(caseSensitive); - break; - } -} - -bool StringMatchCondition::execute(VItem *item) -{ - WhatToSearchForOperand *searchForOperand = static_cast (operands_[0]); - WhatToSearchInOperand *searchInOperand = static_cast (operands_[1]); - - std::string searchIn = searchInOperand->what(); - - if(item->isNode()) - { - VNode* n=static_cast(item); - //TODO XXXX check - name, label, variable, etc - if(searchIn == "node_name") - { - return matcher_->match(searchForOperand->what(), n->strName()); - } - - else if (searchIn == "node_path") - { - return matcher_->match(searchForOperand->what(), n->absNodePath()); - } - } - else if(VAttribute* a=item->isAttribute()) - { - std::string str; - if(a->value(searchIn,str)) - return matcher_->match(searchForOperand->what(),str); - else - return false; - } - - return false; -} - -// ----------------------------------------------------------------- - -bool NodeAttributeCondition::execute(VItem* item) -{ - if (item->isServer()) - { - if(nodeAttrName_ == "locked") - { - return false; // XXX temporary for now - } - } - - else if(item->isNode()) - { - VNode* n=static_cast(item); - node_ptr node = n->node(); - - if (nodeAttrName_ == "has_time") - { - return (node->timeVec().size() > 0 || - node->todayVec().size() > 0 || - node->crons().size() > 0); - } - else if (nodeAttrName_ == "has_date") - { - return (node->days().size() > 0 || - node->dates().size() > 0); - } - else if (nodeAttrName_ == "has_triggers") - { - return (node->triggerAst() || - node->completeAst()); - } - } - - return false; -} -// ----------------------------------------------------------------- - -bool NodeFlagCondition::execute(VItem* item) -{ - if(item->isServer()) - { - return false; - } - else if(item->isNode()) - { - VNode* vnode=static_cast(item); - - if(nodeFlagName_ == "is_zombie") - return vnode->isFlagSet(ecf::Flag::ZOMBIE); - - if(nodeFlagName_ == "has_message") - return vnode->isFlagSet(ecf::Flag::MESSAGE); - - else if(nodeFlagName_ == "is_late") - return vnode->isFlagSet(ecf::Flag::LATE); - - else if(nodeFlagName_ == "is_rerun") - { - node_ptr node=vnode->node(); - if(!node.get()) return false; - - if(Submittable* s = node->isSubmittable()) - { - return (s->try_no() > 1); - } - return false; - } - else if(nodeFlagName_ == "is_waiting") - return vnode->isFlagSet(ecf::Flag::WAIT); - - else if(nodeFlagName_ == "is_migrated") - return vnode->isFlagSet(ecf::Flag::MIGRATED); - - else if(nodeFlagName_ == "is_ecfcmd_failed") - return vnode->isFlagSet(ecf::Flag::JOBCMD_FAILED); - - else if(nodeFlagName_ == "is_killed") - return vnode->isFlagSet(ecf::Flag::KILLED); - - - } - - return false; -} - -WhatToSearchInOperand::WhatToSearchInOperand(std::string what, bool &attr) -{ - what_ = what; - searchInAttributes_ = attr; -} - - -WhatToSearchInOperand::~WhatToSearchInOperand() {} -WhatToSearchForOperand::~WhatToSearchForOperand() {} - -//==================================================== -// -// Attribute condition -// -//==================================================== - -bool AttributeCondition::execute(VItem* item) -{ - if(!item) - return false; - - VAttribute* a=item->isAttribute(); - if(!a) - return false; - - Q_ASSERT(a->type()); - - return a->type() == type_; -} - -//==================================================== -// -// Attribute state condition -// -//==================================================== - -bool AttributeStateCondition::execute(VItem* item) -{ - if(!item) - return false; - - VAttribute* a=item->isAttribute(); - if(!a) - return false; - - assert(a->type()); - - if(attrState_.startsWith("event_")) - { - if(a->type()->name() == "event" && a->data().count() >= 3) - { - QString v=a->data()[2]; - if(attrState_ == "event_set") - return v == "1"; - else if(attrState_ == "event_clear") - return v == "0"; - } - } - else if(attrState_.startsWith("repeat_")) - { - if(a->type()->name() == "repeat" && a->data().count() >= 2) - { - QString v=a->data()[1]; - if(attrState_ == "repeat_date") - return v == "date"; - else if(attrState_ == "repeat_int") - return v == "integer"; - else if(attrState_ == "repeat_string") - return v == "string"; - else if(attrState_ == "repeat_enum") - return v == "enumeration"; - else if(attrState_ == "repeat_day") - return v == "day"; - } - } - return false; -} - -//========================================== -// -// ISO date condition -// -//========================================== - -IsoDateCondition::IsoDateCondition(QString dateStr) : secsSinceEpoch_(-1) -{ - QDateTime d=QDateTime::fromString(dateStr,Qt::ISODate); - if(d.isValid()) - secsSinceEpoch_=d.toMSecsSinceEpoch()/1000; -} - -std::string IsoDateCondition::print() -{ - if(secsSinceEpoch_ > 0) - return QDateTime::fromMSecsSinceEpoch(secsSinceEpoch_*1000).toString(Qt::ISODate).toStdString(); - return std::string(); -} - -//========================================== -// -// Node status change date condition -// -//========================================== - -qint64 NodeStatusChangeDateCondition::secsSinceEpoch(VItem* item) const -{ - Q_ASSERT(item); - if(item->isNode()) - { - VNode* vnode=static_cast(item); - Q_ASSERT(vnode); - return vnode->statusChangeTime(); - } - - return -1; -} - -std::string NodeStatusChangeDateCondition::print() -{ - return "status_change_time"; -} - -//========================================== -// -// ISO date greater than equal condition -// -//========================================== - -bool IsoDateGreaterThanEqualCondition::execute(VItem *node) -{ - UI_ASSERT(operands_.size() == 2,"operands size=" < (operands_[1]); - IsoDateCondition* rightOperand=static_cast (operands_[0]); - Q_ASSERT(leftOperand); - Q_ASSERT(rightOperand); - - return leftOperand->secsSinceEpoch(node) >= rightOperand->secsSinceEpoch(node); -} - -std::string IsoDateGreaterThanEqualCondition::print() -{ - UI_ASSERT(operands_.size() == 2,"operands size=" <print() + " >= " + operands_[0]->print(); -} - -//========================================== -// -// ISO date less than equal condition -// -//========================================== - -bool IsoDateLessThanEqualCondition::execute(VItem *node) -{ - UI_ASSERT(operands_.size() == 2,"operands size=" < (operands_[1]); - IsoDateCondition* rightOperand=static_cast (operands_[0]); - Q_ASSERT(leftOperand); - Q_ASSERT(rightOperand); - - return leftOperand->secsSinceEpoch(node) <= rightOperand->secsSinceEpoch(node); -} - -std::string IsoDateLessThanEqualCondition::print() -{ - UI_ASSERT(operands_.size() == 2,"operands size=" <print() + " <= " + operands_[0]->print(); -} diff -Nru ecflow-4.9.0/Viewer/src/NodeExpression.hpp ecflow-4.11.1/Viewer/src/NodeExpression.hpp --- ecflow-4.9.0/Viewer/src/NodeExpression.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeExpression.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,512 +0,0 @@ -#ifndef NODEEXPRESSION_HPP_ -#define NODEEXPRESSION_HPP_ - -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "DState.hpp" - -#include "VInfo.hpp" -#include "VItem.hpp" -#include "VNState.hpp" -#include "VSState.hpp" -#include "StringMatchMode.hpp" -#include "VAttribute.hpp" - -class VItem; -class VAttributeType; - -// ---------------------- -// Node condition classes -// ---------------------- - -class BaseNodeCondition; // forward declaration - -class NodeExpressionParser -{ -public: - enum NodeType {SERVER, SUITE, FAMILY, TASK, ALIAS, NODE, BAD}; - - enum AttributeType {ATTRIBUTE, METER, EVENT, REPEAT, TRIGGER, LABEL, TIME, DATE, - LATE, LIMIT, LIMITER, VAR, GENVAR, BADATTRIBUTE}; - - static NodeExpressionParser* instance(); - - BaseNodeCondition *parseWholeExpression(const std::string&, bool caseSensitiveStringMatch=true); - - NodeType nodeType(const std::string &name) const; - const std::string& typeName(const NodeType&) const; - VAttributeType* toAttrType(const std::string &name) const; - //AttributeType toAttrType(const std::string &name) const; - //const std::string& toAttrName(const AttributeType&) const; - -protected: - NodeExpressionParser(); - - bool isMenuMode(const std::string &str) const; - bool isNodeHasAttribute(const std::string &str) const; - bool isNodeFlag(const std::string &str) const; - bool isWhatToSearchIn(const std::string &str, bool &isAttribute) const; -#if 0 - bool isAttribute(const std::string &str) const; -#endif - bool isAttributeState(const std::string &str) const; - bool isIsoDate(const std::string &str) const; - - BaseNodeCondition *parseExpression(bool caseSensitiveStringMatch); - void setTokens(std::vector &tokens) {tokens_ = tokens; i_ = tokens_.begin();} - std::vector popLastNOperands(std::vector &inOperands, int n); - - static NodeExpressionParser* instance_; - std::vector tokens_; - std::vector::const_iterator i_; - std::map nameToNodeType_; - std::map nodeTypeToName_; - - std::map nameToAttrType_; - - //std::map nameToAttrType_; - //std::map attrTypeToName_; - std::string badTypeStr_; - std::string badAttributeStr_; -}; - -// ----------------------------------------------------------------- -// BaseNodeCondition -// The parent class for all node conditions. -// delayUnwinding: choose whether to unwind the function stack -// immediately after parsing this condition, or delay until we've -// reached the end of the current sub-expression. Set to true for -// loosely-coupled operators such as 'and', and set to false for -// others which need to consume their arguments immediately. -// ----------------------------------------------------------------- - -class BaseNodeCondition -{ -public: - BaseNodeCondition() {delayUnwinding_ = false;} - virtual ~BaseNodeCondition() {} - - bool execute(VInfo_ptr nodeInfo); - virtual bool execute(VItem* item)=0; - - virtual int numOperands() {return 0;} - virtual std::string print() = 0; - virtual bool operand2IsArbitraryString() {return false;} - - void setOperands(std::vector ops) {operands_ = ops;} - bool containsAttributeSearch(); - bool delayUnwinding() const {return delayUnwinding_;} - -protected: - virtual bool searchInAttributes() {return false;} - - std::vector operands_; - bool delayUnwinding_; -}; - -// ----------------------------------------------------------------- - -class AndNodeCondition : public BaseNodeCondition -{ -public: - AndNodeCondition() {delayUnwinding_ = true;} - ~AndNodeCondition() {} - - bool execute(VItem* node); - int numOperands() {return 2;} - std::string print() {return std::string("and") + "(" + operands_[0]->print() + "," + operands_[1]->print() + ")";} -}; - -// ----------------------------------------------------------------- - -class OrNodeCondition : public BaseNodeCondition -{ -public: - OrNodeCondition() {delayUnwinding_ = true;} - ~OrNodeCondition() {} - - bool execute(VItem* node); - int numOperands() {return 2;} - std::string print() {return std::string("or") + "(" + operands_[0]->print() + "," + operands_[1]->print() + ")";} -}; - -// ----------------------------------------------------------------- - -class NotNodeCondition : public BaseNodeCondition -{ -public: - NotNodeCondition() {} - ~NotNodeCondition() {} - - bool execute(VItem* node); - int numOperands() {return 1;} - std::string print() {return std::string("not") + "(" + operands_[0]->print() + ")";} -}; - - - -// -------------------------------- -// String matching utitlity classes -// -------------------------------- - -// note that it would be ideal for the match() function to take references to strings -// for efficiency, but this is not always possible. - -class StringMatchBase -{ -public: - StringMatchBase(bool caseSensitive) {caseSensitive_ = caseSensitive;} - virtual ~StringMatchBase() {} - - virtual bool match(std::string searchFor, std::string searchIn) = 0; - -protected: - bool caseSensitive_; -}; - -class StringMatchExact : public StringMatchBase -{ -public: - StringMatchExact(bool caseSensitive) : StringMatchBase(caseSensitive) {} - ~StringMatchExact() {} - - bool match(std::string searchFor, std::string searchIn); -}; - -class StringMatchContains : public StringMatchBase -{ -public: - StringMatchContains(bool caseSensitive) : StringMatchBase(caseSensitive) {} - ~StringMatchContains() {} - - bool match(std::string searchFor, std::string searchIn); -}; - -class StringMatchWildcard : public StringMatchBase -{ -public: - StringMatchWildcard(bool caseSensitive) : StringMatchBase(caseSensitive) {} - ~StringMatchWildcard() {} - - bool match(std::string searchFor, std::string searchIn); -}; - -class StringMatchRegexp : public StringMatchBase -{ -public: - StringMatchRegexp(bool caseSensitive) : StringMatchBase(caseSensitive) {} - ~StringMatchRegexp() {} - - bool match(std::string searchFor, std::string searchIn); -}; - -// ----------------------------------------------------------------- - -// ------------------------- -// String matching condition -// ------------------------- - -class StringMatchCondition : public BaseNodeCondition -{ -public: - StringMatchCondition(StringMatchMode::Mode matchMode, bool caseSensitive); - ~StringMatchCondition() {if (matcher_) delete matcher_;} - - bool execute(VItem *node); - int numOperands() {return 2;} - std::string print() {return operands_[0]->print() + " = " + operands_[1]->print();} - bool operand2IsArbitraryString() {return true;} -private: - StringMatchBase *matcher_; -}; - -// ----------------------------------------------------------------- - -// --------------------------- -// Basic true/false conditions -// --------------------------- - -class TrueNodeCondition : public BaseNodeCondition -{ -public: - TrueNodeCondition() {} - ~TrueNodeCondition() {} - - bool execute(VItem*) {return true;} - std::string print() {return std::string("true");} -}; - -class FalseNodeCondition : public BaseNodeCondition -{ -public: - FalseNodeCondition() {} - ~FalseNodeCondition() {} - - bool execute(VItem*) {return false;} - std::string print() {return std::string("false");} -}; - -// ----------------------------------------------------------------- - -// ------------------- -// Node type condition -// ------------------- - -class TypeNodeCondition : public BaseNodeCondition -{ -public: - explicit TypeNodeCondition(NodeExpressionParser::NodeType type) {type_ = type;} - ~TypeNodeCondition() {} - - bool execute(VItem* node); - std::string print() {return NodeExpressionParser::instance()->typeName(type_);} - -private: - NodeExpressionParser::NodeType type_; -}; - -// ----------------------------------------------------------------- - -// -------------------- -// Node state condition -// -------------------- - -class StateNodeCondition : public BaseNodeCondition -{ -public: - explicit StateNodeCondition(QString stateName) : stateName_(stateName) {} - ~StateNodeCondition() {} - - bool execute(VItem* node); - std::string print() {return stateName_.toStdString();} - -private: - QString stateName_; -}; - -// ----------------------------------------------------------------- - -// -------------------- -// User level condition -// -------------------- - -class NodeMenuModeCondition : public BaseNodeCondition -{ -public: - explicit NodeMenuModeCondition(QString menuModeName) : menuModeName_(menuModeName) {} - ~NodeMenuModeCondition() {} - - bool execute(VItem*); - std::string print() {return menuModeName_.toStdString();} - -private: - QString menuModeName_; -}; - -// ----------------------------------------------------------------- - -// -------------------- -// UI state condition -// -------------------- - -class UIStateCondition : public BaseNodeCondition -{ -public: - explicit UIStateCondition(const std::string& uiStateName) : uiStateName_(uiStateName) {} - ~UIStateCondition() {} - - bool execute(VItem*); - std::string print() {return uiStateName_;} - -private: - std::string uiStateName_; -}; - - -// ----------------------------------------------------------------- - -// ------------------------ -// Node attribute condition -// ------------------------ - -class NodeAttributeCondition : public BaseNodeCondition -{ -public: - explicit NodeAttributeCondition(QString nodeAttrName) : nodeAttrName_(nodeAttrName) {} - ~NodeAttributeCondition() {} - - bool execute(VItem*); - std::string print() {return nodeAttrName_.toStdString();} - -private: - QString nodeAttrName_; -}; - -// ----------------------------------------------------------------- - -// ------------------------ -// Node flag condition -// ------------------------ - -class NodeFlagCondition : public BaseNodeCondition -{ -public: - explicit NodeFlagCondition(QString nodeFlagName) : nodeFlagName_(nodeFlagName) {} - ~NodeFlagCondition() {} - - bool execute(VItem*); - std::string print() {return nodeFlagName_.toStdString();} - -private: - QString nodeFlagName_; -}; -// ----------------------------------------------------------------- - -// ---------------------------------- -// ISO date condition -// ---------------------------------- - -class IsoDateCondition : public BaseNodeCondition -{ -public: - explicit IsoDateCondition(QString str=QString()); - ~IsoDateCondition() {} - - bool execute(VItem*) {return false;} - std::string print(); - virtual qint64 secsSinceEpoch(VItem*) const {return secsSinceEpoch_;} - -private: - qint64 secsSinceEpoch_; - -}; - -// ---------------------------------- -// Node status change date condition -// ---------------------------------- - -class NodeStatusChangeDateCondition : public IsoDateCondition -{ -public: - explicit NodeStatusChangeDateCondition() {} - ~NodeStatusChangeDateCondition() {} - - bool execute(VItem*) {return false;} - std::string print(); - qint64 secsSinceEpoch(VItem*) const; -}; - -// -------------------------------- -// ISO date comparison conditions -// -------------------------------- - -class IsoDateGreaterThanEqualCondition : public BaseNodeCondition -{ -public: - IsoDateGreaterThanEqualCondition() {} - ~IsoDateGreaterThanEqualCondition() {} - - bool execute(VItem *node); - int numOperands() {return 2;} - std::string print(); -}; - -class IsoDateLessThanEqualCondition : public BaseNodeCondition -{ -public: - IsoDateLessThanEqualCondition() {} - ~IsoDateLessThanEqualCondition() {} - - bool execute(VItem *node); - int numOperands() {return 2;} - std::string print(); -}; - -// ----------------------------------------------------------------- - -// ----------------- -// Search conditions -// ----------------- - -class WhatToSearchInOperand : public BaseNodeCondition -{ -public: - explicit WhatToSearchInOperand(std::string what, bool &attr); - ~WhatToSearchInOperand(); - - const std::string& name() const {return what_;} - bool execute(VItem* node) {return false;} // not called - std::string print() {return what_;} - const std::string& what() const {return what_;} - -private: - std::string what_; // TODO XXX: optimise - we should store an enum here - bool searchInAttributes_; - - bool searchInAttributes() {return searchInAttributes_;} -}; - -// ----------------------------------------------------------------- - -class WhatToSearchForOperand : public BaseNodeCondition -{ -public: - explicit WhatToSearchForOperand(const std::string& what) : what_(what) {} - ~WhatToSearchForOperand(); - - std::string name() {return what_;} - bool execute(VItem* node) {return false;} // not called - std::string print() {return what_;} - std::string what() {return what_;} - -private: - std::string what_; -}; - -// ------------------------ -// Attribute condition -// ------------------------ - -class AttributeCondition : public BaseNodeCondition -{ -public: - //explicit AttributeCondition(NodeExpressionParser::AttributeType type) {type_ = type;} - explicit AttributeCondition(VAttributeType* type) : type_(type) {} - ~AttributeCondition() {} - - bool execute(VItem*); - //std::string print() {return NodeExpressionParser::instance()->toAttrName(type_);} - std::string print() {return ""; /*type_->strName();*/} - -private: - //NodeExpressionParser::AttributeType type_; - VAttributeType *type_; -}; - -//--------------------------------- -// Node attribute state condition -// ---------------------------- - -class AttributeStateCondition : public BaseNodeCondition -{ -public: - explicit AttributeStateCondition(QString attrState) : attrState_(attrState) {} - ~AttributeStateCondition() {} - - bool execute(VItem*); - std::string print() {return attrState_.toStdString();} - -private: - QString attrState_; -}; - - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeFilterDialog.cpp ecflow-4.11.1/Viewer/src/NodeFilterDialog.cpp --- ecflow-4.9.0/Viewer/src/NodeFilterDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeFilterDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include - -#include "NodeFilterDialog.hpp" -#include "SessionHandler.hpp" -#include "VConfig.hpp" - -NodeFilterDialog::NodeFilterDialog(QWidget *parent) : - QDialog(parent) -{ - setupUi(this); - - //setAttribute(Qt::WA_DeleteOnClose); - editor_->setFilterMode(true); - - QString wt=windowTitle(); - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - connect(applyPb_,SIGNAL(clicked()), - this,SLOT(accept())); - - connect(cancelPb_,SIGNAL(clicked()), - this,SLOT(reject())); - - leftVb_->addStretch(1); - - //Read the qt settings - readSettings(); -} - -NodeFilterDialog::~NodeFilterDialog() -{ -} - -void NodeFilterDialog::setQuery(NodeQuery* q) -{ - editor_->setQuery(q); -} - -NodeQuery* NodeFilterDialog::query() const -{ - return editor_->query(); -} - -void NodeFilterDialog::setServerFilter(ServerFilter* sf) -{ - editor_->setServerFilter(sf); -} - -void NodeFilterDialog::closeEvent(QCloseEvent * event) -{ - event->accept(); - writeSettings(); -} - -void NodeFilterDialog::accept() -{ - writeSettings(); - QDialog::accept(); -} - - -void NodeFilterDialog::reject() -{ - writeSettings(); - QDialog::reject(); -} - -//------------------------------------------ -// Settings read/write -//------------------------------------------ - -void NodeFilterDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeFilterDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void NodeFilterDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeFilterDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(590,460)); - } - - settings.endGroup(); -} - diff -Nru ecflow-4.9.0/Viewer/src/NodeFilterDialog.hpp ecflow-4.11.1/Viewer/src/NodeFilterDialog.hpp --- ecflow-4.9.0/Viewer/src/NodeFilterDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeFilterDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEFILTERDIALOG_HPP_ -#define VIEWER_SRC_NODEFILTERDIALOG_HPP_ - -#include - -#include "ui_NodeFilterDialog.h" - -class ServerFilter; - -class NodeFilterDialog : public QDialog, protected Ui::NodeFilterDialog -{ - Q_OBJECT - -public: - explicit NodeFilterDialog(QWidget *parent = 0); - ~NodeFilterDialog(); - - void setQuery(NodeQuery*); - NodeQuery* query() const; - void setServerFilter(ServerFilter*); - -protected Q_SLOTS: - void accept(); - void reject(); - -protected: - void closeEvent(QCloseEvent * event); - -private: - void readSettings(); - void writeSettings(); -}; - - - -#endif /* VIEWER_SRC_NODEFILTERDIALOG_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeFilterDialog.ui ecflow-4.11.1/Viewer/src/NodeFilterDialog.ui --- ecflow-4.9.0/Viewer/src/NodeFilterDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeFilterDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ - - - NodeFilterDialog - - - - 0 - 0 - 738 - 696 - - - - Table filter - - - - - - - - - - - - - - - Apply - - - - :/viewer/submit.svg:/viewer/submit.svg - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Cancel - - - - - - - - - - NodeQueryEditor - QWidget -
        NodeQueryEditor.hpp
        - 1 -
        -
        - - -
        diff -Nru ecflow-4.9.0/Viewer/src/NodeObserver.hpp ecflow-4.11.1/Viewer/src/NodeObserver.hpp --- ecflow-4.9.0/Viewer/src/NodeObserver.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeObserver.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef NODEOBSERVER_HPP_ -#define NODEOBSERVER_HPP_ - -#include "Aspect.hpp" -#include "Node.hpp" - -class VNode; -class VNodeChange; - -class NodeObserver -{ -public: - NodeObserver() {} - virtual ~NodeObserver() {} - - virtual void notifyBeginNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&)=0; - virtual void notifyEndNodeChange(const VNode* vn, const std::vector& a,const VNodeChange&)=0; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodePanel.cpp ecflow-4.11.1/Viewer/src/NodePanel.cpp --- ecflow-4.9.0/Viewer/src/NodePanel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodePanel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,444 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "NodePanel.hpp" - -#include "Dashboard.hpp" -#include "DashboardTitle.hpp" -#include "InfoPanel.hpp" -#include "ServerHandler.hpp" -#include "VSettings.hpp" - -#include -#include - -NodePanel::NodePanel(QWidget* parent) : - TabWidget(parent) - -{ - setObjectName("p"); - - connect(this,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotCurrentWidgetChanged(int))); - - connect(this,SIGNAL(newTabRequested()), - this,SLOT(slotNewTab())); - - connect(this,SIGNAL(tabRemoved()), - this,SLOT(slotTabRemoved())); - -} - -NodePanel::~NodePanel() -{ -} - -//============================================= -// -// Tab management desktopAction -// -//============================================= - -Dashboard *NodePanel::addWidget(QString id) -{ - Dashboard *nw=new Dashboard("",this); - - QString name(""); - QPixmap pix; - - addTab(nw,pix,name); - - connect(nw,SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChangedInWidget(VInfo_ptr))); - - connect(nw->titleHandler(),SIGNAL(changed(DashboardTitle*)), - this,SLOT(slotTabTitle(DashboardTitle*))); - - connect(nw,SIGNAL(contentsChanged()), - this,SIGNAL(contentsChanged())); - - adjustTabTitle(); - - //init the title - slotTabTitle(nw->titleHandler()); - - return nw; -} - - -void NodePanel::resetWidgets(QStringList idLst) -{ - if(idLst.count() ==0) - return; - - clear(); - - Q_FOREACH(QString id,idLst) - { - addWidget(id); - } -} - -//Handle the tab bar context menu actions -void NodePanel::tabBarCommand(QString name,int index) -{ - if(name == "reloadTab") - { - Dashboard *w=nodeWidget(index); - if(w) w->reload(); - } - else if(name == "closeOtherTabs") - { - removeOtherTabs(index); - } - else if(name == "closeTab") - { - removeTab(index); - } -} - -Dashboard *NodePanel::nodeWidget(int index) -{ - QWidget *w=widget(index); - return (w)?static_cast(w):0; -} - -Dashboard *NodePanel::currentDashboard() -{ - QWidget *w=currentWidget(); - return static_cast(w); -} - -void NodePanel::slotCurrentWidgetChanged(int /*index*/) -{ - for(int i=0; i < count(); i++) - { - if(QWidget *w=widget(i)) - { - if(Dashboard* nw=static_cast(w)) - nw->titleHandler()->setCurrent(i==currentIndex()); - } - } - - Q_EMIT currentWidgetChanged(); - //setDefaults(this); -} - -void NodePanel::slotNewTab() -{ - Dashboard *w=addWidget(""); - w->addWidget("tree"); -} - -VInfo_ptr NodePanel::currentSelection() -{ - if(Dashboard *w=currentDashboard()) - return w->currentSelection(); - - return VInfo_ptr(); -} - -void NodePanel::slotSelection(VInfo_ptr n) -{ - if(Dashboard *w=currentDashboard()) - w->currentSelection(n); -} - -void NodePanel::slotSelectionChangedInWidget(VInfo_ptr n) -{ - if(Dashboard *w=static_cast(sender())) - { - if(w == currentDashboard()) - Q_EMIT selectionChangedInCurrent(n); - - } -} - -bool NodePanel::selectInTreeView(VInfo_ptr info) -{ - for(int i=0; i < count(); i++) - { - if(QWidget *w=widget(i)) - { - if(Dashboard* nw=static_cast(w)) - if(nw->selectInTreeView(info)) - { - setCurrentIndex(i); - return true; - } - } - } - return false; -} - -void NodePanel::setViewMode(Viewer::ViewMode mode) -{ - Dashboard *w=currentDashboard(); - if(w) w->setViewMode(mode); - //setDefaults(this); -} - -Viewer::ViewMode NodePanel::viewMode() -{ - Dashboard *w=currentDashboard(); - return (w)?w->viewMode():Viewer::NoViewMode; -} - -ServerFilter* NodePanel::serverFilter() -{ - Dashboard *w=currentDashboard(); - return (w)?w->serverFilter():NULL; -} - -void NodePanel::addToDashboard(const std::string& type) -{ - if(Dashboard *w=currentDashboard()) - { - w->addWidget(type); - } -} - -void NodePanel::openDialog(VInfo_ptr info,const std::string& type) -{ - if(Dashboard *w=currentDashboard()) - { - w->slotPopInfoPanel(info,QString::fromStdString(type)); - } -} -void NodePanel::addSearchDialog() -{ - if(Dashboard *w=currentDashboard()) - { - w->addSearchDialog(); - } -} - - -void NodePanel::slotTabTitle(DashboardTitle* t) -{ - int index=indexOfWidget(t->dashboard()); - if(index != -1) - { - setTabText(index,t->title()); - setTabIcon(index,t->pix()); - setTabToolTip(index,t->tooltip()); - setTabWht(index,t->desc()); - setTabData(index,t->descPix()); - } -} - -void NodePanel::slotTabRemoved() -{ - adjustTabTitle(); -} - -int NodePanel::tabAreaWidth() const -{ - return width()-80; -} - -void NodePanel::adjustTabTitle() -{ - if(count() > 1) - { - int tabWidth=tabAreaWidth()/count(); - if(tabWidth < 30) - tabWidth=30; - - for(int i=0; i < count(); i++) - { - if(QWidget *w=widget(i)) - { - if(Dashboard* nw=static_cast(w)) - nw->titleHandler()->setMaxPixWidth(tabWidth); - } - } - } -} - -void NodePanel::resizeEvent(QResizeEvent *e) -{ - if(abs(e->oldSize().width()-width()) > 5) - adjustTabTitle(); -} - -/*void NodePanel::slotNewWindow(bool) -{ - //MainWindow::openWindow("",this); - - if(Folder* f=currentFolder()) - { - QString p=QString::fromStdString(f->fullName()); - MvQFileBrowser::openBrowser(p,this); - } -}*/ - -/*void NodePanel::setViewMode(Viewer::NodeViewMode mode) -{ - NodeWidget *w=currentNodeWidget(); - if(w) w->setViewMode(mode); - //setDefaults(this); -} - -Viewer::FolderViewMode NodePanel::viewMode() -{ - NodeWidget *w=currentNodeWidget(); - return (w)?w->viewMode():MvQ::NoViewMode; -}*/ - -//========================================================== -// -// -// -//========================================================== - -void NodePanel::reload() -{ - for(int i=0; i < count(); i++) - { - if(QWidget *w=widget(i)) - { - if(Dashboard* nw=static_cast(w)) - nw->reload(); - } - } -} - -void NodePanel::rerender() -{ - for(int i=0; i < count(); i++) - { - if(QWidget *w=widget(i)) - { - if(Dashboard* nw=static_cast(w)) - nw->rerender(); - } - } -} - - -void NodePanel::refreshCurrent() -{ - ServerFilter* filter=serverFilter(); - if(!filter) - return; - - for(std::vector::const_iterator it=filter->items().begin(); it != filter->items().end(); ++it) - { - if(ServerHandler *sh=(*it)->serverHandler()) - { - sh->refresh(); - } - } -} - - -void NodePanel::resetCurrent() -{ - ServerFilter* filter=serverFilter(); - if(!filter) - return; - - for(std::vector::const_iterator it=filter->items().begin(); it != filter->items().end(); ++it) - { - if(ServerHandler *sh=(*it)->serverHandler()) - { - sh->reset(); - } - } -} - -void NodePanel::init() -{ - Dashboard* nw=addWidget(""); - if(nw) - { - nw->addWidget("tree"); - } -} - -//========================================================== -// -// Save/restore settings -// -//========================================================== - -void NodePanel::writeSettings(VComboSettings *vs) -{ - int currentIdx=(currentIndex()>=0)?currentIndex():0; - - vs->put("tabCount",count()); - vs->put("currentTabId",currentIdx); - - for(int i=0; i < count(); i++) - { - //boost::property_tree::ptree ptTab; - if(Dashboard* nw=nodeWidget(i)) - { - std::string id=NodePanel::tabSettingsId(i); - vs->beginGroup(id); - nw->writeSettings(vs); - vs->endGroup(); - //pt.add_child("tab_"+ boost::lexical_cast(i),ptTab); - } - } -} - -void NodePanel::readSettings(VComboSettings *vs) -{ - using boost::property_tree::ptree; - - int cnt=vs->get("tabCount",0); - int currentIndex=vs->get("currentTabId",-1); - - for(int i=0; i < cnt; i++) - { - std::string id=NodePanel::tabSettingsId(i); - if(vs->contains(id)) - { - Dashboard* nw=addWidget(""); - if(nw) - { - vs->beginGroup(id); - nw->readSettings(vs); - vs->endGroup(); - } - } - } - - //Set current tab - if(currentIndex >=0 && currentIndex < count()) - { - setCurrentIndex(currentIndex); - } - - //If no tabs have been created - if(count()==0) - { - addWidget(""); - setCurrentIndex(0); - if(Dashboard* d=currentDashboard()) - { - d->addWidget("tree"); - } - } - - if(QWidget *w=currentDashboard()) - w->setFocus(); - - //We emit it to trigger the whole window ui update! - Q_EMIT currentWidgetChanged(); -} - -std::string NodePanel::tabSettingsId(int i) -{ - return "tab_" + boost::lexical_cast(i); -} - - diff -Nru ecflow-4.9.0/Viewer/src/NodePanel.hpp ecflow-4.11.1/Viewer/src/NodePanel.hpp --- ecflow-4.9.0/Viewer/src/NodePanel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodePanel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef NODEPANEL_HPP_ -#define NODEPANEL_HPP_ - -#include "Viewer.hpp" -#include "TabWidget.hpp" -#include "VInfo.hpp" - -#include - -#include - -class Dashboard; -class DashboardTitle; -class ServerFilter; -class VComboSettings; - -class NodePanel : public TabWidget -{ - Q_OBJECT - -public: - explicit NodePanel(QWidget* parent=0); - virtual ~NodePanel(); - - void setViewMode(Viewer::ViewMode); - Viewer::ViewMode viewMode(); - - ServerFilter* serverFilter(); - - Dashboard* currentDashboard(); - void addWidget(); - void resetWidgets(QStringList); - void reload(); - void rerender(); - void refreshCurrent(); - void resetCurrent(); - VInfo_ptr currentSelection(); - bool selectInTreeView(VInfo_ptr); - void addToDashboard(const std::string& type); - void init(); - void openDialog(VInfo_ptr,const std::string& type); - void addSearchDialog(); - - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - -public Q_SLOTS: - void slotCurrentWidgetChanged(int); - void slotSelection(VInfo_ptr); - void slotNewTab(); - void slotSelectionChangedInWidget(VInfo_ptr); - -protected Q_SLOTS: - void slotTabRemoved(); - void slotTabTitle(DashboardTitle* w); - -Q_SIGNALS: - void itemInfoChanged(QString); - void currentWidgetChanged(); - void selectionChangedInCurrent(VInfo_ptr); - void contentsChanged(); - -protected: - void resizeEvent(QResizeEvent *e); - void adjustTabTitle(); - int tabAreaWidth() const; - - Dashboard* addWidget(QString); - void tabBarCommand(QString, int); - Dashboard* nodeWidget(int index); - static std::string tabSettingsId(int i); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodePathWidget.cpp ecflow-4.11.1/Viewer/src/NodePathWidget.cpp --- ecflow-4.9.0/Viewer/src/NodePathWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodePathWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1426 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#include "NodePathWidget.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "VNode.hpp" -#include "VProperty.hpp" -#include "Palette.hpp" -#include "PropertyMapper.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "VNState.hpp" -#include "VSState.hpp" -#include "VSettings.hpp" - -static std::vector propVec; - -QColor NodePathItem::disabledBgCol_; -QColor NodePathItem::disabledBorderCol_; -QColor NodePathItem::disabledFontCol_; -int NodePathItem::triLen_=10; -int NodePathItem::height_=0; -int NodePathItem::hPadding_=2; -int NodePathItem::vPadding_=0; - -//#define _UI_NODEPATHWIDGET_DEBUG - -BcWidget::BcWidget(QWidget* parent) : - QWidget(parent), - hMargin_(1), - vMargin_(1), - gap_(5), - width_(0), - maxWidth_(0), - itemHeight_(0), - text_("No selection"), - textCol_(Qt::white), - textDisabledCol_(QColor(220,220,220)), - useGrad_(true), - gradLighter_(150), - hovered_(-1), - elided_(false) -{ - font_=QFont(); - font_.setPointSize(font_.pointSize()-1); - QFontMetrics fm(font_); - - itemHeight_=NodePathItem::height(font_); - height_=itemHeight_+2*vMargin_; - - setMouseTracking(true); - - //Property - if(propVec.empty()) - { - propVec.push_back("view.common.node_style"); - propVec.push_back("view.common.node_gradient"); - } - - prop_=new PropertyMapper(propVec,this); - - updateSettings(); - - setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); - setMinimumSize(width_,height_); - - ellipsisItem_ = new NodePathEllipsisItem(this); - ellipsisItem_->visible_=false; - - reset(items_,100); - - //setAutoFillBackground(true); - //QPalette pal=palette(); - //pal.setColor(QPalette::Window,Qt::transparent); - //setPalette(pal); -} - -BcWidget::~BcWidget() -{ - delete prop_; - delete ellipsisItem_; -} - -void BcWidget::notifyChange(VProperty *p) -{ - updateSettings(); -} - -void BcWidget::updateSettings() -{ - if(VProperty *p=prop_->find("view.common.node_gradient")) - useGrad_=p->value().toBool(); -} - -bool BcWidget::isFull() const -{ - return !elided_ && !ellipsisItem_->visible_; -} - -void BcWidget::clear() -{ - items_.clear(); - reset(items_,100); -} - -void BcWidget::resetBorder(int idx) -{ - if(idx >=0 && idx < items_.count()) - { - items_.at(idx)->resetBorder(idx == hovered_); - updatePixmap(idx); - update(); - } -} - -void BcWidget::reset(QString txt, int maxWidth) -{ - maxWidth_=maxWidth; - hovered_=-1; - ellipsisItem_->visible_=false; - elided_=false; - - QFontMetrics fm(font_); - int xp=hMargin_; - int yp=vMargin_; - - if(!txt.isEmpty()) - { - text_=txt; - } - else - { - text_="No selection"; - } - - int len=fm.width(text_); - textRect_=QRect(xp,yp,len,itemHeight_); - width_=xp+len+4; - - crePixmap(); - resize(width_,height_); - update(); -} - -void BcWidget::reset(int idx,QString text,QColor bgCol,QColor fontCol) -{ - if(idx >=0 && idx < items_.count()) - { - bool newText=(text != items_.at(idx)->text_); - items_[idx]->reset(text,bgCol,fontCol,idx == hovered_); - - if(newText) - reset(items_,maxWidth_); - else - { - updatePixmap(idx); - update(); - } - } -} - -void BcWidget::reset(QList items, int maxWidth) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "BcWidget::reset -->"; - UiLog().dbg() << " maxWidth=" << maxWidth; -#endif - - maxWidth_=maxWidth; - items_=items; - hovered_=-1; - ellipsisItem_->visible_=false; - elided_=false; - - QFontMetrics fm(font_); - int xp=hMargin_; - int yp=vMargin_; - - if(items_.count() ==0) - { - int len=fm.width(text_); - textRect_=QRect(xp,yp,len,itemHeight_); - width_=xp+len+4; - } - else - { - // - // xp is the top right corner of the shape (so it is not the rightmost edge) - // - // server shape: - // - // ******** - // * * - // ******** - // - // other shape: - // - // ******** - // * * - // ******** - // - - NodePathItem *lastItem=items_[items_.count()-1]; - Q_ASSERT(lastItem); - int maxRedTextLen=0; - - //Defines the shapes and positions for all the items - for(int i=0; i < items_.count(); i++) - { - xp=items_[i]->adjust(xp,yp); - - if(i != items_.count()-1) - { - xp+=gap_; - int tl=items_[i]->textLen(); - if(tl > maxRedTextLen) - maxRedTextLen=tl; - } - } - - //The total width - width_=xp+NodePathItem::triLen_+hMargin_; - -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " full width=" << width_; -#endif - - //maxWidth-=2*hMargin_; - - //If the total width is too big we try to use elidedtext in the items - //(with the execption of the last item) - int redTextLen=0; - if(width_ > maxWidth) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " try elided text"; -#endif - //Try different elided text lenghts - for(int i=20; i >= 3; i--) - { - QString t; - for(int j=0; j < i; j++) - { - t+="A"; - } - t+="..."; - - //We only check the elided texts that are shorter then the max text len - redTextLen=fm.width(t); - if(redTextLen < maxRedTextLen) - { - //Estimate the total size with the elided text items - xp=hMargin_; - int estWidth=estimateWidth(0,xp,redTextLen); - - //if the size fits into maxWidth we adjust all the items - if(estWidth < maxWidth) - { - int xp=hMargin_; - width_ = adjustItems(0,xp,yp,redTextLen); - elided_=true; - - Q_ASSERT(width_== estWidth); - Q_ASSERT(width_ < maxWidth); - break; //This breaks the whole for loop - } - } - } - } - - //If the total width is still too big we start hiding items from the left - //and insert an ellipsis item to the front. - int xpAfterEllipsis=0; - if(width_ > maxWidth) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " insert ellipsis to front + remove items"; - UiLog().dbg() << " redTextLen=" << redTextLen; -#endif - Q_ASSERT(elided_==false); - - //width_=maxWidth; - - xp=hMargin_; - ellipsisItem_->visible_=true; - ellipsisItem_->adjust(xp,yp); - xp=ellipsisItem_->estimateRightPos(xp); - xpAfterEllipsis=xp+gap_; - bool fitOk=false; - int estWidth=0; - for(int i=0; i < items_.count()-1; i++) - { - xp=xpAfterEllipsis; - items_[i]->visible_=false; -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " omit item " << i; -#endif - estWidth=estimateWidth(i+1,xp,redTextLen); -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " estWidth " << estWidth; -#endif - if(estWidth < maxWidth) - { - fitOk=true; - break; - } - } - - if(fitOk) - { - xp=xpAfterEllipsis; - width_=adjustVisibleItems(0,xp,yp,redTextLen); - Q_ASSERT(width_ == estWidth); - Q_ASSERT(width_ < maxWidth); - } - else - { - xp=xpAfterEllipsis; - xp=lastItem->estimateRightPos(xp); - estWidth=xp+NodePathItem::triLen_+hMargin_; - if(estWidth < maxWidth) - { - xp=xpAfterEllipsis; - xp=lastItem->adjust(xp,yp); - width_=xp+NodePathItem::triLen_+hMargin_; - Q_ASSERT(width_ == estWidth); - Q_ASSERT(width_ < maxWidth); - } - - } - } - - //If the total width is still too big we try to use elidedtext in the last item - //(at this point all the other items are hidden) - if(width_ > maxWidth) - { - int len=lastItem->textLen(); - - //Try different elided text lenghts - for(int i=30; i >= 3; i--) - { - QString t; - for(int j=0; j < i; j++) - { - t+="A"; - } - t+="..."; - - //We only check the elided texts that are shorter then the max text len - redTextLen=fm.width(t); - if(redTextLen < len) - { - //Estimate the total size with the elided text item - xp=xpAfterEllipsis; - xp=lastItem->estimateRightPos(xp,redTextLen); - int estWidth=xp+NodePathItem::triLen_+hMargin_; - if(estWidth < maxWidth) - { - xp=xpAfterEllipsis; - xp=lastItem->adjust(xp,yp,redTextLen); - width_=xp+NodePathItem::triLen_+hMargin_; - Q_ASSERT(width_ == estWidth); - Q_ASSERT(width_ < maxWidth); - break; - } - } - } - } - - //If the total width is still too big we also hide the last item and - //only show the ellipsis item - if(width_ > maxWidth) - { - lastItem->visible_=false; - width_=maxWidth; - } - } - - crePixmap(); - - resize(width_,height_); - - update(); -} - -int BcWidget::estimateWidth(int startIndex,int xp,int redTextLen) -{ - for(int i=startIndex; i < items_.count(); i++) - { - if(i != items_.count()-1) - { - xp=items_[i]->estimateRightPos(xp,redTextLen); - xp+=gap_; - } - else - xp=items_[i]->estimateRightPos(xp); - } - - return xp+NodePathItem::triLen_+hMargin_; -} - -int BcWidget::adjustItems(int startIndex,int xp,int yp,int redTextLen) -{ - for(int i=startIndex; i < items_.count(); i++) - { - if(i != items_.count()-1) - { - xp=items_[i]->adjust(xp,yp,redTextLen); - xp+=gap_; - } - else - xp=items_[i]->adjust(xp,yp); - } - - return xp+NodePathItem::triLen_+hMargin_; -} - -int BcWidget::adjustVisibleItems(int startIndex,int xp,int yp,int redTextLen) -{ - for(int i=startIndex; i < items_.count(); i++) - { - if(items_[i]->visible_) - { - if(i != items_.count()-1) - { - xp=items_[i]->adjust(xp,yp,redTextLen); - xp+=gap_; - } - else - xp=items_[i]->adjust(xp,yp); - } - } - return xp+NodePathItem::triLen_+hMargin_; -} - -void BcWidget::adjustSize(int maxWidth) -{ - if(isFull()) - { - if(width_ > maxWidth) - reset(items_,maxWidth); - } - else - { - reset(items_,maxWidth); - } -} - -void BcWidget::crePixmap() -{ - pix_=QPixmap(width_,height_); - pix_.fill(Qt::transparent); - - QPainter painter(&pix_); - painter.setRenderHints(QPainter::Antialiasing,true); - - painter.setFont(font_); - - if(items_.count() == 0) - { - if(isEnabled()) - painter.setPen(textCol_); - else - painter.setPen(textDisabledCol_); - - painter.drawText(textRect_,Qt::AlignHCenter | Qt::AlignVCenter, text_); - } - else - { - for(int i=0; i < items_.count(); i++) - { - items_.at(i)->enabled_=isEnabled(); - items_.at(i)->draw(&painter,useGrad_,gradLighter_); - } - } - - if(ellipsisItem_->visible_) - { - ellipsisItem_->enabled_=isEnabled(); - ellipsisItem_->draw(&painter,false,gradLighter_); - } -} - -void BcWidget::updatePixmap(int idx) -{ - if(idx >=0 && idx < items_.count()) - { - QPainter painter(&pix_); - painter.setRenderHints(QPainter::Antialiasing,true); - painter.setFont(font_); - items_.at(idx)->draw(&painter,useGrad_,gradLighter_); - } -} - -void BcWidget::paintEvent(QPaintEvent*) -{ - QPainter painter(this); - painter.drawPixmap(0,0,pix_); -} - -void BcWidget::mouseMoveEvent(QMouseEvent *event) -{ - for(int i=0; i < items_.count(); i++) - { - if(items_.at(i)->shape_.containsPoint(event->pos(),Qt::OddEvenFill)) - { - if(hovered_ == -1) - { - hovered_=i; - resetBorder(i); - } - else if(hovered_ != i) - { - int prev=hovered_; - hovered_=i; - resetBorder(prev); - resetBorder(i); - } - - return; - } - } - - if(hovered_ != -1) - { - int prev=hovered_; - hovered_=-1; - resetBorder(prev); - } - - QWidget::mouseMoveEvent(event); -} - -void BcWidget::mousePressEvent(QMouseEvent *event) -{ - if(event->button() != Qt::RightButton && event->button() != Qt::LeftButton) - return; - - for(int i=0; i < items_.count(); i++) - { - if(items_[i]->visible_ && - items_[i]->shape_.containsPoint(event->pos(),Qt::OddEvenFill)) - { - if(event->button() == Qt::RightButton) - { - Q_EMIT menuSelected(i,event->pos()); - return; - } - else if(event->button() == Qt::LeftButton) - { - Q_EMIT itemSelected(i); - return; - } - } - } - - QWidget::mousePressEvent(event); -} - -void BcWidget::changeEvent(QEvent* event) -{ - if(event->type() == QEvent::EnabledChange) - { - crePixmap(); -//TODO: Will update be called automatically? - } - - QWidget::changeEvent(event); -} - -//===================================================== -// -// NodePathItem -// -//===================================================== - -NodePathItem::NodePathItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current) : - owner_(owner), - index_(index), - text_(text), - bgCol_(bgCol), - fontCol_(fontCol), - current_(current), - hasMenu_(hasMenu), - visible_(false), - enabled_(true) -{ - height(owner_->font()); - - if(!disabledBgCol_.isValid()) - { - disabledBgCol_=QColor(200,200,200); - disabledBorderCol_=QColor(170,170,170); - disabledFontCol_=QColor(40,40,40); - } - - grad_.setCoordinateMode(QGradient::ObjectBoundingMode); - grad_.setStart(0,0); - grad_.setFinalStop(0,1); -} - -int NodePathItem::height(QFont f) -{ - if(height_==0) - { - QFontMetrics fm(f); - height_=fm.height()+2*vPadding_; - } - return height_; -} - -void NodePathItem::setCurrent(bool) -{ -} - -int NodePathItem::textLen() const -{ - QFontMetrics fm(owner_->font()); - return fm.width(text_); -} - -void NodePathItem::makeShape(int xp,int yp,int len) -{ - QVector vec; - vec << QPoint(0,0); - vec << QPoint(len+triLen_,0); - vec << QPoint(len+2*triLen_,height_/2); - vec << QPoint(len+triLen_,height_); - vec << QPoint(0,height_); - vec << QPoint(triLen_,height_/2); - - shape_=QPolygon(vec).translated(xp,yp); - - textRect_=QRect(xp+triLen_+hPadding_,yp,len,height_); -} - -int NodePathItem::adjust(int xp,int yp,int elidedLen) -{ - visible_=true; - - QFontMetrics fm(owner_->font()); - int len; - if(elidedLen == 0) - { - elidedText_=QString(); - len=fm.width(text_); - } - else - { - elidedText_=fm.elidedText(text_,Qt::ElideRight,elidedLen); - len=fm.width(elidedText_); - } - - borderCol_=bgCol_.darker(125); - - makeShape(xp,yp,len); - - return rightPos(xp,len); -} - - -//It returns the x position of the top right corner! -int NodePathItem::rightPos(int xp,int len) const -{ - return xp+len+triLen_; -} - - -//It returns the x position of the top right corner! -int NodePathItem::estimateRightPos(int xp,int elidedLen) -{ - QFontMetrics fm(owner_->font()); - int len; - - if(elidedLen==0) - len=fm.width(text_); - else - len=fm.width(fm.elidedText(text_,Qt::ElideRight,elidedLen)); - - return rightPos(xp,len); -} - -void NodePathItem::resetBorder(bool hovered) -{ - if(!hovered) - borderCol_=bgCol_.darker(125); - else - borderCol_=bgCol_.darker(240); -} - -void NodePathItem::reset(QString text,QColor bgCol,QColor fontCol,bool hovered) -{ - text_=text; - bgCol_=bgCol; - fontCol_=fontCol; - - if(!hovered) - borderCol_=bgCol_.darker(125); - else - borderCol_=bgCol_.darker(240); -} - -void NodePathItem::draw(QPainter *painter,bool useGrad,int lighter) -{ - if(!visible_) - return; - - QColor border, bg, fontCol; - if(enabled_) - { - border=borderCol_; - bg=bgCol_; - fontCol=fontCol_; - } - else - { - border=disabledBorderCol_; - bg=disabledBgCol_; - fontCol=disabledFontCol_; - } - - QBrush bgBrush; - if(useGrad) - { - QColor bgLight; - Palette::statusColours(bg,bgLight,border); - - //QColor bgLight=bg.lighter(lighter); - grad_.setColorAt(0,bgLight); - grad_.setColorAt(1,bg); - bgBrush=QBrush(grad_); - } - else - bgBrush=QBrush(bg); - - painter->setPen(QPen(border,0)); - painter->setBrush(bgBrush); - painter->drawPolygon(shape_); - - /*if(current_) - { - painter->setPen(QPen(borderCol_,0)); - }*/ - - painter->setPen(fontCol); - painter->drawText(textRect_,Qt::AlignVCenter | Qt::AlignHCenter,(elidedText_.isEmpty())?text_:elidedText_); - -} - -//===================================================== -// -// NodePathServerItem -// -//===================================================== - -//It returns the x position of the top right corner! -int NodePathServerItem::rightPos(int xp,int len) const -{ - return xp+len; -} - -void NodePathServerItem::makeShape(int xp,int yp,int len) -{ - QVector vec; - vec << QPoint(0,0); - vec << QPoint(len,0); - vec << QPoint(len+triLen_,height_/2); - vec << QPoint(len,height_); - vec << QPoint(0,height_); - - shape_=QPolygon(vec).translated(xp,yp); - - textRect_=QRect(xp+hPadding_,yp,len,height_); -} - -//===================================================== -// -// NodePathEllipsisItem -// -//===================================================== - -NodePathEllipsisItem::NodePathEllipsisItem(BcWidget* owner) : - NodePathItem(owner,-1,QString(0x2026),QColor(240,240,240),QColor(Qt::black),false,false) -{ - borderCol_=QColor(190,190,190); -} - -//============================================================= -// -// NodePathWidget -// -//============================================================= - -NodePathWidget::NodePathWidget(QWidget *parent) : - QWidget(parent), - mode_(GuiMode) -{ - layout_=new QHBoxLayout(this); - layout_->setSpacing(0); - layout_->setContentsMargins(2,2,3,2); - setLayout(layout_); - - bc_=new BcWidget(this); - layout_->addWidget(bc_); - - connect(bc_,SIGNAL(itemSelected(int)), - this,SLOT(slotNodeSelected(int))); - connect(bc_,SIGNAL(menuSelected(int,QPoint)), - this,SLOT(slotMenuSelected(int,QPoint))); - - setAutoFillBackground(true); - - //We make the background transparent - QPalette pal=palette(); - pal.setColor(QPalette::Window,Qt::transparent); - setPalette(pal); -} - -NodePathWidget::~NodePathWidget() -{ - clear(true); -} - -void NodePathWidget::useTransparentBg(bool b) -{ - QPalette pal=palette(); - - if(b) - { - pal.setColor(QPalette::Window,Qt::transparent); - bc_->setTextColour(Qt::white); - bc_->setTextDisabledColour(QColor(220,220,220)); - } - else - { - pal.setColor(QPalette::Window,Qt::white); - bc_->setTextColour(Qt::black); - bc_->setTextDisabledColour(QColor(60,60,60)); - } - setPalette(pal); -} - -void NodePathWidget::clear(bool detachObservers) -{ - setEnabled(true); - - if(detachObservers && info_ && info_->server()) - { - info_->server()->removeNodeObserver(this); - info_->server()->removeServerObserver(this); - } - - if(detachObservers && info_) - { - info_->removeObserver(this); - } - - if(info_) - info_->removeObserver(this); - - info_.reset(); - - clearItems(); - - setEnabled(true); -} - -void NodePathWidget::clearItems() -{ - bc_->clear(); - - int cnt=nodeItems_.count(); - for(int i=0; i < cnt; i++) - { - delete nodeItems_.takeLast(); - } - nodeItems_.clear(); -} - -void NodePathWidget::setMode(Mode mode) -{ - if(mode_ != mode) - { - mode_=mode; - VInfo_ptr info=info_; - clear(true); - setPath(info); - } -} - -void NodePathWidget::slotContextMenu(const QPoint& pos) -{ -} - -void NodePathWidget::adjust(VInfo_ptr info,ServerHandler** serverOut,bool &sameServer) -{ - ServerHandler* server=0; - - //Check if there is data in info - if(info) - { - server=info->server(); - - sameServer=(info_)?(info_->server() == server):false; - - //Handle observers - if(!sameServer) - { - if(info_ && info_->server()) - { - info_->server()->removeServerObserver(this); - info_->server()->removeNodeObserver(this); - } - - info->server()->addServerObserver(this); - info->server()->addNodeObserver(this); - -#if 0 - if(server) - { - if(reloadTb_) - { - reloadTb_->setToolTip("Refresh server " + QString::fromStdString(server->name()) + ""); - reloadTb_->setEnabled(true); - } - } - else - { - reloadTb_->setToolTip(""); - reloadTb_->setEnabled(false); - } -#endif - } - } - //If the there is no data we clean everything and return - else - { - if(info_ && info_->server()) - { - info_->server()->removeServerObserver(this); - info_->server()->removeNodeObserver(this); - } -#if 0 - reloadTb_->setToolTip(""); - reloadTb_->setEnabled(false); -#endif - } - - //Set the info - if(info_) - { - info_->removeObserver(this); - } - - info_=info; - - if(info_) - { - info_->addObserver(this); - } - - *serverOut=server; -} - - -void NodePathWidget::reset() -{ - setPath(info_); -} - - -void NodePathWidget::setPath(VInfo_ptr info) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::setPath -->"; -#endif - - setEnabled(true); - - ServerHandler *server=0; - bool sameServer=false; - - VInfo_ptr info_ori=info_; - - adjust(info,&server,sameServer); - - if(!info_ || !info_->server()) - { - clear(); - return; - } - else - { - clearItems(); - } - - //------------------------------------ - // Only text is displayed - //------------------------------------ - - if(mode_ == TextMode) - { - info_=info; - QString pt; - if(info_) - pt=QString::fromStdString(info_->path()); - - bc_->reset(pt,bcWidth()); - return; - } - - //------------------------------------ - // Interactive breadcrumsbs - //------------------------------------ - - Q_ASSERT(mode_ = GuiMode); - - //Get the node list including the server - std::vector lst; - if(info_->node()) - { - lst=info_->node()->ancestors(VNode::ParentToChildSort); - } - - //-------------------------------------------- - // Reset/rebuild the contents - //-------------------------------------------- - - for(unsigned int i=0; i < lst.size(); i++) - { - //--------------------------- - // Create node/server item - //--------------------------- - - QColor col; - QString name; - NodePathItem* nodeItem=0; - - VNode *n=lst.at(i); - col=n->stateColour(); -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " state=" << n->stateName(); -#endif - QColor fontCol=n->stateFontColour(); - name=n->name(); - bool hasChildren=(n->numOfChildren() >0); - - if(i==0) - { - nodeItem=new NodePathServerItem(bc_,i,name,col,fontCol,hasChildren,(i == lst.size()-1)?true:false); - } - else - { - nodeItem=new NodePathItem(bc_,i,name,col,fontCol,hasChildren,(i == lst.size()-1)?true:false); - } - nodeItems_ << nodeItem; - } - - bc_->reset(nodeItems_,bcWidth()); - -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "<-- setPath"; -#endif -} - -int NodePathWidget::bcWidth() -{ - //return width()-reloadTb_->width()-5; - return width()-5; -} - -void NodePathWidget::slotNodeSelected(int idx) -{ - Q_ASSERT(mode_ == GuiMode); - if(idx != -1) - { - Q_EMIT selected(nodeAt(idx)); - } -} - -void NodePathWidget::slotMenuSelected(int idx,QPoint bcPos) -{ - Q_ASSERT(mode_ == GuiMode); - if(idx != -1) - { - loadMenu(bc_->mapToGlobal(bcPos),nodeAt(idx)); - } -} - -//------------------------------------------------------------------------------------------- -// Get the object from nodeItems_ at position idx. -// This is the order/position of the items: -// -// 0 1 2 .... nodeItems_.count()-2 nodeItems_.count()-1 -// server node's parent node (=info_) -//-------------------------------------------------------------------------------------------- - -VInfo_ptr NodePathWidget::nodeAt(int idx) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::nodeAt idx=" << idx; -#endif - - Q_ASSERT(mode_ == GuiMode); - if(mode_ == TextMode) - return VInfo_ptr(); - - ServerHandler* server=info_->server(); - - if(info_ && server) - { - if(VNode *n=info_->node()->ancestorAt(idx,VNode::ParentToChildSort)) - { - if(n == info_->node()) - return info_; - else if(n->isServer()) - return VInfoServer::create(n->server()); - else - return VInfoNode::create(n); - } - } - - return VInfo_ptr(); -} - -void NodePathWidget::loadMenu(const QPoint& pos,VInfo_ptr p) -{ - Q_ASSERT(mode_ == GuiMode); - if(mode_ == TextMode) - return; - - if(p && p->node()) - { - QList acLst; - VNode* node=p->node(); - - for(int i=0; i < node->numOfChildren(); i++) - { - QAction *ac=new QAction(node->childAt(i)->name(),this); - ac->setData(i); - acLst << ac; - } - - if(acLst.count() > 0) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().info() << "NodePathWidget::loadMenu"; -#endif - - if(QAction *ac=QMenu::exec(acLst,pos,acLst.front(),this)) - { - int idx=ac->data().toInt(); - VInfo_ptr res=VInfoNode::create(node->childAt(idx)); - Q_EMIT selected(res); - } - } - - Q_FOREACH(QAction* ac,acLst) - { - delete ac; - } - } -} - -void NodePathWidget::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange&) -{ - Q_ASSERT(mode_ == GuiMode); - if(mode_ == TextMode) - return; - -#if 0 - if(!active_) - return; -#endif - - //Check if there is data in info - if(info_ && !info_->isServer() && info_->node()) - { - //TODO: MAKE IT SAFE!!!! - - //State changed - if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::STATE) != aspect.end() || - std::find(aspect.begin(),aspect.end(),ecf::Aspect::SUSPENDED) != aspect.end()) - { - std::vector nodes=info_->node()->ancestors(VNode::ParentToChildSort); - for(int i=0; i < static_cast(nodes.size()); i++) - { - if(nodes[i] == node) - { - if(i < nodeItems_.count()) - { - bc_->reset(i,node->name(),node->stateColour(),node->stateFontColour()); - } - return; - } - } - } - - //A child was removed or added - else if(std::find(aspect.begin(),aspect.end(),ecf::Aspect::ADD_REMOVE_NODE) != aspect.end()) - { - std::vector nodes=info_->node()->ancestors(VNode::ParentToChildSort); - for(unsigned int i=0; i < nodes.size(); i++) - { - if(node == nodes.at(i)) - { - //Reload everything - setPath(info_); - } - } - } - } -} - -void NodePathWidget::notifyDefsChanged(ServerHandler* server,const std::vector& aspect) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::notifyDefsChanged -->"; -#endif - - Q_ASSERT(mode_ == GuiMode); - if(mode_ == TextMode) - return; -#if 0 - if(!active_) - return; -#endif - - //Check if there is data in info - if(info_ && info_->server() && info_->server() == server) - { - UiLog().dbg() << "Server change"; - - //State changed - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::SERVER_STATE) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " update server item"; -#endif - if(nodeItems_.count() > 0) - { - bc_->reset(0,server->vRoot()->name(), - server->vRoot()->stateColour(), - server->vRoot()->stateFontColour()); - } - - } - } - } -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "<-- notifyDefsChanged"; -#endif -} - -//This must be called at the beginning of a reset -void NodePathWidget::notifyBeginServerClear(ServerHandler* server) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::notifyBeginServerClear -->"; -#endif - if(info_) - { - if(info_->server() && info_->server() == server) - { - setEnabled(false); - } - } -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "<-- notifyBeginServerClear"; -#endif -} - -//This must be called at the end of a reset -void NodePathWidget::notifyEndServerScan(ServerHandler* server) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::notifyEndServerScan -->"; -#endif - if(info_) - { - if(info_->server() && info_->server() == server) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " setEnabled(true)"; -#endif - setEnabled(true); - -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " regainData"; -#endif - //We try to ressurect the info. We have to do it explicitly because it is not guaranteed that - //notifyEndServerScan() will be first called on the VInfo then on the breadcrumbs. So it - //is possible that the node still exists but it is still set to NULL in VInfo. - info_->regainData(); - - //If the info is not available dataLost() must have already been called and - //the breadcrumbs were reset! - if(!info_) - return; - - Q_ASSERT(info_->server() && info_->node()); - -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " reset"; -#endif - reset(); - } - } - -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "<-- notifyEndServerScan"; -#endif -} - -void NodePathWidget::notifyServerDelete(ServerHandler* server) -{ - if(info_ && info_->server() == server) - { - //We do not want to detach ourselves as an observer the from the server. When this function is - //called the server actually loops through its observers and notify them. - clear(false); - } -} - -void NodePathWidget::notifyServerConnectState(ServerHandler* server) -{ - //TODO: we need to indicate the state here! - if(info_ && info_->server() == server) - { - reset(); - } -} - -void NodePathWidget::notifyServerActivityChanged(ServerHandler* /*server*/) -{ - //reset(); -} - -void NodePathWidget::notifyDataLost(VInfo* info) -{ -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "NodePathWidget::notifyDataLost -->"; -#endif - - if(info_ && info_.get() == info) - { -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << " clear(true)"; -#endif - clear(true); - } -#ifdef _UI_NODEPATHWIDGET_DEBUG - UiLog().dbg() << "<-- notifyDataLost"; -#endif -} - -void NodePathWidget::slotRefreshServer() -{ - Q_ASSERT(mode_ == GuiMode); - if(mode_ == TextMode) - return; - - if(info_ && info_->server()) - { - info_->server()->refresh(); - } -} - -void NodePathWidget::rerender() -{ - reset(); -} - -void NodePathWidget::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void NodePathWidget::resizeEvent(QResizeEvent *) -{ - bc_->adjustSize(bcWidth()); -} - -void NodePathWidget::writeSettings(VSettings *vs) -{ - vs->beginGroup("breadcrumbs"); - vs->put("mode",(mode_==TextMode)?"text":"gui"); - vs->endGroup(); -} - -void NodePathWidget::readSettings(VSettings* vs) -{ - vs->beginGroup("breadcrumbs"); - std::string modeStr=vs->get("mode",""); - - if(modeStr == "text") - setMode(TextMode); - else - setMode(GuiMode); - - vs->endGroup(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/NodePathWidget.hpp ecflow-4.11.1/Viewer/src/NodePathWidget.hpp --- ecflow-4.9.0/Viewer/src/NodePathWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodePathWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,244 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef NODEPATHWIDGET_H -#define NODEPATHWIDGET_H - -#include "NodeObserver.hpp" -#include "ServerObserver.hpp" -#include "VInfo.hpp" -#include "VProperty.hpp" - -#include -#include - -#include - -class QAction; -class QHBoxLayout; -class QMenu; -class QToolButton; - -class Node; -class NodePathWidget; -class PropertyMapper; -class VProperty; -class VSettings; - -class NodePathItem; -class NodePathEllipsisItem; - -class BcWidget : public QWidget, public VPropertyObserver -{ - -Q_OBJECT - -public: - explicit BcWidget(QWidget *parent=0); - ~BcWidget(); - - void reset(QString txt, int maxWidth); - void reset(QList,int); - void reset(int idx,QString text,QColor bgCol,QColor fontCol); - void clear(); - void adjustSize(int); - QFont font() const {return font_;} - void setTextColour(QColor c) {textCol_=c;} - void setTextDisabledColour(QColor c) {textDisabledCol_=c;} - - void notifyChange(VProperty*); - -Q_SIGNALS: - void itemSelected(int); - void menuSelected(int,QPoint); - -protected: - void paintEvent(QPaintEvent*); - void mouseMoveEvent(QMouseEvent *event); - void mousePressEvent(QMouseEvent* event); - void changeEvent(QEvent* event); - - void updateSettings(); - void reset(int); - void resetBorder(int); - void crePixmap(); - void updatePixmap(int); - bool isFull() const; - int estimateWidth(int,int,int); - int adjustItems(int,int,int,int); - int adjustVisibleItems(int,int,int,int); - - QFont font_; - QPixmap pix_; - int hMargin_; - int vMargin_; - int gap_; - int width_; - int maxWidth_; - int height_; - int itemHeight_; - QString text_; - QRect textRect_; - QColor textCol_; - QColor textDisabledCol_; - QList items_; - NodePathEllipsisItem* ellipsisItem_; - - PropertyMapper* prop_; - bool useGrad_; - int gradLighter_; - int hovered_; - bool elided_; -}; - -class NodePathItem -{ - -friend class BcWidget; - -public: - NodePathItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current); - virtual ~NodePathItem() {} - - void setCurrent(bool); - virtual void draw(QPainter*,bool,int); - int adjust(int xp,int yp, int elidedLen=0); - int estimateRightPos(int xp,int elidedLen=0); - void resetBorder(bool hovered); - void reset(QString text,QColor bgCol,QColor fontCol,bool hovered); - int textLen() const; - static int height(QFont); - -protected: - virtual void makeShape(int xp,int yp,int len); - virtual int rightPos(int xp,int len) const; - - BcWidget* owner_; - int index_; - QString text_; - QString elidedText_; - QColor bgCol_; - QColor borderCol_; - QColor fontCol_; - QPolygon shape_; - QRect textRect_; - QFont font_; - - bool current_; - bool hasMenu_; - bool visible_; - QLinearGradient grad_; - bool enabled_; - static QColor disabledBgCol_; - static QColor disabledBorderCol_; - static QColor disabledFontCol_; - - static int triLen_; - static int height_; - static int hPadding_; - static int vPadding_; -}; - -class NodePathServerItem : public NodePathItem -{ - -friend class BcWidget; - -public: - NodePathServerItem(BcWidget* owner,int index,QString text,QColor bgCol,QColor fontCol,bool hasMenu,bool current) : - NodePathItem(owner,index,text,bgCol,fontCol,hasMenu,current) {} -protected: - void makeShape(int xp,int yp,int len); - int rightPos(int xp,int len) const; -}; - - -class NodePathEllipsisItem : public NodePathItem -{ - -friend class BcWidget; - -public: - NodePathEllipsisItem(BcWidget* owner); -}; - - -class NodePathWidget : public QWidget, public NodeObserver, public ServerObserver, public VInfoObserver -{ -Q_OBJECT - -public: - explicit NodePathWidget(QWidget* parent=0); - ~NodePathWidget(); - - enum Mode {TextMode,GuiMode}; - - void clear(bool detachObservers=true); - void setMode(Mode mode); - bool isGuiMode() const {return mode_==GuiMode;} - void useTransparentBg(bool b); - - //From NodeObserver - void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); - void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) {} - - //From ServerObserver - void notifyDefsChanged(ServerHandler* server,const std::vector&); - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - void notifyEndServerClear(ServerHandler* server) {} - void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - void notifyEndServerScan(ServerHandler* server); - void notifyServerConnectState(ServerHandler* server); - void notifyServerActivityChanged(ServerHandler* server); - - //From VInfoObserver - void notifyDelete(VInfo*) {} - void notifyDataLost(VInfo*); - - void rerender(); - - void writeSettings(VSettings *vs); - void readSettings(VSettings *vs); - -public Q_SLOTS: - void setPath(VInfo_ptr); - -protected Q_SLOTS: - void slotContextMenu(const QPoint&); - void slotNodeSelected(int); - void slotMenuSelected(int,QPoint); - void slotRefreshServer(); - -Q_SIGNALS: - void selected(VInfo_ptr); - void activeStateChanged(); - -protected: - void clearItems(); - void adjust(VInfo_ptr info,ServerHandler** serverOut,bool &sameServer); - void loadMenu(const QPoint& pos,VInfo_ptr p); - VInfo_ptr nodeAt(int); - void infoIndex(int idx); - void paintEvent(QPaintEvent *); - void resizeEvent(QResizeEvent *); - void reset(); - void updateSettings(); - int bcWidth(); - - QList nodeItems_; - - QHBoxLayout* layout_; - VInfo_ptr info_; - VInfo_ptr infoFull_; - BcWidget* bc_; - Mode mode_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryCombo.cpp ecflow-4.11.1/Viewer/src/NodeQueryCombo.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryCombo.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryCombo.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryCombo.hpp" - -#include "NodeQuery.hpp" -#include "NodeQueryHandler.hpp" - -#include - -NodeQueryCombo::NodeQueryCombo(QWidget* parent) : QComboBox(parent) -{ - for(std::vector::const_iterator it=NodeQueryHandler::instance()->items().begin(); - it != NodeQueryHandler::instance()->items().end(); ++it) - { - addItem(QString::fromStdString((*it)->name())); - } - - connect(this,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotCurrentChanged(int))); -} - -void NodeQueryCombo::slotCurrentChanged(int current) -{ - if(current != -1) - Q_EMIT changed(itemData(current).toString()); -} - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryCombo.hpp ecflow-4.11.1/Viewer/src/NodeQueryCombo.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryCombo.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryCombo.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_NODEQUERYCOMBO_HPP_ -#define VIEWER_SRC_NODEQUERYCOMBO_HPP_ - -#include - -class NodeQueryCombo : public QComboBox -{ -Q_OBJECT - -public: - explicit NodeQueryCombo(QWidget* parent=0); - -protected Q_SLOTS: - void slotCurrentChanged(int current); - -Q_SIGNALS: - void changed(QString); -}; - -#endif /* VIEWER_SRC_NODEQUERYCOMBO_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQuery.cpp ecflow-4.11.1/Viewer/src/NodeQuery.cpp --- ecflow-4.9.0/Viewer/src/NodeQuery.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQuery.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,496 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQuery.hpp" - -#include "NodeQueryOption.hpp" -#include "UiLog.hpp" -#include "VAttributeType.hpp" -#include "VSettings.hpp" - -int NodeQuery::defaultMaxNum_=50000; - -#define _UI_NODEQUERY_DEBUG - -QString NodeQueryAttrGroup::query() const -{ - QStringList lst; - Q_FOREACH(NodeQueryOption* op,options_) - { - QString s=op->query().simplified(); - if(!s.isEmpty()) - lst << op->query(); - } - - if(lst.count() == 0) - return name_; - else if(lst.count() == 1) - { - return lst[0]; - } - else - { - return "(" + lst.join(" and ") + ")"; - } - - return QString(); -} - -bool NodeQueryVarAttrGroup::hasType(VAttributeType* t) const -{ - if(types_.contains(t)) - { - Q_FOREACH(NodeQueryOption* op,options_) - { - if(op->name() == "var_type") - { - QString v=op->valueAsString(); -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << "NodeQueryVarAttrGroup::hasType var_type=" << v.toStdString(); -#endif - if(v == "any") - return true; - else - return (v == t->name()); - } - } - - Q_ASSERT(false); - } - - return false; -} - -NodeQuery::NodeQuery(const std::string& name,bool ignoreMaxNum) : - name_(name), - advanced_(false), - caseSensitive_(false), - maxNum_(defaultMaxNum_), - ignoreMaxNum_(ignoreMaxNum) -{ -#ifdef _UI_NODEQUERY_DEBUG - UI_FUNCTION_LOG -#endif - - //Build options from defs. Options store the actual query settings. They are - //editable through the gui. - NodeQueryOption::build(this); -} - -NodeQuery::~NodeQuery() -{ - qDeleteAll(options_); -} - -NodeQuery* NodeQuery::clone() -{ - return clone(name_); -} - -NodeQuery* NodeQuery::clone(const std::string& name) -{ - NodeQuery *q=new NodeQuery(name); - q->swap(this); - - return q; -} - -void NodeQuery::swap(const NodeQuery* q) -{ - advanced_=q->advanced_; - //query_=q->query_; - extQuery_=q->extQuery_; - rootNode_=q->rootNode_; - servers_=q->servers_; - //allServers_=q->allServers_; - caseSensitive_=q->caseSensitive_; - maxNum_=q->maxNum_; - ignoreMaxNum_=q->ignoreMaxNum_; - - for(QMap::const_iterator it = options_.constBegin(); it != options_.constEnd(); ++it) - it.value()->swap(q->option(it.key())); - - buildQueryString(); -} - -void NodeQuery::setName(const std::string& name) -{ - name_=name; -} - -bool NodeQuery::hasServer(const std::string& name) const -{ -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << "NodeQuery::hasServer -->"; -#endif - - if(servers_.empty()) - return true; - - return servers_.contains(QString::fromStdString(name)); -} - -NodeQueryOption* NodeQuery::option(QString name) const -{ - QMap::const_iterator it = options_.find(name); - if(it != options_.constEnd()) - return it.value(); - return NULL; -} - -QString NodeQuery::query() const -{ - QString s1=nodeQueryPart(); - QString s2=attrQueryPart(); - if(!s1.isEmpty()) - if(!s2.isEmpty()) - return s1+ " and " + s2; - else - return s1; - else - return s2; - - return QString(); -} - - - -QString NodeQuery::nodeQueryPart() const -{ - QStringList lst; - QStringList keys; - keys << "node" << "type" << "state" << "flag" << "status_change_time"; - Q_FOREACH(QString s,keys) - { - if(!extQuery_.value(s).isEmpty()) - lst << extQuery_.value(s); - } - - //TODO : handle all - return lst.join(" and "); -} - -bool NodeQuery::hasBasicNodeQueryPart() const -{ - QStringList keys; - keys << "node" << "type" << "state" << "flag"; - Q_FOREACH(QString s,keys) - { - if(!extQuery_.value(s).isEmpty()) - return true; - } - return false; -} - -bool NodeQuery::hasPeriodQueryPart() const -{ - return !extQuery_.value("status_change_time").isEmpty(); -} - -QString NodeQuery::attrQueryPart() const -{ - return extQuery_["attr"]; -} - -QString NodeQuery::attrQueryPart(VAttributeType* t) const -{ - //TODO - QStringList attrSel=attrSelection(); - QString q; - Q_FOREACH(NodeQueryAttrGroup* tm,attrGroup_.values()) - { - if(tm->hasType(t) && - attrSel.contains(tm->name())) - { - QStringList qLst; - - Q_FOREACH(NodeQueryOption* op,tm->options()) - { - Q_ASSERT(op); - QString s=op->query(); - if(!s.isEmpty()) - { - qLst << s; - } - } - - if(qLst.isEmpty()) - q=t->name(); - else - q=qLst.join(" and "); - - break; - } - } - return q; -} - -bool NodeQuery::hasAttribute(VAttributeType *t) const -{ - Q_FOREACH(NodeQueryAttrGroup* d,attrGroup_.values()) - { - if(d->hasType(t)) - { - return attrSelection().contains(d->name()); - } - } - return false; -} - -QStringList NodeQuery::attrSelection() const -{ - NodeQueryOption *a=options_["attribute"]; - Q_ASSERT(a); - NodeQueryListOption* op=static_cast(a); - Q_ASSERT(op); - return op->selection(); -} - -NodeQueryListOption* NodeQuery::stateOption() const -{ - NodeQueryOption* op=option("state"); - Q_ASSERT(op); - return op->isList(); -} - -void NodeQuery::buildQueryString() -{ -#ifdef _UI_NODEQUERY_DEBUG - UI_FUNCTION_LOG -#endif - - extQuery_.clear(); - - //Node - QStringList nodePart; - QString name=options_["node_name"]->query(); - QString path=options_["node_path"]->query(); - if(!name.isEmpty()) nodePart << name; - if(!path.isEmpty()) nodePart << path; - if(nodePart.count() > 0) - extQuery_["node"]="(" +nodePart.join(" and ") + ")"; - - //Type - QString typePart=options_["type"]->query(" or "); - if(!typePart.isEmpty()) - extQuery_["type"]="(" + typePart + ")"; - - //State - QString statePart=options_["state"]->query(" or "); - if(!statePart.isEmpty()) - extQuery_["state"]="(" + statePart + ")"; - - //Flag - QString flagPart=options_["flag"]->query(" or "); - if(!flagPart.isEmpty()) - extQuery_["flag"]="(" + flagPart + ")";; - - //Status change time - QString periodPart=options_["status_change_time"]->query(); - if(!periodPart.isEmpty()) - extQuery_["status_change_time"]="(" + periodPart + ")"; - - //Attributes - QString attrPart; - - Q_FOREACH(QString attrName,attrSelection()) - { - NodeQueryAttrGroup* grp=attrGroup_.value(attrName); - Q_ASSERT(grp); - QString grpPart=grp->query(); - - if(!attrPart.isEmpty()) - attrPart+=" or "; - - attrPart+=grpPart; - } - - if(!attrPart.isEmpty()) - //extQuery_["attr"]="(" + attrPart + ")"; - extQuery_["attr"]=attrPart; - - bool hasEq=QStringList(extQuery_.values()).join("").contains("="); - - //Scope - QString scopePart; - if(!servers_.isEmpty()) - scopePart="servers = \'" + servers_.join(", ") + "\'"; - - if(servers_.size() <= 1 && !rootNode_.empty()) - { - if(!scopePart.isEmpty()) - scopePart+=" and "; - - scopePart+="root_node = \'" + QString::fromStdString(rootNode_) + "\'"; - } - if(!scopePart.isEmpty()) - extQuery_["scope"]=scopePart; - - //Other options - QString opPart; - if(!ignoreMaxNum_) - { - opPart="max_results = " + QString::number(maxNum_); - } - - if(hasEq) - { - if(!opPart.isEmpty()) - opPart+=" and "; - if(caseSensitive_) - opPart+="case_sensitive"; - else - opPart+="case_insensitive"; - } - - if(!opPart.isEmpty()) - extQuery_["options"]=opPart; - - //SQL-like query - sqlQuery_.clear(); - QStringList selectPart; - QStringList fromPart; - QStringList wherePart; - sqlQuery_="SELECT"; - QStringList nodeParts; - nodeParts << "node" << "type" << "state" << "flag"; - Q_FOREACH(QString s,nodeParts) - { - QString vs=extQuery_.value(s); - if(!vs.isEmpty()) - { - vs.replace("<","<"); - vs.replace(">",">"); - wherePart << vs; - } - } - - QString sqlPeriodPart=options_["status_change_time"]->sqlQuery(); - if(!sqlPeriodPart.isEmpty()) - { - sqlPeriodPart="(" + sqlPeriodPart + ")"; - sqlPeriodPart.replace("<","<"); - sqlPeriodPart.replace(">",">"); - wherePart << sqlPeriodPart; - } - - selectPart << "node"; - - //Attribute - Q_FOREACH(QString attrName,attrSelection()) - { - NodeQueryAttrGroup* grp=attrGroup_.value(attrName); - Q_ASSERT(grp); - selectPart << grp->name(); - selectPart.removeOne("node"); - QString grpPart=grp->query(); - if(grpPart != grp->name()) - wherePart << grpPart; - } - - sqlQuery_+=" " + selectPart.join(", "); - - //FROM - if(!servers_.isEmpty()) - { - if(servers_.size() ==1 && !rootNode_.empty()) - { - fromPart << servers_[0] + ":/" + QString::fromStdString(rootNode_); - } - else - fromPart=servers_; - - sqlQuery_+=" FROM " + fromPart.join(", "); - } - else - { - //sqlQuery_+=" FROM *"; - } - - if(wherePart.count() > 0) - sqlQuery_+=" WHERE " + wherePart.join(" and "); - - if(!ignoreMaxNum_) - { - sqlQuery_+=" LIMIT " + QString::number(maxNum_); - } -} - -void NodeQuery::load(VSettings* vs) -{ - advanced_=vs->getAsBool("advanced",advanced_); - caseSensitive_=vs->getAsBool("case",caseSensitive_); - - int maxNum=vs->get("maxNum",maxNum_); - if(maxNum_ > 1 && maxNum < 5000000) - maxNum_=maxNum; - - std::vector v; - vs->get("servers",v); - servers_.clear(); - for(std::vector::const_iterator it=v.begin(); it != v.end(); ++it) - servers_ << QString::fromStdString(*it); - - //allServers_=vs->getAsBool("allServers",allServers_); - - rootNode_=vs->get("rootNode",rootNode_); - - Q_FOREACH(QString s,options_.keys()) - { - options_[s]->load(vs); - } -#if 0 - Q_FOREACH(QString s,selectOptions_.keys()) - { - selectOptions_[s]->load(vs); - } -#endif - buildQueryString(); - - //query_=QString::fromStdString(vs->get("query",query_.toStdString())); -} - - -void NodeQuery::save(VSettings* vs) -{ - if(advanced_) - vs->putAsBool("advanced",advanced_); - - if(caseSensitive_) - vs->putAsBool("case",caseSensitive_); - - if(maxNum_ != defaultMaxNum_) - vs->put("maxNum",maxNum_); - - std::vector v; - Q_FOREACH(QString s, servers_) - v.push_back(s.toStdString()); - - //if(!allServers_) - // vs->putAsBool("allServers",allServers_); - - if(!v.empty()) - vs->put("servers",v); - - if(!rootNode_.empty()) - vs->put("rootNode",rootNode_); - - Q_FOREACH(QString s,options_.keys()) - { - options_[s]->save(vs); - } -#if 0 - Q_FOREACH(QString s,selectOptions_.keys()) - { - selectOptions_[s]->save(vs); - } - #endif - //vs->put("query",query_.toStdString()); -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryEditor.cpp ecflow-4.11.1/Viewer/src/NodeQueryEditor.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,624 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryEditor.hpp" - -#include "ComboMulti.hpp" -#include "CustomListWidget.hpp" -#include "Highlighter.hpp" -#include "NodeQuery.hpp" -#include "NodeQueryHandler.hpp" -#include "NodeQueryOption.hpp" -#include "NodeQueryOptionEdit.hpp" -#include "ServerFilter.hpp" -#include "ServerHandler.hpp" -#include "VNode.hpp" -#include "VNState.hpp" - -#include -#include -#include -#include -#include -#include - -//====================================================== -// -// NodeQuerySaveDialog -// -//====================================================== - -NodeQuerySaveDialog::NodeQuerySaveDialog(QWidget *parent) : QDialog(parent) -{ - setupUi(this); -} - -QString NodeQuerySaveDialog::name() const -{ - return nameLe_->text(); -} - -void NodeQuerySaveDialog::accept() -{ - QString name=nameLe_->text(); - - if(!name.contains(QRegExp("[\\w|\\s]+"))) - { - QMessageBox::critical(0,tr("Invalid character"), - "Query names can only contain alphanumeric characters, whitespaces and \"_\". Please choose a different name."); - return; - - } - - if(NodeQueryHandler::instance()->find(name.toStdString())) - { - QMessageBox::critical(0,tr("Duplicated"), - "The specified name is already used by another query. Please choose a different name."); - return; - } - - QDialog::accept(); -} - -//====================================================== -// -// NodeQueryEditor -// -//====================================================== - -NodeQueryEditor::NodeQueryEditor(QWidget *parent) : - QWidget(parent), - query_(NULL), - serverFilter_(NULL), - queryTeCanExpand_(false), - initIsOn_(false), - canBeRun_(false), - filterMode_(false) -{ - setupUi(this); - - query_=new NodeQuery("tmp"); - //attrPanel_->setQuery(query_); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - rootLe_->setClearButtonEnabled(true); -#endif - - Q_ASSERT(tab_->count() == 2); - nodeTabText_=tab_->tabText(0); - attrTabText_=tab_->tabText(1); - - //------------------------- - // Query display - //------------------------- - - - //QFont f("Courier"); - /*QFont f("Monospace"); - f.setStyleHint(QFont::TypeWriter); - f.setFixedPitch(true); - f.setPointSize(10); - f.setStyleStrategy(QFont::PreferAntialias); - queryTe_->setFont(f);*/ - - QFont f; - QFontMetrics fm(f); - - queryTe_->setFixedHeight((fm.height()+2)*3+6); - queryTe_->setReadOnly(true); - queryTe_->setWordWrapMode(QTextOption::WordWrap); - - //The document becomes the owner of the highlighter - new Highlighter(queryTe_->document(),"query"); - - //------------------ - // Options - //------------------ - - //Max item num - numSpin_->setRange(10,250000); - numSpin_->setValue(query_->maxNum()); - numSpin_->setToolTip(tr("The maximum possible value is: ") + QString::number(numSpin_->maximum())); - - connect(numSpin_,SIGNAL(valueChanged(int)), - this,SLOT(slotMaxNum(int))); - - caseCb_->setChecked(query_->caseSensitive()); - - connect(caseCb_,SIGNAL(clicked(bool)), - this,SLOT(slotCase(bool))); - - //------------------------- - // Scope - //------------------------- - - //Servers - serverCb_->setMode(ComboMulti::FilterMode); - - serverResetTb_->setEnabled(serverCb_->hasSelection()); - - connect(serverCb_,SIGNAL(selectionChanged()), - this,SLOT(slotServerCbChanged())); - - connect(serverResetTb_,SIGNAL(clicked()), - serverCb_,SLOT(clearSelection())); - - //Root - connect(rootLe_,SIGNAL(textChanged(QString)), - this,SLOT(slotRootNodeEdited(QString))); - - //nodePathGrid_ - - //Name + path - nameEdit_=new NodeQueryStringOptionEdit(query_->option("node_name"),nodeGrid_,this,false); - pathEdit_=new NodeQueryStringOptionEdit(query_->option("node_path"),nodeGrid_,this,false); - - //Status change time - periodEdit_=new NodeQueryPeriodOptionEdit(query_->option("status_change_time"),nodeGrid_,this); - - //------------------------- - // Filter - //------------------------- - - //Node type - typeEdit_=new NodeQueryListOptionEdit(query_->option("type"),typeList_,typeResetTb_,this); - stateEdit_=new NodeQueryListOptionEdit(query_->option("state"),stateList_,stateResetTb_,this); - flagEdit_=new NodeQueryListOptionEdit(query_->option("flag"),flagList_,flagResetTb_,this); - - attrEdit_=new NodeQueryListOptionEdit(query_->option("attribute"),attrList_,attrResetTb_,this); - - int listHeight=(fm.height()+2)*6+6; - typeList_->setFixedHeight(listHeight); - stateList_->setFixedHeight(listHeight); - flagList_->setFixedHeight(listHeight); - - listHeight=(fm.height()+2)*10+6; - int listWidth=fm.width("variable")+60; - attrList_->setFixedHeight(listHeight); - attrList_->setFixedWidth(listWidth); - - //Attr panel - //connect(attrPanel_,SIGNAL(queryChanged()), - // this,SLOT(slotAttrPanelChanged())); - - - //Attributes - attrPanel_->setProperty("attrArea","1"); - Q_FOREACH(NodeQueryAttrGroup* aGrp,query_->attrGroup().values()) - { - Q_ASSERT(aGrp); - QString grName=aGrp->name(); - - Q_FOREACH(NodeQueryOption* op,aGrp->options()) - { - NodeQueryOptionEdit *e=0; - //TODO: use factory here - if(op->type() == "string") - e=new NodeQueryStringOptionEdit(op,attrGrid_,this,false); - else if(op->type() == "combo") - e=new NodeQueryComboOptionEdit(op,attrGrid_,this); - - Q_ASSERT(e); - attr_[grName] << e; - e->setVisible(false); - } - } - - attrPanelLayout_->addStretch(1); - //attrPanel_->hide(); - - //-------------------------------- - // Select tab - //-------------------------------- - tab_->setCurrentIndex(0); - - //-------------------------------- - // Query management - //-------------------------------- - - connect(saveAsTb_,SIGNAL(clicked()), - this,SLOT(slotSaveQueryAs())); - - connect(advModeTb_,SIGNAL(clicked(bool)), - this,SLOT(slotAdvMode(bool))); - - queryManageW_->hide(); - - /*advModeTb_->hide(); - queryCbLabel_->hide(); - queryCb_->hide(); - saveAsTb_->hide(); - saveTb_->hide();*/ - - //attrList_->hide(); - //attrLabel_->hide(); - //attrResetTb_->hide(); - - init(); - - checkGuiState(); -} - -NodeQueryEditor::~NodeQueryEditor() -{ - delete query_; - - if(serverFilter_) - serverFilter_->removeObserver(this); -} - -void NodeQueryEditor::setFilterMode(bool b) -{ - if(filterMode_ != b) - { - filterMode_=b; - Q_ASSERT(tab_->count() == 2); - tab_->setTabEnabled(1,!filterMode_); - } -} - -void NodeQueryEditor::init() -{ - initIsOn_=true; - - numSpin_->setValue(query_->maxNum()); - caseCb_->setChecked(query_->caseSensitive()); - - numSpin_->setEnabled(!query_->ignoreMaxNum()); - numLabel_->setEnabled(!query_->ignoreMaxNum()); - - //Servers - QStringList servers=query_->servers(); - if(servers == serverCb_->all()) - serverCb_->clearSelection(); - else - serverCb_->setSelection(servers); - - rootLe_->setText(QString::fromStdString(query_->rootNode())); - - //Node name - nameEdit_->init(query_); - pathEdit_->init(query_); - - //Lists - typeEdit_->init(query_); - stateEdit_->init(query_); - flagEdit_->init(query_); - attrEdit_->init(query_); - - //period - periodEdit_->init(query_); - - //attrPanel_->init(); - - initIsOn_=false; - - updateQueryTe(); - checkGuiState(); - - //switch to the first - tab_->setCurrentIndex(0); -} - -void NodeQueryEditor::showDefPanel(bool b) -{ - scopeBox_->setVisible(b); - optionBox_->setVisible(b); - tab_->setVisible(b); -} - -bool NodeQueryEditor::isDefPanelVisible() const -{ - return scopeBox_->isVisible(); -} - -void NodeQueryEditor::showQueryPanel(bool b) -{ - queryLabel_->setVisible(b); - queryTe_->setVisible(b); -} - -bool NodeQueryEditor::isQueryPanelVisible() const -{ - return queryTe_->isVisible(); -} - -void NodeQueryEditor::slotAdvMode(bool b) -{ -#if 0 - // filterBox_->setVisible(!b); - queryTe_->setReadOnly(!b); -#endif -} - -void NodeQueryEditor::slotMaxNum(int v) -{ - if(!initIsOn_) - { - query_->setMaxNum(v); - updateQueryTe(); - } -} - -void NodeQueryEditor::slotCase(bool b) -{ - if(!initIsOn_) - { - query_->setCaseSensitive(b); - updateQueryTe(); - } -} - -void NodeQueryEditor::slotServerCbChanged() -{ - serverResetTb_->setEnabled(serverCb_->hasSelection()); - - if(!initIsOn_) - { - query_->setServers(serverCb_->selection()); - updateQueryTe(); - checkGuiState(); - } -} - -void NodeQueryEditor::slotRootNodeEdited(QString s) -{ - if(!initIsOn_) - { - query_->setRootNode(rootLe_->text().simplified().toStdString()); - updateQueryTe(); - checkGuiState(); - } -} - -void NodeQueryEditor::slotOptionEditChanged() -{ - if(!initIsOn_) - { - NodeQueryOptionEdit *e=static_cast(sender()); - Q_ASSERT(e); - if(e->optionId() == "attribute") - setAttributePanel(attrList_->selection()); - - updateQueryTe(); - checkGuiState(); - } -} - -void NodeQueryEditor::setAttributePanel(QStringList lst) -{ - QMapIterator > it(attr_); - while (it.hasNext()) - { - it.next(); - bool st=lst.contains(it.key()); - Q_FOREACH(NodeQueryOptionEdit* e,it.value()) - { - e->setVisible(st); - } - } -#if 0 - if(lst.isEmpty()) - attrPanel_->hide(); - else - attrPanel_->show(); -#endif -} - - -#if 0 -void NodeQueryEditor::slotAttrListChanged() -{ - if(!initIsOn_) - { - attrResetTb_->setEnabled(attrList_->hasSelection()); - attrPanel_->setSelection(attrList_->selection()); - query_->setAttrGroupSelection(attrList_->selection()); - updateQueryTe(); - checkGuiState(); - } -} -#endif - - -void NodeQueryEditor::slotAttrPanelChanged() -{ - if(!initIsOn_) - { - updateQueryTe(); - checkGuiState(); - } -} - -void NodeQueryEditor::checkGuiState() -{ - serverResetTb_->setEnabled(serverCb_->hasSelection()); - - bool oneServer=(serverCb_->count() == 1 || serverCb_->selection().count() == 1); - - rootLabel_->setEnabled(oneServer); - rootLe_->setEnabled(oneServer); - - QString t=nodeTabText_; - if(query_->hasBasicNodeQueryPart()) - t+="*"; - tab_->setTabText(0,t); - - if(!filterMode_) - { - t=attrTabText_; - if(!query_->attrQueryPart().isEmpty()) - t+="*"; - tab_->setTabText(1,t); - } -} - -void NodeQueryEditor::updateQueryTe() -{ - query_->buildQueryString(); - - QColor bg(241,241,241); - queryTe_->setHtml(query_->sqlQuery()); -} - -void NodeQueryEditor::slotClear() -{ - nameEdit_->clear(); - pathEdit_->clear(); - typeEdit_->clear(); - stateEdit_->clear(); - flagEdit_->clear(); - periodEdit_->clear(); - attrEdit_->clear(); -} - -//------------------------------------------ -// Servers -//------------------------------------------ - -QStringList NodeQueryEditor::allServers() const -{ - return serverCb_->all(); -} - -void NodeQueryEditor::updateServers() -{ - serverCb_->clear(); - - if(serverFilter_) - { - for(std::vector::const_iterator it=serverFilter_->items().begin(); it != serverFilter_->items().end(); ++it) - { - serverCb_->addItem(QString::fromStdString((*it)->name())); - } - } - - //Init - if(serverCb_->count() == 1) - { - //serverCb_->selectSoleItem(); - } - else - { - //slotServerCbChanged(); - } - - slotServerCbChanged(); - -} - -void NodeQueryEditor::setServerFilter(ServerFilter* sf) -{ - if(serverFilter_ == sf) - return; - - if(serverFilter_) - { - serverFilter_->removeObserver(this); - } - - serverFilter_=sf; - - if(serverFilter_) - { - serverFilter_->addObserver(this); - } - - updateServers(); -} - -void NodeQueryEditor::notifyServerFilterAdded(ServerItem* item) -{ - /*ServerHandler* s=item->serverHandler(); - s->addServerObserver(this); - updateTitle();*/ -} - -void NodeQueryEditor::notifyServerFilterRemoved(ServerItem* item) -{ - /*ServerHandler* s=item->serverHandler(); - s->removeServerObserver(this); - updateTitle();*/ -} - -void NodeQueryEditor::notifyServerFilterChanged(ServerItem*) -{ - //updateTitle(); -} - -void NodeQueryEditor::notifyServerFilterDelete() -{ - serverFilter_->removeObserver(this); - serverFilter_=0; -} - -//------------------------------------------ -// Root node -//------------------------------------------ - -void NodeQueryEditor::setRootNode(VInfo_ptr info) -{ - if(info && info.get()) - { - if(ServerHandler *server=info->server()) - { - QStringList sel(QString::fromStdString(server->name())); - serverCb_->setSelection(sel); - - if(info->isNode()) - { - if(VNode *node=info->node()) - { - rootLe_->setText(QString::fromStdString(node->absNodePath())); - } - } - else - { - rootLe_->clear(); - } - } - } -} - -//------------------------------------------ -// Query -//------------------------------------------ - -int NodeQueryEditor::maxNum() const -{ - return numSpin_->value(); -} - -NodeQuery* NodeQueryEditor::query() const -{ - return query_; -} - -//------------------------------------------ -// Query management -//------------------------------------------ - -void NodeQueryEditor::setQuery(NodeQuery* q) -{ - query_->swap(q); - init(); -} - - -void NodeQueryEditor::slotSaveQueryAs() -{ - NodeQuerySaveDialog d(this); - if(d.exec() == QDialog::Accepted) - { - std::string name=d.name().toStdString(); - NodeQuery* q=query_->clone(name); - NodeQueryHandler::instance()->add(q,true); - } -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryEditor.hpp ecflow-4.11.1/Viewer/src/NodeQueryEditor.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef NODEQUERYEDITOR_HPP_ -#define NODEQUERYEDITOR_HPP_ - -#include "ui_NodeQueryEditor.h" -#include "ui_NodeQuerySaveDialog.h" - -#include -#include -#include - -#include "ServerFilter.hpp" -#include "VInfo.hpp" - -class NodeQuery; -class NodeQueryDef; -class NodeQueryListModel; -class NodeQueryOptionEdit; - -class NodeQuerySaveDialog : public QDialog, protected Ui::NodeQuerySaveDialog -{ -Q_OBJECT - -public: - explicit NodeQuerySaveDialog(QWidget *parent = 0); - ~NodeQuerySaveDialog() {} - QString name() const; - -public Q_SLOTS: - void accept(); -}; - -class NodeQueryEditor : public QWidget, protected Ui::NodeQueryEditor, public ServerFilterObserver -{ - Q_OBJECT - -public: - explicit NodeQueryEditor(QWidget *parent = 0); - ~NodeQueryEditor(); - - void setServerFilter(ServerFilter*); - void setRootNode(VInfo_ptr); - void setQuery(NodeQuery*); - NodeQuery* query() const; - void setQueryTeCanExpand(bool); - bool isDefPanelVisible() const; - void showDefPanel(bool); - bool isQueryPanelVisible() const; - void showQueryPanel(bool); - int maxNum() const; - QStringList allServers() const; - void setFilterMode(bool); - - void notifyServerFilterAdded(ServerItem*); - void notifyServerFilterRemoved(ServerItem*); - void notifyServerFilterChanged(ServerItem*); - void notifyServerFilterDelete(); - -public Q_SLOTS: - void slotClear(); - -protected Q_SLOTS: - void slotOptionEditChanged(); - void slotServerCbChanged(); - void slotRootNodeEdited(QString); - void slotAttrPanelChanged(); - void slotSaveQueryAs(); - void slotAdvMode(bool b); - void slotMaxNum(int); - void slotCase(bool); - -Q_SIGNALS: - void queryEnabledChanged(bool); - -private: - void updateServers(); - void init(); - void initAttr(); - void updateQueryTe(); - void checkGuiState(); - void setAttributePanel(QStringList lst); - - NodeQuery* query_; - ServerFilter* serverFilter_; - bool queryTeCanExpand_; - bool initIsOn_; - bool canBeRun_; - bool filterMode_; - - NodeQueryOptionEdit* nameEdit_; - NodeQueryOptionEdit* pathEdit_; - NodeQueryOptionEdit* typeEdit_; - NodeQueryOptionEdit* stateEdit_; - NodeQueryOptionEdit* flagEdit_; - NodeQueryOptionEdit* periodEdit_; - NodeQueryOptionEdit* attrEdit_; - QMap > attr_; - QString nodeTabText_; - QString attrTabText_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryEditor.ui ecflow-4.11.1/Viewer/src/NodeQueryEditor.ui --- ecflow-4.9.0/Viewer/src/NodeQueryEditor.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryEditor.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,401 +0,0 @@ - - - NodeQueryEditor - - - - 0 - 0 - 1564 - 1113 - - - - - 0 - 0 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - Queries: - - - - - - - - - - Save query as ... - - - - - - - Save query - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Advanced mode - - - true - - - - - - - - - - - - Scope - - - - - - - - - Clear server filter - - - ... - - - - :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg - - - true - - - - - - - Search root node: - - - - - - - Search in servers: - - - - - - - - - - - - - Global options - - - - - - - - Max results: - - - - - - - - 0 - 0 - - - - - - - - - - Case sensitive - - - - - - - - - - - - 1 - - - - Nodes - - - - - - - - - - - Flag - - - - - - - Type - - - - - - - Clear status filter - - - ... - - - - :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg - - - true - - - - - - - Clear type filter - - - ... - - - - :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg - - - true - - - - - - - Status - - - - - - - Clear flag filter - - - ... - - - - :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg - - - true - - - - - - - - - - - - - - - - - - - Attributes - - - - - - - - Types - - - - - - - .a.. - - - - :/viewer/reset_to_default.svg:/viewer/reset_to_default.svg - - - true - - - - - - - - - - 0 - 0 - - - - - - - - true - - - - - 0 - 0 - 1281 - 646 - - - - - - - - - - - - - - Attribute-specific options - - - - - - - - - - - 4 - - - - - Query - - - - - - - - 0 - 0 - - - - This is the query presented in a MySQL-like syntax. - - - true - - - - - - - - - - ComboMulti - QComboBox -
        ComboMulti.hpp
        -
        - - CustomListWidget - QListWidget -
        CustomListWidget.hpp
        -
        - - NodeQueryCombo - QComboBox -
        NodeQueryCombo.hpp
        -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryEngine.cpp ecflow-4.11.1/Viewer/src/NodeQueryEngine.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryEngine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryEngine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,422 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryEngine.hpp" - -#include -#include - -#include "NodeExpression.hpp" -#include "NodeQuery.hpp" -#include "ServerDefsAccess.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "VAttributeType.hpp" -#include "VFilter.hpp" -#include "VNode.hpp" - -static bool metaRegistered=false; - -NodeQueryEngine::NodeQueryEngine(QObject* parent) : - QThread(parent), - query_(new NodeQuery("tmp")), - parser_(NULL), - cnt_(0), - scanCnt_(0), - maxNum_(250000), - chunkSize_(100), - stopIt_(false), - maxReached_(false), - rootNode_(0) -{ - //We will need to pass various non-Qt types via signals and slots - //So we need to register these types. - if(!metaRegistered) - { - qRegisterMetaType("NodeQueryResultTmp_ptr"); - qRegisterMetaType >("QList"); - metaRegistered=true; - } - - connect(this,SIGNAL(finished()), - this,SLOT(slotFinished())); -} - -NodeQueryEngine::~NodeQueryEngine() -{ - delete query_; - - if(parser_) - delete parser_; -} - -bool NodeQueryEngine::runQuery(NodeQuery* query,QStringList allServers) -{ - UiLog().dbg() << "NodeQueryEngine::runQuery -->"; - - if(isRunning()) - wait(); - - stopIt_=false; - maxReached_=false; - res_.clear(); - cnt_=0; - scanCnt_=0; - rootNode_=0; - - query_->swap(query); - - maxNum_=query_->maxNum(); - - servers_.clear(); - - //Init the parsers - if(parser_) - { - delete parser_; - parser_=NULL; - } - - qDeleteAll(attrParser_); - attrParser_.clear(); - - //The nodequery parser - UiLog().dbg() << " node part: " << query_->nodeQueryPart().toStdString(); - - parser_=NodeExpressionParser::instance()->parseWholeExpression(query_->nodeQueryPart().toStdString(), query->caseSensitive()); - if(parser_ == NULL) - { - UiLog().err() << " unable to parse node query: " << query_->nodeQueryPart().toStdString(); - UserMessage::message(UserMessage::ERROR,true,"Error, unable to parse node query: " + query_->nodeQueryPart().toStdString()); - return false; - } - - QStringList serverNames=query_->servers(); - if(query_->servers().isEmpty()) - serverNames=allServers; - - Q_FOREACH(QString s,serverNames) - { - if(ServerHandler* server=ServerHandler::find(s.toStdString())) - { - servers_.push_back(server); - } - } - - if(!query_->rootNode().empty()) - { - if(servers_.size() != 1) - return false; - - rootNode_=servers_.at(0)->vRoot()->find(query_->rootNode()); - if(!rootNode_) - { - UiLog().err() << " the specified root node does not exist: " << query_->rootNode(); - UserMessage::message(UserMessage::ERROR,true, - "Error, the specified root node does not exist: " + query_->rootNode()); - return false; - } - } - - //The attribute parser - UiLog().dbg() << " full attr part: " << query_->attrQueryPart().toStdString(); - - for(std::vector::const_iterator it=VAttributeType::types().begin(); - it != VAttributeType::types().end(); ++it) - { - if(query_->hasAttribute(*it)) - { - QString attrPart=(query_->attrQueryPart(*it)); - UiLog().dbg() << " " << (*it)->strName() << ": " << attrPart.toStdString(); - BaseNodeCondition* ac=NodeExpressionParser::instance()->parseWholeExpression(attrPart.toStdString(), query->caseSensitive()); - if(!ac) - { - UiLog().err() << " unable to parse attribute query: " << attrPart.toStdString(); - UserMessage::message(UserMessage::ERROR,true, "Error, unable to parse attribute query: " + attrPart.toStdString()); - return false; - } - attrParser_[*it]=ac; - } - } - - //Notify the servers that the search began - Q_FOREACH(ServerHandler* s,servers_) - { - s->searchBegan(); - } - - //Start thread execution - start(); - - UiLog().dbg() << "<-- runQuery"; - - return true; -} - -void NodeQueryEngine::stopQuery() -{ - stopIt_=true; - wait(); -} - -void NodeQueryEngine::run() -{ - if(rootNode_) - { - run(servers_.at(0),rootNode_); - } - else - { - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - { - ServerHandler *server=*it; - - run(server,server->vRoot()); - - if(stopIt_) - { - broadcastChunk(true); - return; - } - } - } - - broadcastChunk(true); -} - -void NodeQueryEngine::run(ServerHandler *server,VNode* root) -{ - runRecursively(root); -} - - -void NodeQueryEngine::runRecursively(VNode *node) -{ - if(stopIt_) - return; - - //Execute the node part - if(parser_->execute(node)) - { - //Then execute the attribute part - if(!attrParser_.isEmpty()) - { - QMap::const_iterator it = attrParser_.constBegin(); - while (it != attrParser_.constEnd()) - { - //Process a given attribute type - const std::vector& av=node->attr(); - bool hasType=false; - for(size_t i=0; i < av.size(); i++) - { - if(av[i]->type() == it.key()) - { - hasType=true; - if(it.value()->execute(av[i])) - { - broadcastFind(node,av[i]->data(true)); - scanCnt_++; - } - } - //The the attribute vector elements are grouped by type. - //So we leave the loop when we reach the next type group - else if(hasType) - { - break; - } - } - ++it; - } - - - } - else - { - broadcastFind(node); - scanCnt_++; - } - } - - for(int i=0; i < node->numOfChildren(); i++) - { - runRecursively(node->childAt(i)); - if(stopIt_) - return; - } -} - -void NodeQueryEngine::broadcastFind(VNode* node,QStringList attr) -{ - Q_ASSERT(node); - - if(!attr.isEmpty()) - { - NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node,attr)); - res_ << d; - } - else - { - NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node)); - res_ << d; - } - - broadcastChunk(false); - - cnt_++; - - if(cnt_ >= maxNum_) - { - broadcastChunk(true); - stopIt_=true; - maxReached_=true; - } -} - -void NodeQueryEngine::broadcastFind(VNode* node) -{ - Q_ASSERT(node); - - NodeQueryResultTmp_ptr d(new NodeQueryResultTmp(node)); - res_ << d; - - broadcastChunk(false); - - cnt_++; - - if(cnt_ >= maxNum_) - { - broadcastChunk(true); - stopIt_=true; - maxReached_=true; - } -} - - -void NodeQueryEngine::broadcastChunk(bool force) -{ - bool doIt=false; - if(!force) - { - if(res_.count() >= chunkSize_) - { - doIt=true; - } - } - else if(!res_.isEmpty()) - { - doIt=true; - } - - if(doIt) - { - Q_EMIT found(res_); - res_.clear(); - } -} - -void NodeQueryEngine::slotFinished() -{ - //Notify the servers that the search finished - Q_FOREACH(ServerHandler* s,servers_) - { - s->searchFinished(); - } -} - -void NodeQueryEngine::slotFailed() -{ - -} - -NodeFilterEngine::NodeFilterEngine(NodeFilter* owner) : - query_(new NodeQuery("tmp")), - parser_(NULL), - server_(NULL), - owner_(owner), - rootNode_(0) -{ -} - -NodeFilterEngine::~NodeFilterEngine() -{ - delete query_; - - if(parser_) - delete parser_; -} - -void NodeFilterEngine::setQuery(NodeQuery* query) -{ - query_->swap(query); - - if(parser_) - delete parser_; - - parser_=NodeExpressionParser::instance()->parseWholeExpression(query_->query().toStdString()); - if(parser_ == NULL) - { - UiLog().err() << "Error, unable to parse enabled condition: " << query_->query().toStdString(); - UserMessage::message(UserMessage::ERROR, true,"Error, unable to parse enabled condition: " + query_->query().toStdString()); - } -} - - -bool NodeFilterEngine::runQuery(ServerHandler* server) -{ - rootNode_=0; - - if(!query_) - return false; - - server_=server; - if(!server_) - return false; - - if(!parser_) - return false; - - if(!query_->rootNode().empty() && - (query_->servers().count() == 1 && !query_->servers()[0].simplified().isEmpty())) - { - rootNode_=server_->vRoot()->find(query_->rootNode()); - if(!rootNode_) - { - UiLog().err() << " the specified root node does not exist: " << query_->rootNode(); -#if 0 - UserMessage::message(UserMessage::ERROR,true, - "Node filter failed! The specified root node does not exist: " + query_->rootNode() + - "
        Please redefine your filter!"); -#endif - return false; - } - } - - if(rootNode_) - runRecursively(rootNode_); - else - runRecursively(server_->vRoot()); - - return true; -} - -void NodeFilterEngine::runRecursively(VNode *node) -{ - if(!node->isServer() && - (node == owner_->forceShowNode() || parser_->execute(node))) - { - owner_->match_.push_back(node); - } - - for(int i=0; i < node->numOfChildren(); i++) - { - runRecursively(node->childAt(i)); - } -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryEngine.hpp ecflow-4.11.1/Viewer/src/NodeQueryEngine.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryEngine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryEngine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,99 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERYENGINE_HPP_ -#define VIEWER_SRC_NODEQUERYENGINE_HPP_ - -#include -#include - -#include -#include -#include - -#include "NodeQueryResultTmp.hpp" - -class BaseNodeCondition; -class ServerHandler; -class VNode; -class NodeFilter; -class NodeQuery; -class NodeQueryOptions; -class VAttribute; -class VAttributeType; - -class NodeQueryEngine : public QThread -{ - -Q_OBJECT - -public: - explicit NodeQueryEngine(QObject* parent=0); - ~NodeQueryEngine(); - - bool runQuery(NodeQuery* query,QStringList allServers); - void stopQuery(); - int scannedCount() const {return scanCnt_;} - bool wasStopped() const {return stopIt_;} - bool wasMaxReached() const {return maxReached_;} - -protected Q_SLOTS: - void slotFinished(); - void slotFailed(); - -Q_SIGNALS: - void found(NodeQueryResultTmp_ptr); - void found(QList); - -protected: - void run(); - -private: - void run(ServerHandler*,VNode*); - void runRecursively(VNode *node); - void broadcastFind(VNode*); - void broadcastFind(VNode*,QStringList); - void broadcastChunk(bool); - - NodeQuery* query_; - BaseNodeCondition* parser_; - QMap attrParser_; - std::vector servers_; - int cnt_; - int scanCnt_; - int maxNum_; - int chunkSize_; - QList res_; - bool stopIt_; - bool maxReached_; - VNode* rootNode_; -}; - -class NodeFilterEngine -{ -public: - explicit NodeFilterEngine(NodeFilter*); - ~NodeFilterEngine(); - - bool runQuery(ServerHandler*); - void setQuery(NodeQuery*); - -private: - void runRecursively(VNode *node); - - NodeQuery* query_; - BaseNodeCondition* parser_; - ServerHandler* server_; - NodeFilter *owner_; - VNode* rootNode_; -}; - - -#endif /* VIEWER_SRC_NODEQUERYENGINE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryHandler.cpp ecflow-4.11.1/Viewer/src/NodeQueryHandler.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,115 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include - -#include "NodeQueryHandler.hpp" - -#include "DirectoryHandler.hpp" -#include "File.hpp" -#include "NodeQuery.hpp" - -NodeQueryHandler* NodeQueryHandler::instance_=0; - -NodeQueryHandler::NodeQueryHandler() : suffix_("query") -{ -} - -NodeQueryHandler* NodeQueryHandler::instance() -{ - if(!instance_) - { - instance_=new NodeQueryHandler(); - } - - return instance_; -} - -NodeQuery* NodeQueryHandler::add(const std::string& name) -{ - NodeQuery *item=new NodeQuery(name); - items_.push_back(item); - return item; -} - -NodeQuery* NodeQueryHandler::add(const std::string& name,const std::string& query) -{ - NodeQuery *item=new NodeQuery(name); - items_.push_back(item); - return item; -} - -void NodeQueryHandler::add(NodeQuery* item,bool saveToFile) -{ - items_.push_back(item); - if(saveToFile) - save(item); -} - - -void NodeQueryHandler::remove(const std::string&) -{ -} - -void NodeQueryHandler::remove(NodeQuery*) -{ - -} - -NodeQuery* NodeQueryHandler::find(const std::string& name) const -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name) - return *it; - } - return NULL; -} - -void NodeQueryHandler::save() -{ -} - -void NodeQueryHandler::save(NodeQuery *item) -{ - std::string f=DirectoryHandler::concatenate(dirPath_,item->name() + "." + suffix_); - VSettings vs(f); - item->save(&vs); - vs.write(); - -} - -void NodeQueryHandler::init(const std::string& dirPath) -{ - dirPath_=dirPath; - DirectoryHandler::createDir(dirPath_); - - std::vector res; - std::string pattern=".*\\." + suffix_ + "$"; - DirectoryHandler::findFiles(dirPath_,pattern,res); - - for(std::vector::const_iterator it=res.begin(); it != res.end(); ++it) - { - std::string fName=DirectoryHandler::concatenate(dirPath_,*it); - VSettings vs(fName); - vs.read(); - - std::size_t pos=(*it).find("." + suffix_); - assert(pos != std::string::npos); - - std::string name=(*it).substr(0,pos); - NodeQuery* item=add(name); - item->load(&vs); - } -} - - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryHandler.hpp ecflow-4.11.1/Viewer/src/NodeQueryHandler.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_NODEQUERYHANDLER_HPP_ -#define VIEWER_SRC_NODEQUERYHANDLER_HPP_ - -#include -#include - -class NodeQuery; - -class NodeQueryHandler -{ -public: - NodeQueryHandler(); - - NodeQuery* add(const std::string& name); - NodeQuery* add(const std::string& name,const std::string& query); - void add(NodeQuery* item,bool save); - void remove(const std::string& name); - void remove(NodeQuery*); - NodeQuery* find(const std::string& name) const; - - void save(); - void save(NodeQuery*); - void init(const std::string& dirPath); - const std::vector& items() const {return items_;} - - static NodeQueryHandler* instance(); - -protected: - static NodeQueryHandler* instance_; - - std::string dirPath_; - const std::string suffix_; - std::vector items_; -}; - - -#endif /* VIEWER_SRC_NODEQUERYHANDLER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQuery.hpp ecflow-4.11.1/Viewer/src/NodeQuery.hpp --- ecflow-4.9.0/Viewer/src/NodeQuery.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQuery.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERY_HPP_ -#define VIEWER_SRC_NODEQUERY_HPP_ - -#include -#include - -#include -#include - -#include "StringMatchMode.hpp" -#include "VSettings.hpp" - -class NodeQuery; -class NodeQueryOption; -class NodeQueryListOption; -class VAttributeType; - -class NodeQueryAttrGroup -{ -//friend class NodeQuery; - -public: - NodeQueryAttrGroup(QString name,QList types,QList options) : - name_(name), types_(types), options_(options) {} - - QString name() const {return name_;} - virtual bool hasType(VAttributeType* t) const {return types_.contains(t);} - QList options() const {return options_;} - QString query() const; - -protected: - QString name_; - QList types_; - QList options_; -}; - -class NodeQueryVarAttrGroup : public NodeQueryAttrGroup -{ -public: - NodeQueryVarAttrGroup(QString name,QList types,QList options) : - NodeQueryAttrGroup(name,types,options) {} - - bool hasType(VAttributeType*) const; -}; - -class NodeQuery -{ -friend class NodeQueryOption; - -public: - NodeQuery(const std::string& name,bool ignoreMaxNum=false); - ~NodeQuery(); - NodeQuery* clone(); - NodeQuery* clone(const std::string& name); - - void swap(const NodeQuery*); - - void setName(const std::string& name); - const std::string& name() const {return name_;} - - QString query() const; - QString sqlQuery() const {return sqlQuery_;} - QString nodeQueryPart() const; - bool hasBasicNodeQueryPart() const; - bool hasPeriodQueryPart() const; - QString attrQueryPart() const; - QString attrQueryPart(VAttributeType*) const; - bool hasAttribute(VAttributeType*) const; - QStringList attrSelection() const; - NodeQueryListOption* stateOption() const; - - void setRootNode(const std::string& rootNode) {rootNode_=rootNode;} - const std::string& rootNode() const {return rootNode_;} - void setServers(QStringList servers) {servers_=servers;} - QStringList servers() const {return servers_;} - bool hasServer(const std::string& name) const; - - void buildQueryString(); - - int maxNum() const {return maxNum_;} - void setMaxNum(int m) {maxNum_=m;} - bool ignoreMaxNum() const {return ignoreMaxNum_;} - - void setCaseSensitive(bool b) {caseSensitive_=b;} - bool caseSensitive() const {return caseSensitive_;} - - NodeQueryOption* option(QString name) const; - QMap attrGroup() {return attrGroup_;} - - void load(VSettings*); - void save(VSettings*); - -protected: - void checkDir(); - - std::string name_; - bool advanced_; - std::string rootNode_; - QStringList servers_; - bool allServers_; - QMap extQuery_; - QString sqlQuery_; - bool caseSensitive_; - int maxNum_; - bool ignoreMaxNum_; - QMap options_; - QMap attrGroup_; - static bool defaultCaseSensitive_; - static int defaultMaxNum_; -}; - -#endif /* VIEWER_SRC_NODEQUERY_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryOption.cpp ecflow-4.11.1/Viewer/src/NodeQueryOption.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryOption.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryOption.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,543 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryOption.hpp" - -#include -#include - -#include - -#include "NodeQuery.hpp" -#include "UiLog.hpp" -#include "VAttributeType.hpp" -#include "VConfig.hpp" -#include "VProperty.hpp" -#include "VSettings.hpp" - -StringMatchMode::Mode NodeQueryStringOption::defaultMatchMode_=StringMatchMode::ContainsMatch; -bool NodeQueryStringOption::defaultCaseSensitive_=false; - -#define _UI_NODEQUERY_DEBUG - -class NodeQueryOptionFactory; -static std::map* makers = 0; - -//========================================= -// Factory -//========================================= - -class NodeQueryOptionFactory -{ -public: - explicit NodeQueryOptionFactory(const std::string& t); - virtual ~NodeQueryOptionFactory() {} - - virtual NodeQueryOption* make(VProperty* p) = 0; - static NodeQueryOption* create(VProperty* p); - -private: - explicit NodeQueryOptionFactory(const NodeQueryOptionFactory&); - NodeQueryOptionFactory& operator=(const NodeQueryOptionFactory&); -}; - -template -class NodeQueryOptionMaker : public NodeQueryOptionFactory -{ - NodeQueryOption* make(VProperty* p) { return new T(p); } -public: - explicit NodeQueryOptionMaker(const std::string& t) : NodeQueryOptionFactory(t) {} -}; - -NodeQueryOptionFactory::NodeQueryOptionFactory(const std::string& type) -{ - if(makers == 0) - makers = new std::map; - - (*makers)[type] = this; -} - -NodeQueryOption* NodeQueryOptionFactory::create(VProperty *p) -{ - if(!p) - return 0; - - std::string type=p->param("type").toStdString(); - std::map::iterator j = makers->find(type); - if(j != makers->end()) - return (*j).second->make(p); - - return 0; -} - -//=============================================== -// -// NodeQueryOption -// -//=============================================== - -NodeQueryOption::NodeQueryOption(VProperty* p) : ignoreIfAny_(false) -{ - type_=p->param("type"); - name_=p->name(); - label_=p->param("label"); - if(label_.isEmpty()) - label_=name_; - - ignoreIfAny_=(p->param("ignoreIfAny") == "true")?true:false; -} - -void NodeQueryOption::build(NodeQuery* query) -{ -#ifdef _UI_NODEQUERY_DEBUG - UI_FUNCTION_LOG -#endif - - //Node query part - VProperty* prop=VConfig::instance()->find("node_query"); - Q_ASSERT(prop); -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << " load node_query"; -#endif - for(int i=0; i < prop->children().size(); i++) - { - VProperty *p=prop->children().at(i); - QString type=p->param("type"); -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << " name=" << p->strName() << " type=" << type.toStdString(); -#endif - NodeQueryOption* op=NodeQueryOptionFactory::create(p); - Q_ASSERT(op); - query->options_[op->name()]=op; - } - - //Attr query part - prop=VConfig::instance()->find("attribute_query"); - Q_ASSERT(prop); -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << " load attribute_query"; -#endif - for(int i=0; i < prop->children().size(); i++) - { - VProperty *p=prop->children().at(i); - if(p->name() == "attribute") - { - query->options_[p->name()]=NodeQueryOptionFactory::create(p); - } - else - { -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << " name=" << p->strName(); -#endif - //Q_ASSERT(def_["attribute"]->values().contains(p->name())); - - QList typeLst; - QList opLst; - - QString types=p->param("types"); - if(types.isEmpty()) - { - VAttributeType *t=VAttributeType::find(p->name().toStdString()); - Q_ASSERT(t); - typeLst << t; - } - else - { - Q_FOREACH(QString s,types.split("|")) - { - VAttributeType *t=VAttributeType::find(s.toStdString()); - Q_ASSERT(t); - typeLst << t; - } - } - - Q_ASSERT(!typeLst.empty()); - - Q_FOREACH(VProperty* ch,p->children()) - { -#ifdef _UI_NODEQUERY_DEBUG - UiLog().dbg() << " option: name=" << ch->strName() << " type=" << ch->param("type").toStdString(); -#endif - NodeQueryOption *op=NodeQueryOptionFactory::create(ch); - query->options_[ch->name()]=op; - opLst << op; - Q_ASSERT(op); - } - if(p->name() == "variable") - query->attrGroup_[p->name()] = new NodeQueryVarAttrGroup(p->name(),typeLst,opLst); - else - query->attrGroup_[p->name()] = new NodeQueryAttrGroup(p->name(),typeLst,opLst); - } - } -} - -//=============================================== -// -// NodeQueryStringOption -// -//=============================================== - -NodeQueryStringOption::NodeQueryStringOption(VProperty* p) : - NodeQueryOption(p), - matchMode_(defaultMatchMode_), - caseSensitive_(defaultCaseSensitive_) -{ -} - -QString NodeQueryStringOption::query() const -{ - QString s; - if(!value_.isEmpty()) - { - s= name_ + " " + matchOperator() + " \'" + value_ + "\'"; - } - return s; -} - -void NodeQueryStringOption::swap(const NodeQueryOption* option) -{ - const NodeQueryStringOption* op=static_cast(option); - Q_ASSERT(op); - - value_=op->value(); - matchMode_=op->matchMode(); - caseSensitive_=op->caseSensitive(); -} - -void NodeQueryStringOption::save(VSettings* vs) -{ - if(value_.isEmpty() && matchMode_.mode() == defaultMatchMode_ && - caseSensitive_ == defaultCaseSensitive_) - return; - - vs->beginGroup(name_.toStdString()); - vs->put("value",value_.toStdString()); - vs->put("matchMode",matchMode_.toInt()); - vs->putAsBool("caseSensistive",caseSensitive_); - vs->endGroup(); -} - -void NodeQueryStringOption::load(VSettings* vs) -{ - if(!vs->contains(name().toStdString())) - return; - - vs->beginGroup(name_.toStdString()); - value_=QString::fromStdString(vs->get("value",value_.toStdString())); - matchMode_.setMode(static_cast(vs->get("matchMode",matchMode_.toInt()))); - caseSensitive_=vs->getAsBool("caseSensistive",caseSensitive_); - vs->endGroup(); -} - -//=============================================== -// -// NodeQueryListOption -// -//=============================================== - -NodeQueryListOption::NodeQueryListOption(VProperty* p) : - NodeQueryOption(p) -{ - values_=p->param("values").split("|"); - valueLabels_=p->param("labels").split("|"); - if(valueLabels_.count() == 1 && valueLabels_[0].isEmpty()) - valueLabels_=values_; - - Q_ASSERT(valueLabels_.count() == values_.count()); -} - -QString NodeQueryListOption::query(QString op) const -{ - QString s; - if(!selection_.isEmpty()) - { - s=selection_.join(op); - } - return s; -} - -void NodeQueryListOption::swap(const NodeQueryOption* option) -{ - const NodeQueryListOption* op=static_cast(option); - Q_ASSERT(op); - selection_=op->selection(); -} - -void NodeQueryListOption::save(VSettings* vs) -{ - if(selection_.isEmpty()) - return; - - std::vector v; - Q_FOREACH(QString s, selection_) - v.push_back(s.toStdString()); - - vs->put(name_.toStdString(),v); -} - -void NodeQueryListOption::load(VSettings* vs) -{ - if(!vs->contains(name_.toStdString())) - return; - - std::vector v; - vs->get(name_.toStdString(),v); - - selection_.clear(); - for(std::vector::const_iterator it=v.begin(); it != v.end(); ++it) - selection_ << QString::fromStdString(*it); -} - -//=============================================== -// -// NodeQueryComboOption -// -//=============================================== - -NodeQueryComboOption::NodeQueryComboOption(VProperty* p) : - NodeQueryOption(p) -{ - values_=p->param("values").split("|"); - valueLabels_=p->param("labels").split("|"); - if(valueLabels_.count() == 1 && valueLabels_[0].isEmpty()) - valueLabels_=values_; - - Q_ASSERT(values_.count() >0); - Q_ASSERT(valueLabels_.count() == values_.count()); - - value_=p->defaultValue().toString(); - if(!values_.contains(value_)) - value_=values_[0]; -} - -QString NodeQueryComboOption::query() const -{ - QString s; - if(ignoreIfAny_ && value_ == "any") - return s; - - if(!value_.isEmpty()) - { - s= name_ + " = " + " \'" + value_ + "\'"; - } - return s; -} - -void NodeQueryComboOption::setValue(QString val) -{ - value_=val; -} - -void NodeQueryComboOption::swap(const NodeQueryOption* option) -{ - const NodeQueryComboOption* op=static_cast(option); - Q_ASSERT(op); - value_=op->value(); -} - -void NodeQueryComboOption::save(VSettings* vs) -{ - vs->put(name_.toStdString(),value().toStdString()); -} - -void NodeQueryComboOption::load(VSettings* vs) -{ - -} - -//=============================================== -// -// NodeQueryPeriodOption -// -//=============================================== - -NodeQueryPeriodOption::NodeQueryPeriodOption(VProperty* p) : - NodeQueryOption(p), - mode_(NoMode), - lastPeriod_(-1) -{ -} - -void NodeQueryPeriodOption::clear() -{ - mode_=NoMode; - lastPeriod_=-1; - lastPeriodUnits_.clear(); - fromDate_=QDateTime(); - toDate_=QDateTime(); -} - -void NodeQueryPeriodOption::setLastPeriod(int interval,QString intervalUnits) -{ - mode_=LastPeriodMode; - lastPeriod_=interval; - lastPeriodUnits_=intervalUnits; - fromDate_=QDateTime(); - toDate_=QDateTime(); -} - -void NodeQueryPeriodOption::setPeriod(QDateTime fromDate,QDateTime toDate) -{ - mode_=FixedPeriodMode; - fromDate_=fromDate; - toDate_=toDate; - lastPeriod_=-1; - lastPeriodUnits_.clear(); -} - -QString NodeQueryPeriodOption::query() const -{ - QString s; - if(mode_ == LastPeriodMode) - { - if(lastPeriod_ >=0 && lastPeriodUnits_ >=0) - { - QDateTime prev=QDateTime::currentDateTime(); - if(lastPeriodUnits_ == "minute") - prev=prev.addSecs(-60*lastPeriod_); - else if(lastPeriodUnits_ == "hour") - prev=prev.addSecs(-3600*lastPeriod_); - else if(lastPeriodUnits_ == "day") - prev=prev.addDays(-lastPeriod_); - else if(lastPeriodUnits_ == "week") - prev=prev.addDays(-7*lastPeriod_); - else if(lastPeriodUnits_ == "month") - prev=prev.addMonths(-lastPeriod_); - else if(lastPeriodUnits_ == "year") - prev=prev.addYears(-lastPeriod_); - else - return QString(); - - s=name_ + " date::>= " + prev.toString(Qt::ISODate); - } - - } - else if(mode_ == FixedPeriodMode) - { - if(fromDate_.isValid() && toDate_.isValid()) - { - s=name_ + " date::>= " + fromDate_.toString(Qt::ISODate) + " and " + - name_ + " date::<= " + toDate_.toString(Qt::ISODate); - } - } - return s; -} - -QString NodeQueryPeriodOption::sqlQuery() const -{ - QString s; - if(mode_ == LastPeriodMode) - { - if(lastPeriod_ >=0 && lastPeriodUnits_ >=0) - { - s=name_ +" >= now() -interval " + QString::number(lastPeriod_) + " " + lastPeriodUnits_; - } - - } - else if(mode_ == FixedPeriodMode) - { - if(fromDate_.isValid() && toDate_.isValid()) - { - s=name_ + " between " + fromDate_.toString(Qt::ISODate) + - " and " + toDate_.toString(Qt::ISODate); - } - } - return s; -} - -void NodeQueryPeriodOption::swap(const NodeQueryOption* option) -{ - const NodeQueryPeriodOption* op=static_cast(option); - Q_ASSERT(op); - - if(op->mode_ == LastPeriodMode) - { - setLastPeriod(op->lastPeriod_,op->lastPeriodUnits_); - } - else if(op->mode_ == FixedPeriodMode) - { - setPeriod(op->fromDate(),op->toDate()); - } - else - mode_=NoMode; -} - - -void NodeQueryPeriodOption::save(VSettings* vs) -{ - if(mode_ == NoMode) - return; - - vs->beginGroup(name_.toStdString()); - if(mode_ == LastPeriodMode) - { - vs->put("mode","last"); - vs->put("value",lastPeriod_); - vs->put("units",lastPeriodUnits_.toStdString()); - } - else if(mode_ == FixedPeriodMode) - { - vs->put("mode","fixed"); - vs->put("from",fromDate_.toString(Qt::ISODate).toStdString()); - vs->put("to",toDate_.toString(Qt::ISODate).toStdString()); - } - vs->endGroup(); -} - -void NodeQueryPeriodOption::load(VSettings* vs) -{ - if(!vs->contains(name().toStdString())) - return; - - vs->beginGroup(name_.toStdString()); - QString mode=QString::fromStdString(vs->get("mode",std::string())); - - if(mode == "last") - { - mode_=LastPeriodMode; - lastPeriod_=vs->get("value",lastPeriod_); - lastPeriodUnits_=QString::fromStdString(vs->get("units",lastPeriodUnits_.toStdString())); - - //Check period length - if(lastPeriod_ <= 0) - clear(); - - //Check period units - if(lastPeriodUnits_ != "minute" && lastPeriodUnits_ != "hour" && - lastPeriodUnits_ != "day" && lastPeriodUnits_ != "week" && - lastPeriodUnits_ != "month" && lastPeriodUnits_ != "year") - clear(); - - } - else if(mode == "fixed") - { - mode_=FixedPeriodMode; - QString from=QString::fromStdString(vs->get("from",fromDate_.toString(Qt::ISODate).toStdString())); - QString to=QString::fromStdString(vs->get("to",fromDate_.toString(Qt::ISODate).toStdString())); - - fromDate_=QDateTime::fromString(from,Qt::ISODate); - toDate_=QDateTime::fromString(from,Qt::ISODate); - - //Check if dates are valis - if(!fromDate_.isValid() || !fromDate_.isValid()) - clear(); - } - else - { - clear(); - } - - vs->endGroup(); -} - -static NodeQueryOptionMaker maker1("string"); -static NodeQueryOptionMaker maker2("list"); -static NodeQueryOptionMaker maker3("combo"); -static NodeQueryOptionMaker maker4("period"); diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryOptionEdit.cpp ecflow-4.11.1/Viewer/src/NodeQueryOptionEdit.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryOptionEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryOptionEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,481 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryOptionEdit.hpp" - -#include "ComboMulti.hpp" -#include "CustomListWidget.hpp" -#include "NodeQuery.hpp" -#include "NodeQueryOption.hpp" -#include "StringMatchCombo.hpp" -#include "ViewerUtil.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -NodeQueryOptionEdit::NodeQueryOptionEdit(QString optionId,QGridLayout* grid,QWidget* parent) : - QObject(parent), - optionId_(optionId), - initIsOn_(false), - parent_(parent), - grid_(grid) -{ - connect(this,SIGNAL(changed()), - parent_,SLOT(slotOptionEditChanged())); -} - -void NodeQueryOptionEdit::init(NodeQuery* query) -{ - init(query->option(optionId_)); -} - - -NodeQueryStringOptionEdit::NodeQueryStringOptionEdit(NodeQueryOption* option,QGridLayout* grid, - QWidget* parent,bool sameRow) : - NodeQueryOptionEdit(option->name(),grid,parent), - label_(0), - matchCb_(0), - le_(0), - option_(0) -{ - label_=new QLabel(option->label() + ":",parent_); - matchCb_=new StringMatchCombo(parent_); - matchCb_->setProperty("options","1"); - - le_=new QLineEdit(parent_); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - le_->setClearButtonEnabled(true); - //QWidgetAction *wa=new QWidgetAction(this); - //StringMatchTb* tb=new StringMatchTb(parent_); - //wa->setDefaultWidget(tb); - //le_->addAction(wa,QLineEdit::LeadingPosition); -#endif -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - le_->setPlaceholderText(tr("ANY")); -#endif - - int row=grid_->rowCount(); - if(!sameRow) - { - grid_->addWidget(label_,row,0); - grid_->addWidget(matchCb_,row,1); - grid_->addWidget(le_,row,2,1,1); - } - else - { - row=row-1; - Q_ASSERT(row >= 0); - grid_->addWidget(label_,row,3); - grid_->addWidget(matchCb_,row,4); - grid_->addWidget(le_,row,5,1,-1); - } - - connect(le_,SIGNAL(textChanged(QString)), - this,SLOT(slotEdited(QString))); - - connect(matchCb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotMatchChanged(int))); - - init(option); - Q_ASSERT(option_); -} - -void NodeQueryStringOptionEdit::init(NodeQueryOption* option) -{ - initIsOn_=true; - option_=static_cast(option); - Q_ASSERT(option_); - Q_ASSERT(optionId_ == option_->name()); - le_->setText(option_->value()); - matchCb_->setMatchMode(option_->matchMode()); - initIsOn_=false; -} - -void NodeQueryStringOptionEdit::slotEdited(QString val) -{ - if(initIsOn_) - return; - - if(option_) - { - option_->setValue(val); - Q_EMIT changed(); - } -} - -void NodeQueryStringOptionEdit::slotMatchChanged(int val) -{ - if(initIsOn_) - return; - - if(option_) - { - option_->setMatchMode(matchCb_->matchMode(val)); - Q_EMIT changed(); - } -} - -void NodeQueryStringOptionEdit::clear() -{ - le_->clear(); -} - -void NodeQueryStringOptionEdit::setVisible(bool st) -{ - label_->setVisible(st); - matchCb_->setVisible(st); - le_->setVisible(st); -} - -//=================================================================== -// -// -//================================================================== - -NodeQueryListOptionEdit::NodeQueryListOptionEdit(NodeQueryOption *option,CustomListWidget* list, - QToolButton* tb,QWidget* parent) : - NodeQueryOptionEdit(option->name(),0,parent), - list_(list), - resetTb_(tb), - option_(0) -{ - option_=static_cast(option); - Q_ASSERT(option_); - - connect(list_,SIGNAL(selectionChanged()), - this,SLOT(slotListChanged())); - - connect(resetTb_,SIGNAL(clicked()), - list_,SLOT(clearSelection())); - - list_->addItems(option_->values(),false); - resetTb_->setEnabled(list_->hasSelection()); - - init(option_); -} - -void NodeQueryListOptionEdit::init(NodeQueryOption* option) -{ - initIsOn_=true; - option_=static_cast(option); - Q_ASSERT(option_); - Q_ASSERT(option_->name() == optionId_); - list_->setSelection(option_->selection()); - initIsOn_=false; -} - -void NodeQueryListOptionEdit::clear() -{ - list_->clearSelection(); -} - -void NodeQueryListOptionEdit::slotListChanged() -{ - resetTb_->setEnabled(list_->hasSelection()); - if(initIsOn_) - return; - - if(option_) - { - option_->setSelection(list_->selection()); - Q_EMIT changed(); - } -} - -//=================================================================== -// -// NodeQueryComboOptionEdit -// -//================================================================== - -NodeQueryComboOptionEdit::NodeQueryComboOptionEdit(NodeQueryOption *option,QGridLayout* grid, QWidget* parent) : - NodeQueryOptionEdit(option->name(),grid,parent), - cb_(0), - option_(0) -{ - option_=static_cast(option); - Q_ASSERT(option_); - - label_=new QLabel(option->label() + ":",parent_); - cb_=new QComboBox(parent_); - - int row=grid_->rowCount(); - grid_->addWidget(label_,row,0); - grid_->addWidget(cb_,row,1,1,-1); - - connect(cb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotCbChanged(int))); - - QStringList vals=option_->values(); - QStringList labels=option_->valueLabels(); - for(int i=0; i < vals.count(); i++) - { - cb_->addItem(labels[i],vals[i]); - } - - init(option_); -} - -void NodeQueryComboOptionEdit::init(NodeQueryOption* option) -{ - initIsOn_=true; - option_=static_cast(option); - Q_ASSERT(option_); - Q_ASSERT(option_->name() == optionId_); - - for(int i=0; i < cb_->count(); i++) - { - if(cb_->itemData(i).toString() == option_->value()) - { - cb_->setCurrentIndex(i); - break; - } - } - initIsOn_=false; -} - -void NodeQueryComboOptionEdit::slotCbChanged(int idx) -{ - if(initIsOn_) - return; - - if(option_) - { - option_->setValue(cb_->itemData(idx).toString()); - Q_EMIT changed(); - } -} - -void NodeQueryComboOptionEdit::setVisible(bool st) -{ - label_->setVisible(st); - cb_->setVisible(st); -} - -//=================================================================== -// -// NodeQueryPeriodOptionEdit -// -//================================================================== - -NodeQueryPeriodOptionEdit::NodeQueryPeriodOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget *parent) : - NodeQueryOptionEdit(option->name(),grid,parent), - option_(0) -{ - int row=grid_->rowCount(); - - label_=new QLabel(option->label() + ": ",parent_); - - modeCb_=new QComboBox(parent); - modeCb_->addItem("any time","any"); - modeCb_->addItem("in the last","last"); - modeCb_->addItem("in period","period"); - - connect(modeCb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(modeChanged(int))); - - holder_=new QWidget(parent); - QHBoxLayout* hb=new QHBoxLayout(holder_); - hb->setContentsMargins(0,0,0,0); - - //last period - lastValueSpin_=new QSpinBox(holder_); - lastValueSpin_->setRange(1,100000); - hb->addWidget(lastValueSpin_); - - lastUnitsCb_=new QComboBox(holder_); - lastUnitsCb_->addItem(tr("minutes"),"minute"); - lastUnitsCb_->addItem(tr("hours"),"hour"); - lastUnitsCb_->addItem(tr("days"),"day"); - lastUnitsCb_->addItem(tr("weeks"),"week"); - lastUnitsCb_->addItem(tr("months"),"month"); - lastUnitsCb_->addItem(tr("years"),"year"); - hb->addWidget(lastUnitsCb_); - - connect(lastValueSpin_,SIGNAL(valueChanged(int)), - this,SLOT(lastValueChanged(int))); - - connect(lastUnitsCb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(lastUnitsChanged(int))); - - //Period: from-to - periodFromDe_=new QDateTimeEdit(holder_); - periodFromDe_->setCalendarPopup(true); - periodFromDe_->setDisplayFormat("yyyy-MM-dd hh:mm"); - periodFromDe_->setDate(QDate::currentDate()); - periodFromDe_->setMaximumDate(QDate::currentDate()); - hb->addWidget(periodFromDe_); - - periodToDe_=new QDateTimeEdit(holder_); - periodToDe_->setCalendarPopup(true); - periodToDe_->setDisplayFormat("yyyy-MM-dd hh:mm"); - periodToDe_->setDate(QDate::currentDate()); - periodToDe_->setMaximumDate(QDate::currentDate()); - periodToDe_->setTime(QTime(23,59)); - hb->addWidget(periodToDe_); - - connect(periodFromDe_,SIGNAL(dateTimeChanged(QDateTime)), - this,SLOT(slotFromChanged(QDateTime))); - - connect(periodToDe_,SIGNAL(dateTimeChanged(QDateTime)), - this,SLOT(slotToChanged(QDateTime))); - - //Add to grid - grid_->addWidget(label_,row,0); - grid_->addWidget(modeCb_,row,1); - grid_->addWidget(holder_,row,2); - - //Init - modeCb_->setCurrentIndex(0); - lastUnitsCb_->setCurrentIndex(1); //minutes - - init(option); - Q_ASSERT(option_); - - //we need to call it to have a proper init!! - modeChanged(0); -} - -void NodeQueryPeriodOptionEdit::init(NodeQueryOption* option) -{ - initIsOn_=true; - - option_=static_cast(option); - Q_ASSERT(option_); - Q_ASSERT(optionId_ == option_->name()); - if(option_->mode() == NodeQueryPeriodOption::LastPeriodMode) - { - modeCb_->setCurrentIndex(1); - lastValueSpin_->setValue(option_->lastPeriod()); - ViewerUtil::initComboBoxByData(option_->lastPeriodUnits(),lastUnitsCb_); - } - else if(option_->mode() == NodeQueryPeriodOption::FixedPeriodMode) - { - modeCb_->setCurrentIndex(2); - periodFromDe_->setDateTime(option_->fromDate()); - periodToDe_->setDateTime(option_->toDate()); - } - else - { - modeCb_->setCurrentIndex(0); - } - - initIsOn_=false; -} - -void NodeQueryPeriodOptionEdit::setVisible(bool) -{ -} - -void NodeQueryPeriodOptionEdit::modeChanged(int) -{ - int idx=modeCb_->currentIndex(); - if(idx < 0) - { - holder_->hide(); - updateOptions(); - return; - } - - QString mode=modeCb_->itemData(idx).toString(); - if(mode == "last") - { - holder_->show(); - lastValueSpin_->show(); - lastUnitsCb_->show(); - periodFromDe_->hide(); - periodToDe_->hide(); - } - else if(mode == "period") - { - holder_->show(); - lastValueSpin_->hide(); - lastUnitsCb_->hide(); - periodFromDe_->show(); - periodToDe_->show(); - } - else - { - holder_->hide(); - } - - updateOptions(); -} - -void NodeQueryPeriodOptionEdit::lastValueChanged(int) -{ - updateOptions(); -} - -void NodeQueryPeriodOptionEdit::lastUnitsChanged(int) -{ - updateOptions(); -} - -void NodeQueryPeriodOptionEdit::slotFromChanged(QDateTime) -{ - updateOptions(); -} - -void NodeQueryPeriodOptionEdit::slotToChanged(QDateTime) -{ - updateOptions(); -} - -void NodeQueryPeriodOptionEdit::updateOptions() -{ - if(initIsOn_) - return; - - if(option_) - { - int modeIdx=modeCb_->currentIndex(); - if(modeIdx < 0) - { - return; - } - QString mode=modeCb_->itemData(modeIdx).toString(); - - //last period - if(mode == "last") - { - int val=lastValueSpin_->value(); - int idx=lastUnitsCb_->currentIndex(); - QString units; - if(idx > -1) - units=lastUnitsCb_->itemData(idx).toString(); - else - val=-1; - - option_->setLastPeriod(val,units); - } - - //fixed period - else if(mode == "period") - { - option_->setPeriod(periodFromDe_->dateTime(),periodToDe_->dateTime()); - } - else - { - option_->clear(); - } - - Q_EMIT changed(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryOptionEdit.hpp ecflow-4.11.1/Viewer/src/NodeQueryOptionEdit.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryOptionEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryOptionEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,150 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef NODEQUERYOPTIONEDIT_HPP -#define NODEQUERYOPTIONEDIT_HPP - -#include -#include - -class CustomListWidget; -class NodeQuery; -class NodeQueryDef; -class NodeQueryListModel; -class NodeQueryOption; -class NodeQueryStringOption; -class NodeQueryListOption; -class NodeQueryComboOption; -class StringMatchCombo; -class NodeQueryPeriodOption; - -class QComboBox; -class QDateTimeEdit; -class QLabel; -class QGridLayout; -class QLineEdit; -class QSpinBox; -class QToolButton; -class QWidget; - -class NodeQueryOptionEdit : public QObject -{ -Q_OBJECT -public: - NodeQueryOptionEdit(QString optionId,QGridLayout* grid,QWidget *parent); - - void init(NodeQuery*); - QString optionId() const {return optionId_;} - virtual void setVisible(bool)=0; - virtual void clear() {} - -Q_SIGNALS: - void changed(); - -protected: - virtual void init(NodeQueryOption*)=0; - - QString optionId_; - bool initIsOn_; - QWidget* parent_; - QGridLayout* grid_; -}; - -class NodeQueryStringOptionEdit : public NodeQueryOptionEdit -{ -Q_OBJECT -public: - NodeQueryStringOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget* parent, bool sameRow); - void setVisible(bool); - void clear(); - -protected Q_SLOTS: - void slotEdited(QString val); - void slotMatchChanged(int val); - -protected: - void init(NodeQueryOption*); - - QLabel* label_; - StringMatchCombo* matchCb_; - QLineEdit *le_; - NodeQueryStringOption* option_; -}; - -class NodeQueryListOptionEdit : public NodeQueryOptionEdit -{ -Q_OBJECT -public: - NodeQueryListOptionEdit(NodeQueryOption* option,CustomListWidget* cl,QToolButton*,QWidget*); - void setVisible(bool) {} - void clear(); - -protected Q_SLOTS: - void slotListChanged(); - -protected: - void init(NodeQueryOption*); - - CustomListWidget* list_; - QToolButton *resetTb_; - NodeQueryListOption* option_; -}; - -class NodeQueryComboOptionEdit : public NodeQueryOptionEdit -{ -Q_OBJECT -public: - NodeQueryComboOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget*); - void setVisible(bool); - -protected Q_SLOTS: - void slotCbChanged(int); - -protected: - void init(NodeQueryOption*); - - QLabel* label_; - QComboBox* cb_; - NodeQueryComboOption* option_; -}; - -class NodeQueryPeriodOptionEdit : public NodeQueryOptionEdit -{ -Q_OBJECT -public: - NodeQueryPeriodOptionEdit(NodeQueryOption* option,QGridLayout* grid,QWidget *parent); - void setVisible(bool); - -protected: - void init(NodeQueryOption*); - -protected Q_SLOTS: - void updateOptions(); - void modeChanged(int); - void lastValueChanged(int); - void lastUnitsChanged(int); - void slotFromChanged(QDateTime); - void slotToChanged(QDateTime); - -private: - NodeQueryPeriodOption* option_; - QLabel* label_; - QComboBox* modeCb_; - QSpinBox* lastValueSpin_; - QComboBox* lastUnitsCb_; - QDateTimeEdit* periodFromDe_; - QDateTimeEdit* periodToDe_; - QWidget* holder_; -}; - - - -#endif // NODEQUERYOPTIONEDIT_HPP - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryOption.hpp ecflow-4.11.1/Viewer/src/NodeQueryOption.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryOption.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryOption.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,178 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef NODEQUERYOPTION_HPP -#define NODEQUERYOPTION_HPP - -#include -#include - -#include "StringMatchMode.hpp" - -class NodeQuery; -class NodeQueryAttributeTerm; -class VAttributeType; -class VProperty; -class VSettings; -class NodeQueryStringOption; -class NodeQueryListOption; -class NodeQueryComboOption; - -class NodeQueryOption -{ -public: - NodeQueryOption(VProperty*); - virtual ~NodeQueryOption() {} - - QString type() const {return type_;} - QString name() const {return name_;} - QString label() const {return label_;} - virtual QString valueAsString() const=0; - - virtual void swap(const NodeQueryOption*)=0; - virtual QString query() const {return QString();} - virtual QString sqlQuery() const {return query();} - virtual QString query(QString op) const {return QString();} - virtual void load(VSettings*)=0; - virtual void save(VSettings*)=0; - - virtual NodeQueryStringOption* isString() const {return NULL;} - virtual NodeQueryListOption* isList() const {return NULL;} - virtual NodeQueryComboOption* isCombo() const {return NULL;} - - static void build(NodeQuery*); - -protected: - QString type_; - QString name_; - QString label_; - bool ignoreIfAny_; - - //QStringList values_; - //QStringList valueLabels_; -}; - -class NodeQueryStringOption : public NodeQueryOption -{ -public: - NodeQueryStringOption(VProperty*); - - void swap(const NodeQueryOption*); - - QString value() const {return value_;} - QString valueAsString() const {return value();} - const StringMatchMode& matchMode() const {return matchMode_;} - QString matchOperator() const {return QString::fromStdString(matchMode_.matchOperator());} - bool caseSensitive() const {return caseSensitive_;} - - void setValue(QString s) {value_=s;} - void setMatchMode(StringMatchMode::Mode m) {matchMode_.setMode(m);} - void setMatchMode(const StringMatchMode& m) {matchMode_=m;} - void setCaseSensitive(bool b) {caseSensitive_=b;} - - QString query() const; - void load(VSettings*); - void save(VSettings*); - - NodeQueryStringOption* isString() const {return const_cast(this);} - -protected: - QString value_; - StringMatchMode matchMode_; - bool caseSensitive_; - - static StringMatchMode::Mode defaultMatchMode_; - static bool defaultCaseSensitive_; -}; - -class NodeQueryListOption : public NodeQueryOption -{ -public: - NodeQueryListOption(VProperty*); - - void swap(const NodeQueryOption*); - - QString query(QString op) const; - void load(VSettings*); - void save(VSettings*); - - QString valueAsString() const {return QString();} - QStringList values() const {return values_;} - QStringList valueLabels() const {return valueLabels_;} - void setSelection(QStringList lst) {selection_=lst;} - QStringList selection() const {return selection_;} - - NodeQueryListOption* isList() const {return const_cast(this);} - -protected: - QStringList selection_; - QStringList values_; - QStringList valueLabels_; -}; - -class NodeQueryComboOption : public NodeQueryOption -{ -public: - NodeQueryComboOption(VProperty*); - - void swap(const NodeQueryOption*); - - QString query() const; - void load(VSettings*); - void save(VSettings*); - - QStringList values() const {return values_;} - QStringList valueLabels() const {return valueLabels_;} - void setValue(QString); - QString value() const {return value_;} - QString valueAsString() const {return value();} - - NodeQueryComboOption* isCombo() const {return const_cast(this);} - -protected: - QString value_; - QStringList values_; - QStringList valueLabels_; -}; - -class NodeQueryPeriodOption : public NodeQueryOption -{ -public: - NodeQueryPeriodOption(VProperty*); - - void swap(const NodeQueryOption*); - - QString query() const; - QString sqlQuery() const; - void load(VSettings*); - void save(VSettings*); - - void clear(); - void setLastPeriod(int lastPeriod,QString lastPeriodUnits); - void setPeriod(QDateTime,QDateTime); - - enum Mode {LastPeriodMode,FixedPeriodMode,NoMode}; - Mode mode() const {return mode_;} - int lastPeriod() const {return lastPeriod_;} - QString lastPeriodUnits() const {return lastPeriodUnits_;} - QDateTime fromDate() const {return fromDate_;} - QDateTime toDate() const {return toDate_;} - QString valueAsString() const {return QString();} - -protected: - Mode mode_; - QDateTime fromDate_; - QDateTime toDate_; - int lastPeriod_; - QString lastPeriodUnits_; -}; - -#endif // NODEQUERYOPTION_HPP - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResult.cpp ecflow-4.11.1/Viewer/src/NodeQueryResult.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryResult.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResult.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,444 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryResult.hpp" - -#include - -#include "ServerHandler.hpp" -#include "VNode.hpp" - -//======================================================== -// -// NodeQueryResultItem -// -//======================================================== - -NodeQueryResultItem::NodeQueryResultItem(VNode* node) : - node_(node), - server_(NULL) -{ - if(node_) - server_=node_->server(); -} - -NodeQueryResultItem::NodeQueryResultItem(NodeQueryResultTmp_ptr d) -{ - node_=d->node_; - attr_=d->attr_; - - if(node_) - server_=node_->server(); -} - -void NodeQueryResultItem::invalidateNode() -{ - assert(node_); - path_=node_->absNodePath(); - node_=NULL; -} - -bool NodeQueryResultItem::updateNode() -{ - if(node_) - return (node_->server() != NULL); - - else - { - node_=server_->vRoot()->find(path_); - path_.clear(); - return (node_ != NULL); - } - - return false; -} - -QString NodeQueryResultItem::serverStr() const -{ - return (server_)?QString::fromStdString(server_->name()):QString(); -} - -QString NodeQueryResultItem::pathStr() const -{ - if(node_) - return QString::fromStdString(node_->absNodePath()); - - return QString::fromStdString(path_); -} - -QString NodeQueryResultItem::typeStr() const -{ - if(node_) - return QString::fromStdString(node_->nodeType()); - - return QString("???"); -} - -QString NodeQueryResultItem::stateStr() const -{ - if(node_) - return node_->stateName(); - return QString("???"); -} - -QColor NodeQueryResultItem::stateColour() const -{ - if(node_) - return node_->stateColour(); - return QColor(Qt::transparent); -} - -QString NodeQueryResultItem::stateChangeTimeAsString() const -{ - QString s; - if(node_) - node_->statusChangeTime(s); - - return s; -} - -unsigned int NodeQueryResultItem::stateChangeTime() const -{ - if(node_) - return node_->statusChangeTime(); - - return 0; -} - -QStringList NodeQueryResultItem::attr() const -{ - return attr_; -} - -void NodeQueryResultBlock::add(VNode* node,int pos) -{ - if(pos_==-1) - pos_=pos; - - cnt_++; - - if(nodes_[node].pos_==-1) - { - nodes_[node].pos_=pos; - } - nodes_[node].cnt_++; -} - -void NodeQueryResultBlock::clear() -{ - pos_=-1; - cnt_=0; - nodes_.clear(); -} - -bool NodeQueryResultBlock::find(const VNode* nc,int &pos, int &cnt) -{ - QHash::const_iterator it = nodes_.find(const_cast(nc)); - if(it != nodes_.end()) - { - pos=it.value().pos_; - cnt=it.value().cnt_; - return true; - } - - return false; -} - -//======================================================== -// -// NodeQueryResult -// -//======================================================== - -NodeQueryResult::NodeQueryResult(QObject *parent) : - QObject(parent) -{ -} - -NodeQueryResult::~NodeQueryResult() -{ - clear(); -} - -NodeQueryResultItem* NodeQueryResult::itemAt(int i) -{ - if(i >= 0 && i < static_cast(data_.size())) - return data_.at(i); - - return NULL; -} - -void NodeQueryResult::add(NodeQueryResultTmp_ptr item) -{ - ServerHandler *s=item->node_->server(); - if(!s) - return; - - attach(s); - - Q_EMIT beginAppendRow(); - - data_.push_back(new NodeQueryResultItem(item)); - blocks_[s].add(item->node_,data_.size()-1); - - Q_EMIT endAppendRow(); -} - -void NodeQueryResult::add(QList items) -{ - if(items.count() == 0) - return; - - Q_EMIT beginAppendRows(items.count()); - - for(int i=0; i < items.count(); i++) - { - VNode *node=items[i]->node_; - ServerHandler *s=node->server(); - attach(s); - data_.push_back(new NodeQueryResultItem(items[i])); - blocks_[s].add(node,data_.size()-1); - } - - Q_EMIT endAppendRows(items.count()); -} - -void NodeQueryResult::add(std::vector items) -{ - if(items.size() == 0) - return; - - //Count the needed items - int num=0; - for(unsigned int i=0; i < items.size(); i++) - { - assert(items.at(i) && items.at(i).get()); - if(items.at(i)->isServer() || items.at(i)->isNode()) - { - num++; - } - } - - Q_EMIT beginAppendRows(items.size()); - - for(unsigned int i=0; i < items.size(); i++) - { - if(items.at(i)->isServer() || items.at(i)->isNode()) - { - VNode *node=items.at(i)->node(); - ServerHandler *s=items.at(i)->server(); - attach(s); - data_.push_back(new NodeQueryResultItem(node)); - blocks_[s].add(node,data_.size()-1); - } - } - - Q_EMIT endAppendRows(items.size()); -} -void NodeQueryResult::clear() -{ - Q_EMIT beginReset(); - - for(std::map::const_iterator it=blocks_.begin(); it != blocks_.end(); it++) - { - it->first->removeServerObserver(this); - it->first->removeNodeObserver(this); - } - blocks_.clear(); - - for(std::vector::const_iterator it=data_.begin(); it != data_.end(); it++) - { - delete *it; - } - - data_.clear(); - - Q_EMIT endReset(); -} - -void NodeQueryResult::clear(ServerHandler* server) -{ - std::vector prev=data_; - data_.clear(); - - //Adjust the servers - detach(server); - - for(std::map::iterator it=blocks_.begin(); it != blocks_.end(); it++) - { - it->second.clear(); - } - - //Adjust data - for(std::vector::const_iterator it=prev.begin(); it != prev.end(); ++it) - { - ServerHandler *s=(*it)->node_->server(); - if(s == server) - { - delete *it; - } - else - { - data_.push_back(*it); - blocks_[s].add((*it)->node_,data_.size()-1); - } - } -} - -void NodeQueryResult::serverClear(ServerHandler* server) -{ - for(std::vector::const_iterator it=data_.begin(); it != data_.end(); ++it) - { - if(server == (*it)->server_) - { - (*it)->invalidateNode(); - } - } -} - -void NodeQueryResult::serverScan(ServerHandler* server) -{ - std::vector prev=data_; - data_.clear(); - - for(std::map::iterator it=blocks_.begin(); it != blocks_.end(); it++) - { - it->second.clear(); - } - - for(std::vector::const_iterator it=prev.begin(); it != prev.end(); ++it) - { - if(server == (*it)->server_) - { - if((*it)->updateNode()) - { - data_.push_back(*it); - blocks_[server].add((*it)->node_,data_.size()-1); - } - else - delete *it; - } - else - { - data_.push_back(*it); - blocks_[server].add((*it)->node_,data_.size()-1); - } - } - - if(blocks_[server].cnt_ == 0) - detach(server); -} - - -void NodeQueryResult::attach(ServerHandler *s) -{ - if(blocks_.find(s) == blocks_.end()) - { - s->addServerObserver(this); - s->addNodeObserver(this); - } -} - -void NodeQueryResult::detach(ServerHandler* s) -{ - std::map::iterator it=blocks_.find(s); - if(it != blocks_.end()) - { - blocks_.erase(it); - - s->removeServerObserver(this); - s->removeNodeObserver(this); - } -} - -/*void NodeQueryResult::detach(VNode *node) -{ - std::map::iterator it=serverCnt_.find(node->server()); - if(it != serverCnt_.end()) - { - it->second--; - if(it->second == 0) - { - detach(node->server()); - } - } -}*/ - -void NodeQueryResult::notifyServerDelete(ServerHandler* server) -{ - Q_EMIT beginReset(); - clear(server); - Q_EMIT endReset(); -} - -void NodeQueryResult::notifyBeginServerClear(ServerHandler* server) -{ - serverClear(server); -} - -void NodeQueryResult::notifyEndServerClear(ServerHandler* server) -{ -} - -void NodeQueryResult::notifyBeginServerScan(ServerHandler* server,const VServerChange&) -{ - Q_EMIT beginReset(); -} - -void NodeQueryResult::notifyEndServerScan(ServerHandler* server) -{ - serverScan(server); - Q_EMIT endReset(); -} - - -void NodeQueryResult::notifyBeginNodeChange(const VNode* node, const std::vector& aspect,const VNodeChange& vn) -{ - bool changed=false; - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - //Changes in the nodes - if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::SUSPENDED) - { - changed=true; - break; - } - } - - if(changed) - { - int pos=-1; - int cnt=0; - if(range(node,pos,cnt)) - Q_EMIT stateChanged(node,pos,cnt); - } -} - -void NodeQueryResult::notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&) -{ - -} - -//----------------------------------------------- -// Mapping -//----------------------------------------------- - -bool NodeQueryResult::range(const VNode* node,int &pos,int &cnt) -{ - ServerHandler *server=node->server(); - std::map::iterator it=blocks_.find(server); - if(it != blocks_.end()) - { - if(it->second.find(node,pos,cnt)) - return true; - } - return false; -} - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResult.hpp ecflow-4.11.1/Viewer/src/NodeQueryResult.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryResult.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResult.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERYRESULT_HPP_ -#define VIEWER_SRC_NODEQUERYRESULT_HPP_ - -#include -#include -#include -#include - -#include "NodeObserver.hpp" -#include "NodeQueryResultTmp.hpp" -#include "ServerObserver.hpp" -#include "VInfo.hpp" - -class ServerHandler; -class VNode; - -class NodeQueryResultItem -{ - friend class NodeQueryEngine; - friend class NodeQueryResult; - friend class NodeQueryResultModel; - friend class TriggerGraphModel; - -public: - NodeQueryResultItem() : node_(NULL), server_(NULL) {} - NodeQueryResultItem(VNode* node); - NodeQueryResultItem(NodeQueryResultTmp_ptr); - - void invalidateNode(); - bool updateNode(); - - QString serverStr() const; - QString pathStr() const; - QString typeStr() const; - QString stateStr() const; - QColor stateColour() const; - QString stateChangeTimeAsString() const; - unsigned int stateChangeTime() const; - QStringList attr() const; - bool hasAttribute() const {return attr_.count() > 0;} - -protected: - VNode* node_; - ServerHandler* server_; - QStringList attr_; - std::string path_; -}; - -struct Pos -{ - Pos() : pos_(-1), cnt_(0) {} - int pos_; - int cnt_; -}; - -struct NodeQueryResultBlock : public Pos -{ - NodeQueryResultBlock() : server_(0) {} - void add(VNode*,int); - void clear(); - bool find(const VNode* node,int &pos, int &cnt); - - ServerHandler* server_; - QHash nodes_; -}; - - -class NodeQueryResult : public QObject, public ServerObserver, public NodeObserver -{ - Q_OBJECT - -public: - explicit NodeQueryResult(QObject* parent=0); - ~NodeQueryResult(); - - int size() const {return data_.size();} - NodeQueryResultItem* itemAt(int i); - void clear(); - - //From ServerObserver - void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - void notifyEndServerClear(ServerHandler* server); - void notifyBeginServerScan(ServerHandler* server,const VServerChange&); - void notifyEndServerScan(ServerHandler* server); - - //From NodeObserver - void notifyBeginNodeChange(const VNode*, const std::vector&,const VNodeChange&); - void notifyEndNodeChange(const VNode*, const std::vector&,const VNodeChange&); - void add(std::vector); - - -public Q_SLOTS: - void add(NodeQueryResultTmp_ptr); - void add(QList items); - -Q_SIGNALS: - void beginAppendRow(); - void endAppendRow(); - void beginAppendRows(int); - void endAppendRows(int); - void beginRemoveRow(int); - void endRemoveRow(int); - void beginRemoveRows(int,int); - void endRemoveRows(int,int); - void beginReset(); - void endReset(); - void stateChanged(const VNode*,int,int); - -protected: - void clearData(bool hideOnly); - void clear(ServerHandler*); - void serverClear(ServerHandler*); - void serverScan(ServerHandler*); - void attach(ServerHandler*); - void detach(ServerHandler*); - bool range(const VNode*,int&,int&); - //void detach(VNode*); - - std::vector data_; - //std::map serverCnt_; - std::map blocks_; -}; - - - -#endif /* VIEWER_SRC_NODEQUERYRESULT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResultModel.cpp ecflow-4.11.1/Viewer/src/NodeQueryResultModel.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryResultModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResultModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,360 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryResultModel.hpp" - -#include "ModelColumn.hpp" -#include "NodeQueryResult.hpp" -#include "ServerHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -#include - -NodeQueryResultModel::NodeQueryResultModel(QObject *parent) : - QAbstractItemModel(parent), - columns_(0) -{ - columns_=ModelColumn::def("query_columns"); - Q_ASSERT(columns_); - - //Check the mapping between the enum and column ids - Q_ASSERT(columns_->id(ServerColumn) == "server"); - Q_ASSERT(columns_->id(PathColumn) == "path"); - Q_ASSERT(columns_->id(StatusColumn) == "status"); - Q_ASSERT(columns_->id(TypeColumn) == "type"); - Q_ASSERT(columns_->id(StatusChangeColumn) == "statusChange"); - Q_ASSERT(columns_->id(AttributeColumn) == "attribute"); - - data_=new NodeQueryResult(this); - - connect(data_,SIGNAL(beginAppendRow()), - this,SLOT(slotBeginAppendRow())); - - connect(data_,SIGNAL(endAppendRow()), - this,SLOT(slotEndAppendRow())); - - connect(data_,SIGNAL(beginAppendRows(int)), - this,SLOT(slotBeginAppendRows(int))); - - connect(data_,SIGNAL(endAppendRows(int)), - this,SLOT(slotEndAppendRows(int))); - - connect(data_,SIGNAL(beginRemoveRow(int)), - this,SLOT(slotBeginRemoveRow(int))); - - connect(data_,SIGNAL(endRemoveRow(int)), - this,SLOT(slotEndRemoveRow(int))); - - connect(data_,SIGNAL(beginRemoveRows(int,int)), - this,SLOT(slotBeginRemoveRows(int,int))); - - connect(data_,SIGNAL(endRemoveRows(int,int)), - this,SLOT(slotEndRemoveRows(int,int))); - - connect(data_,SIGNAL(beginReset()), - this,SLOT(slotBeginReset())); - - connect(data_,SIGNAL(endReset()), - this,SLOT(slotEndReset())); - - connect(data_,SIGNAL(stateChanged(const VNode*,int,int)), - this,SLOT(slotStateChanged(const VNode*,int,int))); - -} - -NodeQueryResultModel::~NodeQueryResultModel() -{ -} - - -void NodeQueryResultModel::clearData() -{ - data_->clear(); -} - -bool NodeQueryResultModel::hasData() const -{ - return data_->size() > 0; -} - -int NodeQueryResultModel::columnCount( const QModelIndex& /*parent */) const -{ - return columns_->count(); -} - -int NodeQueryResultModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return data_->size(); - } - - return 0; -} - -Qt::ItemFlags NodeQueryResultModel::flags ( const QModelIndex & index) const -{ - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -QVariant NodeQueryResultModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData() || - (role != Qt::DisplayRole && role != Qt::BackgroundRole && - role != Qt::TextAlignmentRole && role != SortRole )) - { - return QVariant(); - } - - int row=index.row(); - if(row < 0 || row >= data_->size()) - return QVariant(); - - ColumnType id=static_cast(index.column()); - NodeQueryResultItem* d=data_->itemAt(row); - - if(role == Qt::DisplayRole) - { - if(id == PathColumn) - return d->pathStr(); - else if(id == ServerColumn) - return d->serverStr(); - else if(id == TypeColumn) - return d->typeStr(); - else if(id == StatusColumn) - return d->stateStr(); - else if(id == StatusChangeColumn) - return d->stateChangeTimeAsString(); - else if(id == AttributeColumn) - return d->attr(); - - return QVariant(); - } - else if(role == Qt::BackgroundRole) - { - if(id == StatusColumn) - return d->stateColour(); - - return QVariant(); - } - else if(role == Qt::TextAlignmentRole) - { - if(id == StatusColumn || id == TypeColumn || id == StatusChangeColumn) - return Qt::AlignCenter; - - return QVariant(); - } - - else if(role == SortRole) - { - if(id == PathColumn) - return d->pathStr(); - else if(id == ServerColumn) - return d->serverStr(); - else if(id == TypeColumn) - return d->typeStr(); - else if(id == StatusColumn) - return d->stateStr(); - else if(id == StatusChangeColumn) - return d->stateChangeTime(); - else if(id == AttributeColumn) - return d->attr(); - - return QVariant(); - } - - return QVariant(); -} - -QVariant NodeQueryResultModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal) - return QAbstractItemModel::headerData( section, orient, role ); - - ColumnType id=static_cast(section); - - if(role == Qt::DisplayRole) - return columns_->label(section); - else if(role == Qt::UserRole) - return columns_->id(section); - else if(role == Qt::ToolTipRole) - return columns_->tooltip(section); - else if(role == Qt::TextAlignmentRole) - { - if(id == StatusColumn || id == TypeColumn || id == StatusChangeColumn) - return Qt::AlignCenter; - } - - return QVariant(); -} - -QModelIndex NodeQueryResultModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex NodeQueryResultModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - -VInfo_ptr NodeQueryResultModel::nodeInfo(const QModelIndex& index) -{ - //For invalid index no info is created. - if(!index.isValid()) - { - VInfo_ptr res; - return res; - } - - if(index.row() >=0 && index.row() <= data_->size()) - { - NodeQueryResultItem* d=data_->itemAt(index.row()); - - if(ServerHandler *s=d->server_) - { - if(d->node_) - { - //server - if(d->node_->isServer()) - { - return VInfoServer::create(s); - } - //node - else if(!d->hasAttribute()) - { - return VInfoNode::create(d->node_); - } - //attribute - else - { - if(VAttribute* a=d->node_->findAttribute(d->attr_)) - return VInfoAttribute::create(a); - else - return VInfoNode::create(d->node_); - } - } - } - } - - VInfo_ptr res; - return res; -} - -QModelIndex NodeQueryResultModel::infoToIndex(VInfo_ptr info) -{ - /*if(info && info.get()) - { - if(info->isServer()) - { - if(ServerHandler *s=info->server()) - { - return serverToIndex(s); - } - } - else if(VNode* n=info->node()) - { - return nodeToIndex(n); - } - }*/ - - return QModelIndex(); -} - -void NodeQueryResultModel::slotBeginAppendRow() -{ - int num=data_->size(); - Q_EMIT beginInsertRows(QModelIndex(),num,num); - - - Q_EMIT endInsertRows(); -} - -void NodeQueryResultModel::slotEndAppendRow() -{ - Q_EMIT endInsertRows(); -} - -void NodeQueryResultModel::slotBeginAppendRows(int n) -{ - if(n <= 0) - return; - - int num=data_->size(); - Q_EMIT beginInsertRows(QModelIndex(),num,num+n-1); -} - -void NodeQueryResultModel::slotEndAppendRows(int n) -{ - if(n <= 0) - return; - Q_EMIT endInsertRows(); -} - -void NodeQueryResultModel::slotBeginRemoveRow(int row) -{ - beginRemoveRows(QModelIndex(),row,row); -} - -void NodeQueryResultModel::slotEndRemoveRow(int row) -{ - endRemoveRows(); -} - -void NodeQueryResultModel::slotBeginRemoveRows(int rowStart,int rowEnd) -{ - beginRemoveRows(QModelIndex(),rowStart,rowEnd); -} - -void NodeQueryResultModel::slotEndRemoveRows(int,int) -{ - endRemoveRows(); -} - -void NodeQueryResultModel::slotBeginReset() -{ - beginResetModel(); -} - -void NodeQueryResultModel::slotEndReset() -{ - endResetModel(); -} - -void NodeQueryResultModel::slotStateChanged(const VNode*,int pos,int cnt) -{ - int col=columns_->indexOf("status"); - - if(col != -1) - { - QModelIndex fromIdx=index(pos,col); - QModelIndex toIdx=index(pos+cnt-1,col); - - Q_EMIT dataChanged(fromIdx,toIdx); - } -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResultModel.hpp ecflow-4.11.1/Viewer/src/NodeQueryResultModel.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryResultModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResultModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ -#define VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ - -#include -#include -//#include - -#include "VInfo.hpp" - -class ModelColumn; -class NodeQueryResult; - -class NodeQueryResultModel : public QAbstractItemModel -{ -Q_OBJECT - -public: - enum CustomItemRole {SortRole = Qt::UserRole + 1}; - - explicit NodeQueryResultModel(QObject *parent=0); - ~NodeQueryResultModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - NodeQueryResult *data() const {return data_;} - void clearData(); - bool hasData() const; - //void addDataStart(); - //void addDataEnd(); - - VInfo_ptr nodeInfo(const QModelIndex&); - QModelIndex infoToIndex(VInfo_ptr); - - //To speed up identifying a column. The mapping here must match the definition of - //"query_columns" in ecflowview_view_conf.json !!! - enum ColumnType {ServerColumn=0,PathColumn=1,StatusColumn=2,TypeColumn=3, - StatusChangeColumn=4,AttributeColumn=5}; - -public Q_SLOTS: - void slotBeginAppendRow(); - void slotEndAppendRow(); - void slotBeginAppendRows(int); - void slotEndAppendRows(int); - void slotBeginRemoveRow(int); - void slotEndRemoveRow(int); - void slotBeginRemoveRows(int,int); - void slotEndRemoveRows(int,int); - void slotBeginReset(); - void slotEndReset(); - void slotStateChanged(const VNode*,int,int); - -protected: - NodeQueryResult *data_; - ModelColumn* columns_; -}; - -#endif /* VIEWER_SRC_NODEQUERYRESULTMODEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResultTmp.hpp ecflow-4.11.1/Viewer/src/NodeQueryResultTmp.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryResultTmp.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResultTmp.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ -#define VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ - -#include -#include -#include - -class VNode; - -class NodeQueryResultTmp; -typedef boost::shared_ptr NodeQueryResultTmp_ptr; - -struct NodeQueryResultTmp -{ - NodeQueryResultTmp() : node_(NULL) {} - NodeQueryResultTmp(VNode *node) : node_(node) {} - NodeQueryResultTmp(VNode *node,QStringList attr) : node_(node), attr_(attr) {} - - VNode* node_; - QStringList attr_; -}; - -#endif /* VIEWER_SRC_NODEQUERYRESULTTMP_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResultView.cpp ecflow-4.11.1/Viewer/src/NodeQueryResultView.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryResultView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResultView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,276 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryResultView.hpp" - -#include -#include -#include -#include -#include -#include - -#include "ActionHandler.hpp" -#include "NodeQueryResultModel.hpp" -#include "NodeQueryViewDelegate.hpp" -#include "UserMessage.hpp" -#include "VNode.hpp" - -NodeQueryResultView::NodeQueryResultView(QWidget* parent) : - QTreeView(parent), - model_(NULL), - sortModel_(NULL), - needItemsLayout_(false) -{ - //setProperty("style","nodeView"); - setProperty("view","query"); - - actionHandler_=new ActionHandler(this,this); - - sortModel_=new QSortFilterProxyModel(this); - //sortModel_->setDynamicSortFilter(true); - setModel(sortModel_); - - //Create delegate to the view - delegate_=new NodeQueryViewDelegate(this); - setItemDelegate(delegate_); - - connect(delegate_,SIGNAL(sizeHintChangedGlobal()), - this,SLOT(slotSizeHintChangedGlobal())); - - //setRootIsDecorated(false); - setAllColumnsShowFocus(true); - setUniformRowHeights(true); - setMouseTracking(true); - setRootIsDecorated(false); - setSortingEnabled(true); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - //!!!!We need to do it because: - //The background colour between the view's left border and the nodes cannot be - //controlled by delegates or stylesheets. It always takes the QPalette::Highlight - //colour from the palette. Here we set this to transparent so that Qt could leave - //this area empty and we will fill it appropriately in our delegate. - QPalette pal=palette(); - pal.setColor(QPalette::Highlight,QColor(128,128,128,0));//Qt::transparent); - setPalette(pal); - - //Context menu - enableContextMenu(true); - - //Selection - connect(this,SIGNAL(clicked(const QModelIndex&)), - this,SLOT(slotSelectItem(const QModelIndex&))); - - connect(this,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClickItem(const QModelIndex&))); - -} - -NodeQueryResultView::~NodeQueryResultView() -{ - delete actionHandler_; -} - - -void NodeQueryResultView::enableContextMenu(bool enable) -{ - if (enable) - { - setContextMenuPolicy(Qt::CustomContextMenu); - - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - } - else - { - setContextMenuPolicy(Qt::NoContextMenu); - - disconnect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - } -} - - -void NodeQueryResultView::setSourceModel(NodeQueryResultModel *model) -{ - model_= model; - sortModel_->setSourceModel(model_); - sortModel_->setSortRole(NodeQueryResultModel::SortRole); -} - - - -//Collects the selected list of indexes -QModelIndexList NodeQueryResultView::selectedList() -{ - QModelIndexList lst; - Q_FOREACH(QModelIndex idx,selectedIndexes()) - { - if(idx.column() == 0) - lst << idx; - } - return lst; -} - -// this is called even if the user clicks outside of the node list to deselect all -void NodeQueryResultView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ - QTreeView::selectionChanged(selected, deselected); - Q_EMIT selectionChanged(); -} - -void NodeQueryResultView::slotSelectItem(const QModelIndex&) -{ - QModelIndexList lst=selectedIndexes(); - if(lst.count() > 0) - { - VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(lst.front())); - if(info) - { - Q_EMIT selectionChanged(info); - } - } -} - -VInfo_ptr NodeQueryResultView::currentSelection() -{ - QModelIndexList lst=selectedIndexes(); - if(lst.count() > 0) - { - return model_->nodeInfo(sortModel_->mapToSource(lst.front())); - } - return VInfo_ptr(); -} - -void NodeQueryResultView::currentSelection(VInfo_ptr info) -{ - /*QModelIndex idx=model_->infoToIndex(info); - if(idx.isValid()) - { - setCurrentIndex(idx); - Q_EMIT selectionChanged(info); - }*/ -} - -void NodeQueryResultView::slotSetCurrent(VInfo_ptr info) -{ - /*QModelIndex idx=model_->infoToIndex(info); - if(idx.isValid()) - { - setCurrentIndex(idx); - Q_EMIT selectionChanged(info); - }*/ -} - -void NodeQueryResultView::selectFirstServer() -{ - QModelIndex idx=sortModel_->index(0,0); - if(idx.isValid()) - { - setCurrentIndex(idx); - VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(idx)); - Q_EMIT selectionChanged(info); - } -} - - -void NodeQueryResultView::getListOfSelectedNodes(std::vector &nodeList) -{ - QModelIndexList indexList=selectedList(); - - nodeList.clear(); - for(int i=0; i < indexList.count(); i++) - { - VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(indexList[i])); - if(info && !info->isEmpty()) - nodeList.push_back(info); - } -} - - -void NodeQueryResultView::slotDoubleClickItem(const QModelIndex&) -{ -} - -void NodeQueryResultView::slotContextMenu(const QPoint &position) -{ - QModelIndexList lst=selectedList(); - //QModelIndex index=indexAt(position); - QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); - - handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); -} - - -void NodeQueryResultView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) -{ - //Node actions - if(indexClicked.isValid()) //indexLst[0].isValid() && indexLst[0].column() == 0) - { - std::vector nodeLst; - for(int i=0; i < indexLst.count(); i++) - { - VInfo_ptr info=model_->nodeInfo(sortModel_->mapToSource(indexLst[i])); - if(info && !info->isEmpty()) - nodeLst.push_back(info); - } - - actionHandler_->contextMenu(nodeLst,globalPos); - } - - //Desktop actions - else - { - } -} - -void NodeQueryResultView::slotViewCommand(std::vector nodeLst,QString cmd) -{ - - if(nodeLst.size() == 0) - return; - - /*if(cmd == "set_as_root") - { - model_->setRootNode(nodeLst.at(0)->node()); - expandAll(); - }*/ -} - -void NodeQueryResultView::reload() -{ - //model_->reload(); - //expandAll(); -} - -void NodeQueryResultView::rerender() -{ - if(needItemsLayout_) - { - doItemsLayout(); - needItemsLayout_=false; - } - else - { - viewport()->update(); - } -} - -void NodeQueryResultView::slotRerender() -{ - rerender(); -} - - -void NodeQueryResultView::slotSizeHintChangedGlobal() -{ - needItemsLayout_=true; -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryResultView.hpp ecflow-4.11.1/Viewer/src/NodeQueryResultView.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryResultView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryResultView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,70 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ -#define VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ - -#include - -#include "VInfo.hpp" - -class ActionHandler; -class NodeQueryResultModel; -class NodeQueryViewDelegate; - -class QSortFilterProxyModel; - -class NodeQueryResultView : public QTreeView -{ -Q_OBJECT - -public: - explicit NodeQueryResultView(QWidget *parent=0); - ~NodeQueryResultView(); - - void reload(); - void rerender(); - VInfo_ptr currentSelection(); - void currentSelection(VInfo_ptr n); - void selectFirstServer(); - void setSourceModel(NodeQueryResultModel* model); - void enableContextMenu(bool enable); - void getListOfSelectedNodes(std::vector &nodeList); - - //void readSettings(VSettings* vs) {}; - -public Q_SLOTS: - void slotSelectItem(const QModelIndex&); - void slotDoubleClickItem(const QModelIndex&); - void slotContextMenu(const QPoint &position); - void slotViewCommand(std::vector,QString); - void slotSetCurrent(VInfo_ptr); - void slotRerender(); - void slotSizeHintChangedGlobal(); - void selectionChanged (const QItemSelection &selected, const QItemSelection &deselected); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - void selectionChanged(); - void infoPanelCommand(VInfo_ptr,QString); - -protected: - QModelIndexList selectedList(); - void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); - - NodeQueryResultModel* model_; - QSortFilterProxyModel* sortModel_; - ActionHandler* actionHandler_; - bool needItemsLayout_; - NodeQueryViewDelegate* delegate_; -}; - - -#endif /* VIEWER_SRC_NODEQUERYRESULTVIEW_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeQuerySaveDialog.ui ecflow-4.11.1/Viewer/src/NodeQuerySaveDialog.ui --- ecflow-4.9.0/Viewer/src/NodeQuerySaveDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQuerySaveDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ - - - NodeQuerySaveDialog - - - - 0 - 0 - 279 - 77 - - - - Save query as - - - true - - - - - - QFormLayout::ExpandingFieldsGrow - - - - - &Query name: - - - label - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - NodeQuerySaveDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox - rejected() - NodeQuerySaveDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryViewDelegate.cpp ecflow-4.11.1/Viewer/src/NodeQueryViewDelegate.cpp --- ecflow-4.9.0/Viewer/src/NodeQueryViewDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeQueryViewDelegate.hpp" - -#include -#include -#include -#include -#include - -#include - -#include "AbstractNodeModel.hpp" -#include "Animation.hpp" -#include "IconProvider.hpp" -#include "ModelColumn.hpp" -#include "PropertyMapper.hpp" - -static std::vector propVec; - -//Define node renderer properties -struct QueryNodeDelegateBox : public NodeDelegateBox -{ - QueryNodeDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=3; - rightMargin=0; - topPadding=0; - bottomPadding=0; - leftPadding=2; - rightPadding=1; - } -}; - -//Define attribute renderer properties -struct QueryAttrDelegateBox : public AttrDelegateBox -{ - QueryAttrDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=1; - rightMargin=0; - topPadding=0; - bottomPadding=0; - leftPadding=0; - rightPadding=0; - } -}; - -NodeQueryViewDelegate::NodeQueryViewDelegate(QWidget *parent) -{ - nodeBox_=new QueryNodeDelegateBox; - attrBox_=new QueryAttrDelegateBox; - - nodeBox_->adjust(font_); - attrBox_->adjust(attrFont_); - - borderPen_=QPen(QColor(230,230,230)); - - columns_=ModelColumn::def("query_columns"); - - //Property - if(propVec.empty()) - { - //Base settings - addBaseSettings(propVec); - } - - prop_=new PropertyMapper(propVec,this); - - updateSettings(); -} - -NodeQueryViewDelegate::~NodeQueryViewDelegate() -{ -} - -void NodeQueryViewDelegate::updateSettings() -{ - //Update the settings handled by the base class - updateBaseSettings(); -} - -QSize NodeQueryViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const -{ - QSize size=QStyledItemDelegate::sizeHint(option,index); - return QSize(size.width(),nodeBox_->sizeHintCache.height()); -} - -void NodeQueryViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ - //Background -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QStyleOptionViewItem vopt(option); -#else - QStyleOptionViewItemV4 vopt(option); -#endif - - initStyleOption(&vopt, index); - - const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); - const QWidget* widget = vopt.widget; - - //Save painter state - painter->save(); - - QString id=columns_->id(index.column()); - - if(id == "path") - { - QString text=index.data(Qt::DisplayRole).toString(); - renderNode(painter,index,vopt,text); - } - else if(id == "status") - { - renderStatus(painter,index,vopt); - } - - //Render attributes - else if(id == "attribute") - { - QVariant va=index.data(Qt::DisplayRole); - if(va.type() == QVariant::StringList) - { - QStringList lst=va.toStringList(); - if(lst.count() > 0) - { - QMap::const_iterator it=attrRenderers_.find(lst.at(0)); - if(it != attrRenderers_.end()) - { - QSize size; - AttributeRendererProc a=it.value(); - (this->*a)(painter,lst,vopt,size); - } - } - } - } - - //rest of the columns - else - { - QString text=index.data(Qt::DisplayRole).toString(); - QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); - painter->setPen(Qt::black); - - int rightPos=textRect.right()+1; - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - /*QVariant alg=index.data(Qt::TextAlignmentRole); - if(alg.isValid()) - painter->drawText(textRect,alg.value(),text); - else*/ - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - - if(setClipRect) - { - painter->restore(); - } - - } - - //Render the horizontal border for rows. We only render the top border line. - //With this technique we miss the bottom border line of the last row!!! - //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); - QRect bgRect=option.rect; - painter->setPen(borderPen_); - painter->drawLine(bgRect.topLeft(),bgRect.topRight()); - - painter->restore(); -} - -void NodeQueryViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const -{ - bool selected=option.state & QStyle::State_Selected; - QFontMetrics fm(font_); - - QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); - - //The text rectangle - QRect textRect = itemRect; - - int textWidth=fm.width(text); - textRect.setWidth(textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding); - - int currentRight=textRect.x()+textRect.width(); - - QList pixLst; - QList pixRectLst; - - QVariant va=index.data(AbstractNodeModel::IconRole); - if(va.type() == QVariant::List) - { - QVariantList lst=va.toList(); - if(lst.count() >0) - { - int xp=currentRight+nodeBox_->iconPreGap; - int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; - for(int i=0; i < lst.count(); i++) - { - int id=lst[i].toInt(); - if(id != -1) - { - pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); - pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); - xp+=nodeBox_->iconSize+nodeBox_->iconGap; - } - } - - if(!pixLst.isEmpty()) - { - currentRight=xp-nodeBox_->iconGap; - } - } - } - - //Define clipping - int rightPos=currentRight+1; - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw text - QColor fg=index.data(Qt::ForegroundRole).value(); - painter->setPen(fg); - painter->drawText(textRect,Qt::AlignHCenter | Qt::AlignVCenter,text); - - if(selected) - { - QRect sr=textRect; - sr.setX(option.rect.x()+nodeBox_->leftMargin); - renderSelectionRect(painter,sr); - } - - //Draw icons - for(int i=0; i < pixLst.count(); i++) - { - painter->drawPixmap(pixRectLst[i],pixLst[i]); - } - - if(setClipRect) - { - painter->restore(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/NodeQueryViewDelegate.hpp ecflow-4.11.1/Viewer/src/NodeQueryViewDelegate.hpp --- ecflow-4.9.0/Viewer/src/NodeQueryViewDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeQueryViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ -#define VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ - -#include -#include -#include -#include -#include - -#include "NodeViewDelegate.hpp" -#include "VProperty.hpp" - -#include - -class ModelColumn; - -class NodeQueryViewDelegate : public NodeViewDelegate -{ -public: - explicit NodeQueryViewDelegate(QWidget *parent=0); - ~NodeQueryViewDelegate(); - - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - -protected: - void updateSettings(); - - void renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const; - - ModelColumn* columns_; - QPen borderPen_; - -}; - -#endif /* VIEWER_SRC_NODEQUERYVIEWDELEGATE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchDialog.cpp ecflow-4.11.1/Viewer/src/NodeSearchDialog.cpp --- ecflow-4.9.0/Viewer/src/NodeSearchDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include - -#include "NodeSearchDialog.hpp" -#include "SessionHandler.hpp" -#include "VConfig.hpp" -#include "WidgetNameProvider.hpp" - -NodeSearchDialog::NodeSearchDialog(QWidget *parent) : - QDialog(parent) -{ - setupUi(this); - - setAttribute(Qt::WA_DeleteOnClose); - - QString wt=windowTitle(); - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - connect(queryWidget_,SIGNAL(closeClicked()), - this,SLOT(accept())); - - //Read the qt settings - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -NodeSearchDialog::~NodeSearchDialog() -{ -} - -NodeSearchWidget* NodeSearchDialog::queryWidget() const -{ - return queryWidget_; -} - -void NodeSearchDialog::closeEvent(QCloseEvent * event) -{ - queryWidget_->slotStop(); //The search thread might be running!! - event->accept(); - writeSettings(); -} - -void NodeSearchDialog::accept() -{ - writeSettings(); - QDialog::accept(); -} - -void NodeSearchDialog::reject() -{ - writeSettings(); - QDialog::reject(); -} - -void NodeSearchDialog::slotOwnerDelete() -{ - deleteLater(); -} - -//------------------------------------------ -// Settings read/write -//------------------------------------------ - -void NodeSearchDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeSearchDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - queryWidget_->writeSettings(settings); - settings.endGroup(); -} - -void NodeSearchDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("NodeSearchDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(550,540)); - } - - queryWidget_->readSettings(settings); - - /*if(settings.contains("current")) - { - int current=settings.value("current").toInt(); - if(current >=0) - list_->setCurrentRow(current); - }*/ - settings.endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchDialog.hpp ecflow-4.11.1/Viewer/src/NodeSearchDialog.hpp --- ecflow-4.9.0/Viewer/src/NodeSearchDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef NODESEARCHDIALOG_HPP_ -#define NODESEARCHDIALOG_HPP_ - -#include - -#include "ServerFilter.hpp" - -#include "ui_NodeSearchDialog.h" - -class ServerFilter; - -class NodeSearchDialog : public QDialog, protected Ui::NodeSearchDialog -{ - Q_OBJECT - -public: - explicit NodeSearchDialog(QWidget *parent = 0); - ~NodeSearchDialog(); - - NodeSearchWidget* queryWidget() const; - -protected Q_SLOTS: - void accept(); - void reject(); - void slotOwnerDelete(); - -protected: - void closeEvent(QCloseEvent * event); - -private: - void readSettings(); - void writeSettings(); -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchDialog.ui ecflow-4.11.1/Viewer/src/NodeSearchDialog.ui --- ecflow-4.9.0/Viewer/src/NodeSearchDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ - - - NodeSearchDialog - - - - 0 - 0 - 514 - 544 - - - - Query - - - - 4 - - - - - - - - - NodeSearchWidget - QWidget -
        NodeSearchWidget.hpp
        - 1 -
        -
        - - - - -
        diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchWidget.cpp ecflow-4.11.1/Viewer/src/NodeSearchWidget.cpp --- ecflow-4.9.0/Viewer/src/NodeSearchWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,356 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "NodeSearchWidget.hpp" - -#include "ComboMulti.hpp" -#include "CustomListWidget.hpp" -#include "Highlighter.hpp" -#include "NodeQuery.hpp" -#include "NodeQueryEngine.hpp" -#include "NodeQueryHandler.hpp" -#include "NodeQueryHandler.hpp" -#include "NodeQueryResult.hpp" -#include "NodeQueryResultModel.hpp" -#include "ServerFilter.hpp" -#include "UiLog.hpp" -#include "VNState.hpp" - -#include -#include -#include -#include -#include - -#define _UI_NODESEARCHWIDGET_DEBUG - -//====================================================== -// -// NodeQueryWidget -// -//====================================================== - -NodeSearchWidget::NodeSearchWidget(QWidget *parent) : - QWidget(parent), - query_(NULL), - columnsAdjusted_(false) -{ - setupUi(this); - - //-------------------- - // Show/hide - //-------------------- - - //Show hide def panel - connect(defPanelTb_,SIGNAL(toggled(bool)), - this,SLOT(slotShowDefPanel(bool))); - - editor_->show(); - - //connect(editor_,SIGNAL(queryEnabledChanged(bool)), - // this,SLOT(slotQueryEnabledChanged(bool))); - - //Show hide query - connect(queryPanelTb_,SIGNAL(toggled(bool)), - this,SLOT(slotShowQueryPanel(bool))); - - QFont showf; - showf.setBold(true); - showf.setPointSize(showf.pointSize()-1); - showLabel_->setFont(showf); - showLabel_->setText("Show:"); - - //Find button - findPb_->setProperty("startSearch","1"); - QPalette pal=findPb_->palette(); - QColor col(230,245,253); - pal.setColor(QPalette::Button,col); - findPb_->setPalette(pal); - - connect(findPb_,SIGNAL(clicked()), - this,SLOT(slotFind())); - - connect(stopPb_,SIGNAL(clicked()), - this,SLOT(slotStop())); - - //Clear - connect(clearPb_,SIGNAL(clicked()), - editor_,SLOT(slotClear())); - - //Close button - connect(closePb_,SIGNAL(clicked()), - this,SLOT(slotClose())); - - //-------------------------------- - // Result tree/model - //-------------------------------- - - model_=new NodeQueryResultModel(this); - resTree_->setSourceModel(model_); - - connect(resTree_,SIGNAL(selectionChanged(VInfo_ptr)), - this,SIGNAL(selectionChanged(VInfo_ptr))); - - connect(resTree_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); - - //-------------------------------- - // Query - //-------------------------------- - - query_=new NodeQuery("tmp"); - - //-------------------- - // Query engine - //-------------------- - - engine_=new NodeQueryEngine(this); - - connect(engine_,SIGNAL(found(QList)), - model_->data(), SLOT(add(QList))); - - connect(engine_,SIGNAL(found(NodeQueryResultTmp_ptr)), - model_->data(),SLOT(add(NodeQueryResultTmp_ptr))); - - connect(engine_,SIGNAL(started()), - this,SLOT(slotQueryStarted())); - - connect(engine_,SIGNAL(finished()), - this,SLOT(slotQueryFinished())); - - //------------------- - // Progress - //------------------- - - queryProgress_->hide(); - - stopPb_->setEnabled(false); -} - -NodeSearchWidget::~NodeSearchWidget() -{ - delete query_; -} - -void NodeSearchWidget::setServerFilter(ServerFilter* f) -{ - editor_->setServerFilter(f); -} - -void NodeSearchWidget::setRootNode(VInfo_ptr info) -{ - editor_->setRootNode(info); -} - -void NodeSearchWidget::slotShowDefPanel(bool b) -{ - editor_->showDefPanel(b); -} - -void NodeSearchWidget::slotShowQueryPanel(bool b) -{ - editor_->showQueryPanel(b); -} - -void NodeSearchWidget::slotQueryEnabledChanged(bool queryEnabled) -{ - //if(!engine_->isRunning()) - //{ -#if 0 - UiLog().dbg() << "NodeSearchWidget::slotQueryEnabledChanged -->" << std::string((queryEnabled?"true":"false")); - findPb_->setEnabled(queryEnabled); -#endif - //} -} - -void NodeSearchWidget::slotFind() -{ -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << "NodeSearchWidget::slotFind -->"; -#endif - - //Avoid double clicking - if(!findPb_->isEnabled()) - { - UiLog().dbg() << "<-- NodeSearchWidget::slotFind - search is already running"; - return; - } - -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << " isRunning=" << engine_->isRunning(); -#endif - - adjustColumns(); - - //Clear the results - model_->clearData(); - - assert(!engine_->isRunning()); - assert(findPb_->isEnabled()); - assert(!stopPb_->isEnabled()); - - //We set the button state in advance as if the engine were running - adjustButtonState(true); - - elapsed_.start(); - if(!engine_->runQuery(editor_->query(),editor_->allServers())) - { - elapsed_=QTime(); - - //if we are here we could not start the query and we need to reset the button state - adjustButtonState(); - } -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << "<-- NodeSearchWidget::slotFind"; -#endif -} - -void NodeSearchWidget::slotStop() -{ - //It is a blocking call! - engine_->stopQuery(); - assert(!engine_->isRunning()); - adjustButtonState(); -} - -void NodeSearchWidget::slotClose() -{ - slotStop(); - Q_EMIT closeClicked(); -} - -void NodeSearchWidget::slotQueryStarted() -{ -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << "NodeSearchWidget::slotQueryStarted -->"; -#endif - adjustButtonState(); - - queryProgress_->setRange(0,0); - queryProgress_->show(); - - progressLabel_->setText("Search in progress ..."); -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << "<-- slotQueryStarted"; -#endif -} - -void NodeSearchWidget::slotQueryFinished() -{ -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << "NodeSearchWidget::slotQueryFinished -->"; - UiLog().dbg() << " Search finished. Total node scanned: " << engine_->scannedCount(); -#endif - - adjustButtonState(); - - queryProgress_->hide(); - queryProgress_->setRange(0,1); - queryProgress_->setValue(1); - - QString s="" + QString::number(model_->rowCount()) + " items found in " + - QString::number(elapsed_.elapsed()*0.001,'f',1) + " s"; - - QColor col(90,92,92); - if(engine_->wasMaxReached()) - { - s+=" (stopped due to maxnum reached!)"; - } - else if(engine_->wasStopped()) - { - s+=" (query was interrupted!)"; - } - progressLabel_->setText(s); - elapsed_=QTime(); - -#ifdef _UI_NODESEARCHWIDGET_DEBUG - UiLog().dbg() << " isRunning=" << engine_->isRunning(); - UiLog().dbg() << "<-- NodeSearchWidget::slotQueryFinished"; -#endif -} - - -void NodeSearchWidget::adjustButtonState() -{ - adjustButtonState(engine_->isRunning()); -} - -void NodeSearchWidget::adjustButtonState(bool engineRunning) -{ - if(engineRunning) - { - findPb_->setEnabled(false); - stopPb_->setEnabled(true); - editor_->setEnabled(false); - } - else - { - findPb_->setEnabled(true); - stopPb_->setEnabled(false); - editor_->setEnabled(true); - } - - UiLog().dbg() << "NodeSearchWidget::adjustButtonState -->"; - UiLog().dbg() << " findTb_: " << findPb_->isEnabled(); - UiLog().dbg() << "<-- adjustButtonState"; -} - -void NodeSearchWidget::adjustColumns() -{ - if(!columnsAdjusted_) - { - columnsAdjusted_=true; - - //We preset the column width. Setting it dynamically can be expensive - //for a large number of rows (> 1M) - QFont f; - QFontMetrics fm(f); - resTree_->setColumnWidth(0,fm.width("serverserverserse")); - resTree_->setColumnWidth(1,fm.width("/suite/family1/family2/longtaskname1")); - resTree_->setColumnWidth(2,fm.width("suspendedAA")); - resTree_->setColumnWidth(3,fm.width("familyAA")); - resTree_->setColumnWidth(4,fm.width("2017-Mar-07 15:45:56AA")); - } -} - -void NodeSearchWidget::writeSettings(QSettings &settings) -{ - settings.setValue("defPanel",editor_->isDefPanelVisible()); - settings.setValue("queryPanel",editor_->isQueryPanelVisible()); - QStringList colW; - for(int i=0; i < resTree_->model()->columnCount()-1; i++) - colW << QString::number(resTree_->columnWidth(i)); - - settings.setValue("resColumnWidth",colW); -} - -void NodeSearchWidget::readSettings(const QSettings &settings) -{ - if(settings.contains("defPanel")) - { - defPanelTb_->setChecked(settings.value("defPanel").toBool()); - } - if(settings.contains("queryPanel")) - { - queryPanelTb_->setChecked(settings.value("queryPanel").toBool()); - } - if(settings.contains("resColumnWidth")) - { - QStringList lst=settings.value("resColumnWidth").toStringList(); - for(int i=0; i < lst.count(); i++) - resTree_->setColumnWidth(i,lst[i].toInt()); - - if(lst.count() >= 4) - columnsAdjusted_=true; - } -} - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchWidget.hpp ecflow-4.11.1/Viewer/src/NodeSearchWidget.hpp --- ecflow-4.9.0/Viewer/src/NodeSearchWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_NODESEARCHWIDGET_HPP_ -#define VIEWER_SRC_NODESEARCHWIDGET_HPP_ - -#include -#include -#include -#include -#include - -#include "ServerFilter.hpp" -#include "VInfo.hpp" - -#include "ui_NodeSearchWidget.h" - -class NodeQuery; -class NodeQueryEngine; -class NodeQueryResultModel; - -class NodeSearchWidget : public QWidget, protected Ui::NodeSearchWidget -{ - Q_OBJECT - -public: - explicit NodeSearchWidget(QWidget *parent = 0); - ~NodeSearchWidget(); - - void setServerFilter(ServerFilter*); - void setRootNode(VInfo_ptr); - - void writeSettings(QSettings &settings); - void readSettings(const QSettings &settings); - -public Q_SLOTS: - void slotStop(); - -protected Q_SLOTS: - void slotShowDefPanel(bool); - void slotShowQueryPanel(bool); - void slotFind(); - void slotClose(); - void slotQueryStarted(); - void slotQueryFinished(); - void slotQueryEnabledChanged(bool queryEnabled); - -Q_SIGNALS: - void closeClicked(); - void selectionChanged(VInfo_ptr); - void infoPanelCommand(VInfo_ptr,QString); - -private: - void adjustColumns(); - void adjustButtonState(); - void adjustButtonState(bool); - - NodeQuery* query_; - NodeQueryEngine* engine_; - NodeQueryResultModel* model_; - bool columnsAdjusted_; - QTime elapsed_; -}; - -#endif /* VIEWER_SRC_NODESEARCHWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/NodeSearchWidget.ui ecflow-4.11.1/Viewer/src/NodeSearchWidget.ui --- ecflow-4.9.0/Viewer/src/NodeSearchWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeSearchWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,213 +0,0 @@ - - - NodeSearchWidget - - - - 0 - 0 - 1564 - 1113 - - - - Form - - - - - - - - - - - - 4 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - 24 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - - - - - - Search - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - - - - - Stop - - - - - - - Qt::Horizontal - - - - - - - Clear the node and attribute query options - - - Clear options - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Show: - - - - - - - - 0 - 0 - - - - Editor - - - true - - - false - - - - - - - - 0 - 0 - - - - Query - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Close - - - - - - - - - - NodeQueryResultView - QTreeView -
        NodeQueryResultView.hpp
        -
        - - NodeQueryEditor - QWidget -
        NodeQueryEditor.hpp
        - 1 -
        -
        - - -
        diff -Nru ecflow-4.9.0/Viewer/src/NodeViewBase.cpp ecflow-4.11.1/Viewer/src/NodeViewBase.cpp --- ecflow-4.9.0/Viewer/src/NodeViewBase.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeViewBase.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "NodeViewBase.hpp" - -NodeViewBase::NodeViewBase(NodeFilterDef* filterDef) : filterDef_(filterDef) -{ - -} diff -Nru ecflow-4.9.0/Viewer/src/NodeViewBase.hpp ecflow-4.11.1/Viewer/src/NodeViewBase.hpp --- ecflow-4.9.0/Viewer/src/NodeViewBase.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeViewBase.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef NODEVIEWBASE_HPP_ -#define NODEVIEWBASE_HPP_ - -#include "Viewer.hpp" -#include "VInfo.hpp" - -class QWidget; -class QObject; - -class TableNodeSortModel; -class NodeFilterDef; -class VSettings; -class QModelIndex; - -class NodeViewBase -{ -public: - explicit NodeViewBase(NodeFilterDef*); - virtual ~NodeViewBase(){} - - virtual void reload()=0; - virtual void rerender()=0; - virtual QWidget* realWidget()=0; - virtual QObject* realObject()=0; - virtual VInfo_ptr currentSelection()=0; - virtual void selectFirstServer()=0; - virtual void setCurrentSelection(VInfo_ptr n)=0; - - virtual void readSettings(VSettings* vs)=0; - virtual void writeSettings(VSettings* vs)=0; - -protected: - NodeFilterDef* filterDef_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/NodeViewDelegate.cpp ecflow-4.11.1/Viewer/src/NodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/src/NodeViewDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1419 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "NodeViewDelegate.hpp" - -#include -#include -#include -#include - -#include "AbstractNodeModel.hpp" -#include "PropertyMapper.hpp" -#include "UiLog.hpp" - -int NodeViewDelegate::lighter_=150; - -static std::vector propVec; - -NodeViewDelegate::NodeViewDelegate(QWidget *parent) : - QStyledItemDelegate(parent), - prop_(0), - nodeBox_(0), - attrBox_(0), - useStateGrad_(true), - drawAttrSelectionRect_(false), - limitShape_(RectLimitShape) -{ - hoverPen_=QPen(QColor(201,201,201)); - hoverBrush_=QBrush(QColor(250,250,250,210)); - selectPen_=QPen(QColor(125,162,206)); - selectBrush_=QBrush(QColor(193,220,252,110)); - nodePen_=QPen(QColor(180,180,180)); - nodeSelectPen_=QPen(QColor(0,0,0),2); - - lostConnectBgBrush_=QBrush(QColor(150,150,150,150),Qt::Dense7Pattern); - lostConnectBandBrush_=QBrush(QColor(255,166,0,150)); - - QImageReader imgR(":/viewer/warning.svg"); - if(imgR.canRead()) - { - QFont font; - QFontMetrics fm(font); - int size=fm.height()+2; - imgR.setScaledSize(QSize(size,size)); - QImage img=imgR.read(); - errPix_=QPixmap(QPixmap::fromImage(img)); - } - - grad_.setCoordinateMode(QGradient::ObjectBoundingMode); - grad_.setStart(0,0); - grad_.setFinalStop(0,1); - - eventFillBrush_=QBrush(QColor(0,0,255)); - eventFillBrush_=QBrush(QColor(240,240,240)); - meterFillBrush_=QBrush(QColor(0,0,255)); - meterThresholdBrush_=QBrush(QColor(0,0,255)); - limitFillBrush_=QBrush(QColor(0,255,0)); - limitExtraFillBrush_=QBrush(QColor(0,0,255)); - triggerBgBrush_=QBrush(QColor(230,230,230)); - triggerBorderPen_=QPen(QColor(150,150,150)); - triggerFontPen_=QPen(QColor(0,0,0)); - completeBgBrush_=QBrush(QColor(230,230,230)); - completeBorderPen_=QPen(QColor(150,150,150)); - completeFontPen_=QPen(QColor(0,0,255)); - - attrRenderers_["meter"]=&NodeViewDelegate::renderMeter; - attrRenderers_["label"]=&NodeViewDelegate::renderLabel; - attrRenderers_["event"]=&NodeViewDelegate::renderEvent; - attrRenderers_["var"]=&NodeViewDelegate::renderVar; - attrRenderers_["genvar"]=&NodeViewDelegate::renderGenvar; - attrRenderers_["limit"]=&NodeViewDelegate::renderLimit; - attrRenderers_["limiter"]=&NodeViewDelegate::renderLimiter; - attrRenderers_["trigger"]=&NodeViewDelegate::renderTrigger; - attrRenderers_["time"]=&NodeViewDelegate::renderTime; - attrRenderers_["date"]=&NodeViewDelegate::renderDate; - attrRenderers_["repeat"]=&NodeViewDelegate::renderRepeat; - attrRenderers_["late"]=&NodeViewDelegate::renderLate; -} - -NodeViewDelegate::~NodeViewDelegate() -{ - Q_ASSERT(nodeBox_); - delete nodeBox_; - - Q_ASSERT(attrBox_); - delete attrBox_; - - if(prop_) - delete prop_; -} - -void NodeViewDelegate::notifyChange(VProperty* p) -{ - updateSettings(); -} - -void NodeViewDelegate::addBaseSettings(std::vector& propVec) -{ - propVec.push_back("view.common.node_gradient"); - propVec.push_back("view.attribute.eventFillColour"); - propVec.push_back("view.attribute.meterFillColour"); - propVec.push_back("view.attribute.meterThresholdColour"); - propVec.push_back("view.attribute.limitShape"); - propVec.push_back("view.attribute.limitFillColour"); - propVec.push_back("view.attribute.limitExtraFillColour"); - propVec.push_back("view.attribute.triggerBackground"); - propVec.push_back("view.attribute.triggerBorderColour"); - propVec.push_back("view.attribute.triggerFontColour"); - propVec.push_back("view.attribute.completeBackground"); - propVec.push_back("view.attribute.completeBorderColour"); - propVec.push_back("view.attribute.completeFontColour"); -} - -void NodeViewDelegate::updateBaseSettings() -{ - if(VProperty* p=prop_->find("view.common.node_gradient")) - { - useStateGrad_=p->value().toBool(); - } - if(VProperty* p=prop_->find("view.attribute.eventFillColour")) - { - eventFillBrush_=QBrush(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.meterFillColour")) - { - QLinearGradient gr; - gr.setCoordinateMode(QGradient::ObjectBoundingMode); - gr.setStart(0,0); - gr.setFinalStop(0,1); - QColor c1=p->value().value(); - gr.setColorAt(0,c1); - gr.setColorAt(1,c1.lighter(110)); - meterFillBrush_=QBrush(gr); - } - if(VProperty* p=prop_->find("view.attribute.meterThresholdColour")) - { - QLinearGradient gr; - gr.setCoordinateMode(QGradient::ObjectBoundingMode); - gr.setStart(0,0); - gr.setFinalStop(0,1); - QColor c1=p->value().value(); - gr.setColorAt(0,c1); - gr.setColorAt(1,c1.lighter(110)); - meterThresholdBrush_=QBrush(gr); - } - if(VProperty* p=prop_->find("view.attribute.limitShape")) - { - QString shape=p->value().toString(); - if(shape == "rectangle") - limitShape_=RectLimitShape; - else if(shape == "circle") - limitShape_=CircleLimitShape; - else - limitShape_=CircleLimitShape; - } - if(VProperty* p=prop_->find("view.attribute.limitFillColour")) - { - limitFillBrush_=QBrush(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.limitExtraFillColour")) - { - limitExtraFillBrush_=QBrush(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.triggerBackground")) - { - triggerBgBrush_=QBrush(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.triggerBorderColour")) - { - triggerBorderPen_=QPen(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.triggerFontColour")) - { - triggerFontPen_=QPen(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.completeBackground")) - { - completeBgBrush_=QBrush(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.completeBorderColour")) - { - completeBorderPen_=QPen(p->value().value()); - } - if(VProperty* p=prop_->find("view.attribute.completeFontColour")) - { - completeFontPen_=QPen(p->value().value()); - } - - //limit pixmaps - if(limitShape_ == CircleLimitShape) - { - QFontMetrics fm(attrFont_); - int itemSize=static_cast(static_cast(fm.ascent())*0.8); - - QImage img(itemSize,itemSize,QImage::Format_ARGB32_Premultiplied); - img.fill(Qt::transparent); - QPainter painter(&img); - painter.setRenderHint(QPainter::Antialiasing,true); - - painter.setPen(Qt::NoPen); - painter.setBrush(QColor(70,70,70)); - painter.drawEllipse(1,1,itemSize-1,itemSize-1); - painter.setBrush(limitFillBrush_); - painter.drawEllipse(1,1,itemSize-2,itemSize-2); - limitFillPix_=QPixmap::fromImage(img); - - painter.fillRect(QRect(QPoint(0,0),img.size()),Qt::transparent); - painter.setBrush(QColor(70,70,70)); - painter.drawEllipse(1,1,itemSize-1,itemSize-1); - painter.setBrush(limitExtraFillBrush_); - painter.drawEllipse(1,1,itemSize-2,itemSize-2); - limitExtraFillPix_=QPixmap::fromImage(img); - - painter.fillRect(QRect(QPoint(0,0),img.size()),Qt::transparent); - painter.setBrush(QColor(130,130,130)); - painter.drawEllipse(1,1,itemSize-1,itemSize-1); - painter.setBrush(QColor(240,240,240)); - painter.drawEllipse(1,1,itemSize-2,itemSize-2); - limitEmptyPix_=QPixmap::fromImage(img); - } -} - -void NodeViewDelegate::renderSelectionRect(QPainter* painter,QRect r) const -{ - painter->setPen(nodeSelectPen_); - painter->setBrush(Qt::NoBrush); - painter->drawRect(r); -} - -void NodeViewDelegate::renderStatus(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option) const -{ - int offset=4; - - QFontMetrics fm(font_); - int deltaH=(option.rect.height()-(fm.height()+4))/2; - - //The initial filled rect (we will adjust its width) - QRect fillRect=option.rect.adjusted(offset,deltaH,-offset,-deltaH-1); - if(option.state & QStyle::State_Selected) - fillRect.adjust(0,0,0,-0); - - int currentRight=fillRect.right(); - - //The text rectangle - QString text=index.data(Qt::DisplayRole).toString(); - int textWidth=fm.width(text); - QRect textRect = fillRect.adjusted(offset,0,0,0); - textRect.setWidth(textWidth); - - if(textRect.right() > currentRight) - currentRight=textRect.right(); - - //Define clipping - int rightPos=currentRight+1; - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - QRect cr=option.rect.adjusted(0,0,-offset,0); - painter->setClipRect(cr); - } - - //Fill rect - QColor bg=index.data(Qt::BackgroundRole).value(); - QColor bgLight=bg.lighter(lighter_); - QBrush bgBrush; - if(useStateGrad_) - { - grad_.setColorAt(0,bgLight); - grad_.setColorAt(1,bg); - bgBrush=QBrush(grad_); - } - else - bgBrush=QBrush(bg); - - painter->fillRect(fillRect,bgBrush); - - //Draw text - painter->setFont(font_); - painter->setPen(Qt::black); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - - if(setClipRect) - { - painter->restore(); - } -} - -//======================================================== -// data is encoded as a QStringList as follows: -// "meter" name value min max colChange -//======================================================== - -void NodeViewDelegate::renderMeter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 6) - return; - - //The data - int val=data.at(2).toInt(); - int min=data.at(3).toInt(); - int max=data.at(4).toInt(); - int threshold=data.at(5).toInt(); - - bool selected=option.state & QStyle::State_Selected; - - QString name=data.at(1) + ":"; - - if(data.count() == 7) - name.prepend(data[6] + ":"); - - QString valStr=data.at(2); - QFontMetrics fm(attrFont_); - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin, - attrBox_->topMargin+attrBox_->topPadding,0, - -attrBox_->bottomMargin-attrBox_->bottomPadding); - - //The status rectangle - int stHeight=static_cast(static_cast(fm.ascent())*0.8); - int stHeightDiff=(contRect.height()-stHeight)/2; - - QRect stRect=contRect.adjusted(attrBox_->leftPadding,stHeightDiff,0,-stHeightDiff); - stRect.setWidth(50); - - //The text rectangle - QFont nameFont=attrFont_; - nameFont.setBold(true); - fm=QFontMetrics(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect; - nameRect.setLeft(stRect.x()+stRect.width()+attrBox_->spacing); - nameRect.setWidth(nameWidth); - - //The value rectangle - QFont valFont=attrFont_; - fm=QFontMetrics(valFont); - int valWidth=fm.width(valStr); - QRect valRect = nameRect; - valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); - valRect.setWidth(valWidth); - - //Define clipping - int rightPos=valRect.x()+valRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Fill st rect - painter->fillRect(stRect,QColor(229,229,229)); - - //Draw progress - if(max > min) - { - QRect progRect=stRect; - - float valPercent=static_cast(val-min)/static_cast(max-min); - if(threshold > min && threshold < max && val > threshold) - { - int progWidth=static_cast(static_cast(stRect.width())*valPercent); - if(val < max) - { - progRect.setWidth(progWidth); - } - painter->fillRect(progRect,meterThresholdBrush_); - - float thresholdPercent=static_cast(threshold-min)/static_cast(max-min); - progWidth=static_cast(static_cast(stRect.width())*thresholdPercent); - progRect.setWidth(progWidth); - painter->fillRect(progRect,meterFillBrush_); - } - else - { - int progWidth=static_cast(static_cast(stRect.width())*valPercent); - if(val < max) - { - progRect.setWidth(progWidth); - } - painter->fillRect(progRect,meterFillBrush_); - } - } - - //Draw st rect border - if(max > min) - { - painter->setPen(QColor(140,140,140)); - } - else - { - painter->setPen(QPen(QColor(140,140,140),Qt::DotLine)); - } - painter->setBrush(Qt::NoBrush); - painter->drawRect(stRect); - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - //Draw value - painter->setPen(Qt::black); - painter->setFont(valFont); - painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valStr); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -//======================================================== -// data is encoded as a QStringList as follows: -// "label" name value -//======================================================== - -void NodeViewDelegate::renderLabel(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 2) - return; - - QString name=data.at(1) + ":"; - QString val; - if(data.count() > 2) - val=data.at(2); - - bool selected=option.state & QStyle::State_Selected; - - //The border rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin, - attrBox_->topMargin+attrBox_->topPadding,0, - -attrBox_->bottomMargin-attrBox_->bottomPadding); - - int currentRight=contRect.x(); - int multiCnt=val.count('\n'); - - QRect nameRect; - QRect valRect,valRestRect; - - QFont nameFont=attrFont_; - nameFont.setBold(true); - QFont valFont=attrFont_; - QString valFirst,valRest; - - if(multiCnt ==0 ) - { - //The text rectangle - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //The value rectangle - fm=QFontMetrics(valFont); - int valWidth=fm.width(val); - valRect = nameRect; - valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); - valRect.setWidth(valWidth); - - //Adjust the filled rect width - currentRight=valRect.x()+valRect.width(); - } - else - { - //The text rectangle - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - nameRect.setHeight(attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding); - - //The value rectangles - fm=QFontMetrics(valFont); - - //First row comes after the name rect! - QStringList valLst=val.split("\n"); - Q_ASSERT(valLst.count() > 0); - valFirst=valLst[0]; - - valRect=nameRect; - valRect.setX(nameRect.x() + nameRect.width() + attrBox_->spacing); - valRect.setWidth(fm.width(valFirst)); - - //The rest of the rows - valLst.takeFirst(); - valRest=valLst.join("\n"); - QSize valSize=fm.size(0,valRest); - - valRestRect = QRect(nameRect.x(), - nameRect.y()+nameRect.height()+2, - valSize.width(),valSize.height()); - - currentRight=qMax(valRect.x()+valRect.width(), - valRestRect.x() + valRestRect.width()); - } - - - //Define clipping - int rightPos=currentRight+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.left(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - //Draw value - painter->setPen(Qt::black); - painter->setFont(valFont); - - if(multiCnt ==0 ) - painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,val); - else - { - painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valFirst); - painter->drawText(valRestRect,Qt::AlignLeft | Qt::AlignVCenter,valRest); - } - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size=QSize(totalWidth,labelHeight(multiCnt+1)); -} - -void NodeViewDelegate::labelSize(QStringList data,int& totalWidth,int& totalHeight) const -{ - if(data.count() < 2) - return; - - QString name=data.at(1) + ":"; - QString val; - if(data.count() > 2) - val=data.at(2); - - int currentRight=attrBox_->leftMargin; - int currentBottom=attrBox_->topMargin+attrBox_->topPadding; - - int multiCnt=val.count('\n'); - QFont nameFont=attrFont_; - nameFont.setBold(true); - QFont valFont=attrFont_; - - if(multiCnt ==0 ) - { - //The text rectangle - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - currentRight+=attrBox_->leftPadding+nameWidth; - currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; - - //The value rectangle - fm=QFontMetrics(valFont); - int valWidth=fm.width(val); - currentRight+=attrBox_->spacing+valWidth; - } - - else - { - //The text rectangle - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - int startX=currentRight+attrBox_->leftPadding; - currentRight+=attrBox_->leftPadding+nameWidth; - currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; - - //The value rectangle - fm=QFontMetrics(valFont); - - //First row comes after the name rect! - QStringList valLst=val.split("\n"); - Q_ASSERT(valLst.count() > 0); - - currentRight+=attrBox_->spacing+fm.width(valLst[0]); - - //The rest of the rows - valLst.takeFirst(); - QString valRest=valLst.join("\n"); - QSize valSize=fm.size(0,valRest); - - currentRight=qMax(currentRight,startX+valSize.width()); - currentBottom+=2+valSize.height(); - } - - totalWidth=currentRight+attrBox_->rightPadding+attrBox_->rightMargin; - totalHeight=currentBottom+attrBox_->bottomPadding+attrBox_->bottomMargin; -} - -int NodeViewDelegate::labelHeight(int lineNum) const -{ - if(lineNum <=1) - return attrBox_->sizeHintCache.height(); - - int currentBottom=attrBox_->topMargin+attrBox_->topPadding; - - //text rect - currentBottom+=attrBox_->height-attrBox_->topPadding-attrBox_->bottomPadding; - - //value rect - QStringList lst; - for(int i=0; i < lineNum-1; i++) - lst << "1"; - - QFontMetrics fm(attrFont_); - QSize valSize=fm.size(0,lst.join(QString("\n"))); - currentBottom+=2+valSize.height(); - - return currentBottom+attrBox_->bottomPadding+attrBox_->bottomMargin; -} - -//======================================================== -// data is encoded as a QStringList as follows: -// "event" name value -//======================================================== - -void NodeViewDelegate::renderEvent(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 2) - return; - - QString name=data[1]; - bool val=false; - if(data.count() > 2) val=(data[2] == "1"); - - //Add full path - if(data.count() > 3) - { - name.prepend(data[3] + ":/"); - } - - bool selected=option.state & QStyle::State_Selected; - QFont font=attrFont_; - QFontMetrics fm(font); - - //The border rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The control rect - int ctHeight=static_cast(static_cast(fm.ascent())*0.8); - int ctHeightDiff=qMax((contRect.height()-ctHeight)/2,2); - - QRect ctRect=contRect.adjusted(attrBox_->leftPadding,ctHeightDiff,0,-ctHeightDiff); - ctRect.setWidth(ctRect.height()); - - //The text rectangle - int nameWidth=fm.width(name); - QRect nameRect = contRect; - nameRect.setX(ctRect.x()+ctRect.width()+attrBox_->spacing); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.left(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw control - painter->setPen(Qt::black); - painter->setBrush((val)?eventFillBrush_:eventBgBrush_); - painter->drawRect(ctRect); - - //Draw name - painter->setPen(Qt::black); - painter->setFont(font); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderVarCore(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size,QColor textCol) const -{ - int totalWidth=0; - QString text; - - if(data.count() >1) - text+=data.at(1) + "="; - if(data.count() > 2) - text+=data.at(2); - - if(data.count() == 4) - text.prepend(data[3] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The text rectangle - QFont font=attrFont_; - QFontMetrics fm(font); - int textWidth=fm.width(text); - QRect textRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - textRect.setWidth(textWidth); - - //Define clipping - int rightPos=textRect.x()+textRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.left(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw text - painter->setPen(textCol); - painter->setFont(font); - painter->drawText(attrBox_->adjustTextRect(textRect),Qt::AlignLeft | Qt::AlignVCenter,text); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size=QSize(totalWidth,attrBox_->fullHeight); -} - - -void NodeViewDelegate::renderVar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - renderVarCore(painter,data,option,size,Qt::black); -} - -void NodeViewDelegate::renderGenvar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - renderVarCore(painter,data,option,size,Qt::blue); -} - -void NodeViewDelegate::renderLimit(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 4) - return; - - //The data - int val=data.at(2).toInt(); - int maxVal=data.at(3).toInt(); - QString name=data.at(1) + ":"; - QString valStr=QString::number(val) + "/" + QString::number(maxVal); - int totalVal=qMax(val,maxVal); //val can be larger than maxVal!! - - if(data.count() == 5) - name.prepend(data[4] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - QFontMetrics fm(attrFont_); - int itemOffset=0; - int itemWidth=0; - int itemHeight=0; - - if(limitShape_ == RectLimitShape) - { - itemWidth=fm.width('p')/2; - itemOffset=qMax(itemWidth/2,2); - itemHeight=static_cast(contRect.height())*0.8; - } - else - { - itemWidth=limitFillPix_.width(); - itemHeight=itemWidth; - itemOffset=0; - } - - //The text rectangle - QFont nameFont=attrFont_; - nameFont.setBold(true); - fm=QFontMetrics(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //The value rectangle - QFont valFont=attrFont_; - fm=QFontMetrics(valFont); - int valWidth=fm.width(valStr); - QRect valRect = nameRect; - valRect.setX(nameRect.x()+nameRect.width()+attrBox_->spacing); - valRect.setWidth(valWidth); - - int xItem=valRect.x()+valRect.width()+attrBox_->spacing; - int rightPos=xItem+totalVal*(itemWidth+itemOffset)+itemOffset; - - rightPos+=attrBox_->rightPadding + attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - - //Define clipping - const bool setClipRect = rightPos > option.rect.right(); - - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - //Draw value - if(val < maxVal) - painter->setPen(Qt::black); - else if(val == maxVal) - painter->setPen(QColor(14,148,26)); - else - painter->setPen(Qt::red); - - painter->setFont(valFont); - painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,valStr); - - //Draw items - if(limitShape_ == RectLimitShape) - { - int yItem=option.rect.y()+(option.rect.height()-itemHeight)/2; - painter->setRenderHint(QPainter::Antialiasing,false); - - painter->setPen(Qt::NoPen); - painter->setBrush(limitFillBrush_); - - for(int i=0; i < totalVal; i++) - { - QRect r(xItem,yItem,itemWidth,itemHeight); - - if(i == val && i < maxVal) - { - painter->setPen(QPen(QColor(190,190,190),0)); - painter->setBrush(Qt::NoBrush); - } - else if( i == maxVal) - painter->setBrush(limitExtraFillBrush_); - - painter->drawRect(r); - - if(i < val) - { - painter->setPen(painter->brush().color().darker(120)); - painter->drawLine(xItem,yItem,xItem+itemWidth,yItem); - painter->drawLine(xItem,yItem,xItem,yItem+itemHeight); - - painter->setPen(painter->brush().color().darker(170)); - painter->drawLine(xItem+itemWidth,yItem,xItem+itemWidth,yItem+itemHeight); - painter->drawLine(xItem,yItem+itemHeight,xItem+itemWidth,yItem+itemHeight); - } - - xItem+=itemOffset+itemWidth; - } - } - else - { - int yItem=option.rect.y()+(option.rect.height()-itemHeight)/2; - for(int i=0; i < totalVal; i++) - { - if(i >= maxVal) - painter->drawPixmap(xItem,yItem,itemWidth,itemHeight,limitExtraFillPix_); - else if(i >= val) - painter->drawPixmap(xItem,yItem,itemWidth,itemHeight,limitEmptyPix_); - else - painter->drawPixmap(xItem,yItem,itemHeight,itemWidth,limitFillPix_); - - xItem+=itemOffset+itemHeight; - } - } - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderLimiter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 4) - return; - - QString name="inlimit " + data[2] +":" + data[1]; - if(data[3] != "1") - name+=" " + data[3]; - - if(data.count() == 5) - name.prepend(data[4] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The text rectangle - QFont nameFont=attrFont_; - //nameFont.setBold(true); - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderTrigger(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() !=3) - return; - - int triggerType=data[1].toInt(); - QString text=data.at(2); - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin, - 0,-attrBox_->bottomMargin); - - //The text rectangle - QFont font=attrFont_; - QFontMetrics fm(font); - int textWidth=fm.width(text)+4; - QRect textRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - textRect.setWidth(textWidth); - - //Define clipping - int rightPos=textRect.x()+textRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //draw rect - if(triggerType==0) - { - painter->setBrush(triggerBgBrush_); - painter->setPen(triggerBorderPen_); - } - else - { - painter->setBrush(completeBgBrush_); - painter->setPen(completeBorderPen_); - } - - //QRect borderRect=option.rect; - //borderRect.setWidth(rightPos-borderRect.x()); - painter->drawRect(attrBox_->adjustTextBgRect(textRect)); - - //Draw text - if(triggerType==0) - painter->setPen(triggerFontPen_); - else - painter->setPen(completeFontPen_); - - painter->setFont(font); - painter->drawText(attrBox_->adjustTextRect(textRect),Qt::AlignHCenter | Qt::AlignVCenter,text); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setX(textRect.x()); - sr.setWidth(textRect.width()); - //sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRectNonOpt(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderTime(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 2) - return; - - QString name=data[1]; - - if(data.count() == 3) - name.prepend(data[2] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The text rectangle - QFont nameFont=attrFont_; - //nameFont.setBold(true); - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderDate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 2) - return; - - QString name=data[1]; - - if(data.count() == 3) - name.prepend(data[2] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The text rectangle - QFont nameFont=attrFont_; - //nameFont.setBold(true); - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} - -//======================================================== -// data is encoded as a QStringList as follows: -// "repeat" name value -//======================================================== - -void NodeViewDelegate::renderRepeat(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 7) - return; - - QString type=data.at(1); - QString name=data.at(2); - QString val=data.at(3); - QString start=data.at(4); - QString end=data.at(5); - QString step=data.at(6); - - if(data.count() == 9) - name.prepend(data[8] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The contents rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - if(type == "day") - { - QFont nameFont=attrFont_; - QFontMetrics fm(nameFont); - name="day=" + step; - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - } - else - { - QString endDot; - if(start == val) - { - name+="="; - endDot="..."; - } - else if(end == val) - { - name+="=..."; - endDot=""; - } - else - { - name+="=..."; - endDot="..."; - } - - //The name rectangle - QFont nameFont=attrFont_; - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //The value rectangle - QFont valFont=attrFont_; - valFont.setBold(true); - fm=QFontMetrics(valFont); - int valWidth=fm.width(val); - QRect valRect = nameRect; - if(name.endsWith("...")) - valRect.setX(nameRect.x()+nameRect.width() + fm.width('A')/2); - else - valRect.setX(nameRect.x()+nameRect.width() + fm.width(' ')/2); - - valRect.setWidth(valWidth); - - int rightPos=valRect.x()+valRect.width(); - - //End ... - QRect dotRect; - if(!endDot.isEmpty()) - { - fm=QFontMetrics(nameFont); - int dotWidth=fm.width("..."); - dotRect = valRect; - dotRect.setX(rightPos+fm.width('A')/2); - dotRect.setWidth(dotWidth); - rightPos=dotRect.x()+dotRect.width(); - } - - //Define clipping - rightPos+=+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - //Draw value - painter->setPen(Qt::black); - painter->setFont(valFont); - painter->drawText(attrBox_->adjustTextRect(valRect),Qt::AlignLeft | Qt::AlignVCenter,val); - - //Draw end dots - if(!endDot.isEmpty()) - { - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(dotRect),Qt::AlignLeft | Qt::AlignVCenter,"..."); - } - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - } - - size.setWidth(totalWidth); -} - -void NodeViewDelegate::renderLate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option,QSize& size) const -{ - int totalWidth=0; - - size=QSize(totalWidth,attrBox_->fullHeight); - - if(data.count() < 2) - return; - - QString name="late: " + data[1]; - - if(data.count() == 3) - name.prepend(data[2] + ":"); - - bool selected=option.state & QStyle::State_Selected; - - //The border rect (we will adjust its width) - QRect contRect=option.rect.adjusted(attrBox_->leftMargin,attrBox_->topMargin,0,-attrBox_->bottomMargin); - - //The text rectangle - QFont nameFont=attrFont_; - QFontMetrics fm(nameFont); - int nameWidth=fm.width(name); - QRect nameRect = contRect.adjusted(attrBox_->leftPadding,0,0,0); - nameRect.setWidth(nameWidth); - - //Define clipping - int rightPos=nameRect.x()+nameRect.width()+attrBox_->rightPadding+attrBox_->rightMargin; - totalWidth=rightPos-option.rect.x(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw name - painter->setPen(Qt::black); - painter->setFont(nameFont); - painter->drawText(attrBox_->adjustTextRect(nameRect),Qt::AlignLeft | Qt::AlignVCenter,name); - - if(selected && drawAttrSelectionRect_) - { - QRect sr=option.rect; - sr.setWidth(rightPos-sr.x()); - renderSelectionRect(painter,attrBox_->adjustSelectionRect(sr)); - } - - if(setClipRect) - { - painter->restore(); - } - - size.setWidth(totalWidth); -} diff -Nru ecflow-4.9.0/Viewer/src/NodeViewDelegate.hpp ecflow-4.11.1/Viewer/src/NodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/src/NodeViewDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef NODEVIEWDELEGATE_HPP_ -#define NODEVIEWDELEGATE_HPP_ - -#include -#include -#include -#include -#include - -#include "FontMetrics.hpp" -#include "RectMetrics.hpp" -#include "VProperty.hpp" - -#include - -class PropertyMapper; - -//Base class for the node/attr renderer properties -struct BaseNodeDelegateBox -{ - BaseNodeDelegateBox() : fontHeight(10), height(10), fullHeight(10), topMargin(0), bottomMargin(0), - leftMargin(0), rightMargin(0), topPadding(0), bottomPadding(0), - leftPadding(0), rightPadding(0), sizeHintCache(QSize(10,10)), spacing(2), selectRm(2) {} - - virtual ~BaseNodeDelegateBox() {} - - virtual void adjust(const QFont& f)=0; - virtual QRect adjustTextRect(const QRect& rIn) const { QRect r=rIn; return r;} - virtual QRect adjustTextBgRect(const QRect& rIn) const { QRect r=rIn; return r;} - virtual QRect adjustSelectionRect(const QRect& optRect) const { - QRect r=optRect; - r.adjust(leftMargin,1,-1,-1); - return r; - } - virtual QRect adjustSelectionRectNonOpt(const QRect& optRect) const { - QRect r=optRect; - r.adjust(0,1,-1,-1); - return r; - } - - int fontHeight; - int height; - int fullHeight; - int topMargin; - int bottomMargin; - int leftMargin; - int rightMargin; - int topPadding; - int bottomPadding; - int leftPadding; - int rightPadding; - QSize sizeHintCache; - int spacing; - RectMetrics selectRm; -}; - -//Node renderer properties -struct NodeDelegateBox : public BaseNodeDelegateBox -{ - NodeDelegateBox() : iconSize(16), iconPreGap(2), - iconGap(2) {} - - int iconSize; - int iconPreGap; - int iconGap; - - void adjust(const QFont& f) { - QFontMetrics fm(f); - fontHeight=fm.height(); - height=fontHeight+topPadding+bottomPadding; - fullHeight=height+topMargin+bottomMargin; - sizeHintCache=QSize(100,fullHeight); - spacing=fm.width('A')*3/4; - - int h=static_cast(static_cast(fm.height())*0.7); - iconSize=h; - if(iconSize % 2 == 1) - iconSize+=1; - - iconGap=1; - if(iconSize > 16) - iconGap=2; - - iconPreGap=fm.width('A')/2; - } -}; - -//Attr renderer properties -struct AttrDelegateBox : public BaseNodeDelegateBox -{ - AttrDelegateBox() {} - - void adjust(const QFont& f) { - QFontMetrics fm(f); - fontHeight=fm.height(); - height=fontHeight+topPadding+bottomPadding; - fullHeight=height+topMargin+bottomMargin; - sizeHintCache=QSize(100,fullHeight); - spacing=fm.width('A')*3/4; - } -}; - -class NodeViewDelegate : public QStyledItemDelegate, public VPropertyObserver -{ -public: - explicit NodeViewDelegate(QWidget *parent=0); - ~NodeViewDelegate(); - - void notifyChange(VProperty*); - -protected: - virtual void updateSettings()=0; - void addBaseSettings(std::vector&); - void updateBaseSettings(); - - void renderSelectionRect(QPainter* painter,QRect r) const; - - virtual void renderStatus(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option) const; - - typedef void (NodeViewDelegate::*AttributeRendererProc)(QPainter *painter,QStringList data,const QStyleOptionViewItem& optio,QSize&) const; - - virtual void renderMeter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderLabel(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderEvent(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderVar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderGenvar(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderLimit(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderLimiter(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderTrigger(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderTime(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderDate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderRepeat(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - virtual void renderLate(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&) const; - - void labelSize(QStringList data,int& totalWidth,int& totalHeight) const; - int labelHeight(int) const; - void renderVarCore(QPainter *painter,QStringList data,const QStyleOptionViewItem& option, QSize&,QColor) const; - - QPen hoverPen_; - QBrush hoverBrush_; - QPen selectPen_; - QBrush selectBrush_; - QPen nodePen_; - QPen nodeSelectPen_; - QPixmap errPix_; - - QBrush lostConnectBgBrush_; - QBrush lostConnectBandBrush_; - - QMap attrRenderers_; - - PropertyMapper* prop_; - QFont font_; - QFont attrFont_; - NodeDelegateBox* nodeBox_; - AttrDelegateBox* attrBox_; - - bool useStateGrad_; - mutable QLinearGradient grad_; - static int lighter_; - bool drawAttrSelectionRect_; - QBrush eventFillBrush_; - QBrush eventBgBrush_; - QBrush meterFillBrush_; - QBrush meterThresholdBrush_; - QBrush limitFillBrush_; - QBrush limitExtraFillBrush_; - QPixmap limitFillPix_; - QPixmap limitEmptyPix_; - QPixmap limitExtraFillPix_; - enum LimitShape {RectLimitShape,CircleLimitShape}; - LimitShape limitShape_; - QBrush triggerBgBrush_; - QPen triggerBorderPen_; - QPen triggerFontPen_; - QBrush completeBgBrush_; - QPen completeBorderPen_; - QPen completeFontPen_; -}; - -#endif - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeWidget.cpp ecflow-4.11.1/Viewer/src/NodeWidget.cpp --- ecflow-4.9.0/Viewer/src/NodeWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,185 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#include "NodeWidget.hpp" -#include "NodeViewBase.hpp" - -#include "AbstractNodeModel.hpp" -#include "InfoPanelHandler.hpp" -#include "VFilter.hpp" -#include "VModelData.hpp" - -#include -#include - -NodeWidget::NodeWidget(const std::string& type,ServerFilter* serverFilter,QWidget* parent) : - DashboardWidget(type,parent), - serverFilter_(serverFilter), - model_(0), - view_(0), - icons_(0), - atts_(0), - filterDef_(0), - states_(0), - broadcastSelection_(true) -{ - //Define the icon filter for the model. It controls what icons - //are displayed next to the nodes. This is exposed via a menu. - icons_=new IconFilter; - - //Define the attribute filter for the model. It controls what attributes - //are displayed for a given node. This is exposed via a menu. - atts_=new AttributeFilter; - - createActions(); -} - -NodeWidget::~NodeWidget() -{ - //We only need to delete the non-qobject members - delete icons_; - delete atts_; - - if(filterDef_) - delete filterDef_; -} - -QWidget* NodeWidget::widget() -{ - return view_->realWidget(); -} - -VInfo_ptr NodeWidget::currentSelection() -{ - return view_->currentSelection(); -} - -/*void NodeWidget::currentSelection(VInfo_ptr info) -{ - view_->currentSelection(info); -}*/ - -void NodeWidget::setCurrentSelection(VInfo_ptr info) -{ - if(!detached() && info) - { - //We need to avoid inifinite recursion. This could happen like this: - //1. Select node in panel A - //2. We broadcast the selection to panel B - //3. Panel B selects the node - //4 Panel B then brodcasts the selection back to panel A - // ... - //So here we need to check if the given item is already selected!! - VInfo_ptr csInfo=currentSelection(); - if(csInfo && info && *(csInfo.get()) == *(info.get())) - return; - - //This will broadcast the selection!!! - view_->setCurrentSelection(info); - } -} - -void NodeWidget::slotSelectionChangedInBc(VInfo_ptr info) -{ - view_->setCurrentSelection(info); -} - -void NodeWidget::reload() -{ - active(true); - //model_->reload(); -} - -void NodeWidget::active(bool b) -{ - model_->active(b); - view_->realWidget()->setEnabled(b); -} - -bool NodeWidget::active() const -{ - return model_->active(); -} - -void NodeWidget::createActions() -{ - /*QAction* infoAc=new QAction(" ",this); - QPixmap pix(":/viewer/dock_info.svg"); - infoAc->setIcon(QIcon(pix)); - infoAc->setToolTip(tr("Start up information panel as dialog")); - dockActions_ << infoAc; - dockActionMap_["info"]=infoAc; - - QMenu *menu=new QMenu(this); - infoAc->setMenu(menu); - - for(std::vector::const_iterator it=InfoPanelHandler::instance()->panels().begin(); - it != InfoPanelHandler::instance()->panels().end(); it++) - { - if((*it)->show().find("toolbar") != std::string::npos) - { - QAction *ac=new QAction(QString::fromStdString((*it)->label()) + "...",this); - //QPixmap pix(":/viewer/" + QString::fromStdString((*it)->dockIcon())); - QPixmap pix(":/viewer/" + QString::fromStdString((*it)->icon())); - ac->setIcon(QIcon(pix)); - ac->setData(QString::fromStdString((*it)->name())); - ac->setEnabled(false); - - connect(ac,SIGNAL(triggered()), - this,SLOT(slotInfoPanelAction())); - - infoPanelActions_ << ac; - } - }*/ -} - -void NodeWidget::slotInfoPanelAction() -{ - /*if(QAction* ac=static_cast(sender())) - { - Q_EMIT popInfoPanel(view_->currentSelection(),ac->data().toString()); - }*/ -} - -void NodeWidget::updateActionState(VInfo_ptr info) -{ - /* std::vector ids; - InfoPanelHandler::instance()->visible(info,ids); - - QAction *infoAc=dockActionMap_["info"]; - assert(infoAc); - - QMenu* menu=infoAc->menu(); - assert(menu); - - menu->clear(); - - Q_FOREACH(QAction* ac,infoPanelActions_) - { - ac->setEnabled(false); - - std::string name=ac->data().toString().toStdString(); - - for(std::vector::const_iterator it=ids.begin(); it != ids.end(); it++) - { - if((*it)->name() == name) - { - ac->setEnabled(true); - menu->addAction(ac); - break; - } - } - }*/ -} - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/NodeWidget.hpp ecflow-4.11.1/Viewer/src/NodeWidget.hpp --- ecflow-4.9.0/Viewer/src/NodeWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/NodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#ifndef NODEWIDGET_HPP_ -#define NODEWIDGET_HPP_ - -#include -#include - -#include "DashboardWidget.hpp" -#include "Viewer.hpp" -#include "VInfo.hpp" - -class QStackedLayout; -class QWidget; - -class AbstractNodeModel; -class AttributeFilter; -class IconFilter; -class NodeFilterDef; -class NodeStateFilter; -class VModelData; -class NodePathWidget; -class NodeViewBase; -class ServerFilter; - -class NodeWidget : public DashboardWidget -{ -Q_OBJECT - -public: - void active(bool); - bool active() const; - NodeViewBase* view() const {return view_;} - QWidget* widget(); - VInfo_ptr currentSelection(); - //void currentSelection(VInfo_ptr info); - void reload(); - void populateDialog() {} - QList dockTitleActions() {return dockActions_;} - -public Q_SLOTS: - void setCurrentSelection(VInfo_ptr); - -protected Q_SLOTS: - void slotInfoPanelAction(); - void slotSelectionChangedInBc(VInfo_ptr info); - -protected: - explicit NodeWidget(const std::string& type,ServerFilter* serverFilter,QWidget* parent=0); - virtual ~NodeWidget(); - - void updateActionState(VInfo_ptr); - bool broadcastSelection() const {return broadcastSelection_;} - - ServerFilter* serverFilter_; - - AbstractNodeModel* model_; - NodeViewBase* view_; - - IconFilter* icons_; - AttributeFilter* atts_; - - NodeFilterDef* filterDef_; - NodeStateFilter *states_; - -private: - void createActions(); - QList dockActions_; - QMap dockActionMap_; - QList infoPanelActions_; - bool broadcastSelection_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/OneLineTextEdit.cpp ecflow-4.11.1/Viewer/src/OneLineTextEdit.cpp --- ecflow-4.9.0/Viewer/src/OneLineTextEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OneLineTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OneLineTextEdit.hpp" - -#include "Highlighter.hpp" - -#include -#include -#include -#include - -OneLineTextEdit::OneLineTextEdit(QWidget* parent) : QTextEdit(parent) -{ - setReadOnly(true); - setWordWrapMode(QTextOption::NoWrap); - setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); - setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); - - document()->setDocumentMargin(2); - - QFont f; - QFontMetrics fm(f); - - int h=fm.height()+fm.leading() + 1 +6; - - setFixedHeight(h); - - //The document becomes the owner of the highlighter - new Highlighter(document(),"query"); -} - -QSize OneLineTextEdit::sizeHint() const -{ - return QTextEdit::sizeHint(); - /* - QFontMetrics fm(font()); - QStyleOptionFrameV3 opt; - QString text = document()->toHtml(); - - int h = qMax(fm.height(), 14) + 4; - int w = fm.width(text) + 4; - - opt.initFrom(this); - - return style()->sizeFromContents( - QStyle::CT_LineEdit, - &opt, - QSize(w, h).expandedTo(QApplication::globalStrut()), - this - );*/ -} - -void OneLineTextEdit::mousePressEvent(QMouseEvent *e) -{ - Q_EMIT clicked(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/OneLineTextEdit.hpp ecflow-4.11.1/Viewer/src/OneLineTextEdit.hpp --- ecflow-4.9.0/Viewer/src/OneLineTextEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OneLineTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_ONELINETEXTEDIT_HPP_ -#define VIEWER_SRC_ONELINETEXTEDIT_HPP_ - -#include - -class OneLineTextEdit : public QTextEdit -{ -Q_OBJECT - -public: - OneLineTextEdit(QWidget* parent=0); - QSize sizeHint() const; - -Q_SIGNALS: - void clicked(); - -protected: - void mousePressEvent(QMouseEvent *e); -}; - - - -#endif /* VIEWER_SRC_ONELINETEXTEDIT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputBrowser.cpp ecflow-4.11.1/Viewer/src/OutputBrowser.cpp --- ecflow-4.9.0/Viewer/src/OutputBrowser.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputBrowser.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,573 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OutputBrowser.hpp" - -#include -#include -#include - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#endif - -#include "Highlighter.hpp" - -#include "MessageLabel.hpp" -#include "HtmlEdit.hpp" -#include "PlainTextEdit.hpp" -#include "PlainTextSearchInterface.hpp" -#include "TextEditSearchLine.hpp" -#include "TextPager/TextPagerSearchInterface.hpp" -#include "TextPagerWidget.hpp" -#include "DirectoryHandler.hpp" -#include "TextFilterWidget.hpp" -#include "UserMessage.hpp" -#include "UiLog.hpp" - -int OutputBrowser::minPagerTextSize_=1*1024*1024; -int OutputBrowser::minPagerSparseSize_=30*1024*1024; -int OutputBrowser::minConfirmSearchSize_=5*1024*1024; - -OutputBrowser::OutputBrowser(QWidget* parent) : - QWidget(parent), - searchTb_(0) -{ - QVBoxLayout *vb=new QVBoxLayout(this); - vb->setContentsMargins(0,0,0,0); - vb->setSpacing(2); - - //Text filter editor - textFilter_=new TextFilterWidget(this); - vb->addWidget(textFilter_); - - connect(textFilter_,SIGNAL(runRequested(QString,bool,bool)), - this,SLOT(slotRunFilter(QString,bool,bool))); - - connect(textFilter_,SIGNAL(clearRequested()), - this,SLOT(slotRemoveFilter())); - - connect(textFilter_,SIGNAL(closeRequested()), - this,SLOT(slotRemoveFilter())); - - stacked_=new QStackedWidget(this); - vb->addWidget(stacked_,1); - - confirmSearchLabel_=new MessageLabel(this); - confirmSearchLabel_->setShowTypeTitle(false); - confirmSearchLabel_->setNarrowMode(true); - vb->addWidget(confirmSearchLabel_); - - searchLine_=new TextEditSearchLine(this); - vb->addWidget(searchLine_); - - //Basic textedit - textEdit_=new PlainTextEdit(this); - textEdit_->setReadOnly(true); - textEdit_->setWordWrapMode(QTextOption::NoWrap); - textEdit_->setShowLineNumbers(false); - - textEditSearchInterface_=new PlainTextSearchInterface(); - textEditSearchInterface_->setEditor(textEdit_); - - //This highlighter only works for jobs - jobHighlighter_=new Highlighter(textEdit_->document(),"job"); - jobHighlighter_->setDocument(NULL); - - //Pager for very large files - textPager_=new TextPagerWidget(this); - textPager_->textEditor()->setShowLineNumbers(false); - - //textEdit_->setReadOnly(true); - - textPagerSearchInterface_=new TextPagerSearchInterface(); - textPagerSearchInterface_->setEditor(textPager_->textEditor()); - - //Html browser for html files - htmlEdit_=new HtmlEdit(this); - - stacked_->addWidget(textEdit_); - stacked_->addWidget(textPager_); - stacked_->addWidget(htmlEdit_); - - stacked_->setCurrentIndex(BasicIndex); - searchLine_->hide(); - - connect(searchLine_,SIGNAL(visibilityChanged()), - this,SLOT(showConfirmSearchLabel())); - - //the textfilter is is hidden by default - textFilter_->hide(); -} - -OutputBrowser::~OutputBrowser() -{ - delete textEditSearchInterface_; - delete textPagerSearchInterface_; - - if(jobHighlighter_ && !jobHighlighter_->parent()) - { - delete jobHighlighter_; - } -} - -void OutputBrowser::setSearchButtons(QToolButton* searchTb) -{ - searchTb_=searchTb; -} - -void OutputBrowser::setFilterButtons(QToolButton* statusTb,QToolButton* optionTb) -{ - textFilter_->setExternalButtons(statusTb,optionTb); -} - -void OutputBrowser::clear() -{ - textEdit_->clear(); - textPager_->clear(); - htmlEdit_->clear(); - file_.reset(); - oriFile_.reset(); -} - -void OutputBrowser::changeIndex(IndexType indexType,qint64 fileSize) -{ - if(indexType == BasicIndex) - { - stacked_->setCurrentIndex(indexType); - textPager_->clear(); - htmlEdit_->clear(); - - //enable and init search - if(searchTb_) searchTb_->setEnabled(true); - searchLine_->setConfirmSearch(false); - searchLine_->setSearchInterface(textEditSearchInterface_); - - //enable filter - textFilter_->setEnabledExternalButtons(true); - } - else if(indexType == PagerIndex) - { - stacked_->setCurrentIndex(indexType); - textEdit_->clear(); - htmlEdit_->clear(); - - //enable and init search - if(searchTb_) searchTb_->setEnabled(true); - searchLine_->setConfirmSearch(fileSize >=minConfirmSearchSize_); - searchLine_->setSearchInterface(textPagerSearchInterface_); - - //enable filter - textFilter_->setEnabledExternalButtons(true); - } - else if(indexType == HtmlIndex) - { - stacked_->setCurrentIndex(indexType); - textPager_->clear(); - textEdit_->clear(); - if(oriFile_) - oriFile_.reset(); - - //Disable search - if(searchTb_) searchTb_->setEnabled(false); - searchLine_->setSearchInterface(0); - searchLine_->hide(); - - //Disable filter - textFilter_->closeIt(); - textFilter_->setEnabledExternalButtons(false); - } - - showConfirmSearchLabel(); -} - -//This should only be called externally when a new output is loaded -void OutputBrowser::loadFile(VFile_ptr file) -{ - if(!file) - { - clear(); - return; - } - - file_=file; - if(file_->storageMode() == VFile::DiskStorage) - { - loadFile(QString::fromStdString(file_->path())); - } - else - { - QString s(file_->data()); - loadText(s,QString::fromStdString(file_->sourcePath()),true); - } - - //Run the filter if defined - if(textFilter_->isActive()) - { - slotRunFilter(textFilter_->filterText(),textFilter_->isMatched(), - textFilter_->isCaseSensitive()); - } -} - -void OutputBrowser::loadFilteredFile(VFile_ptr file) -{ - if(!file) - { - clear(); - return; - } - - file_=file; - Q_ASSERT(file_->storageMode() == VFile::DiskStorage); - loadFile(QString::fromStdString(file_->path())); -} - -void OutputBrowser::loadFile(QString fileName) -{ - QFile file(fileName); - file.open(QIODevice::ReadOnly); - QFileInfo fInfo(file); - qint64 fSize=fInfo.size(); - - if(isHtmlFile(fileName)) - { - changeIndex(HtmlIndex,fSize); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - QString str=file.readAll(); - htmlEdit_->document()->setHtml(str); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - - } - else if(!isJobFile(fileName) && fSize >= minPagerTextSize_) - { - changeIndex(PagerIndex,fSize); - - TextPagerDocument::DeviceMode mode=(fSize >= minPagerSparseSize_)? - TextPagerDocument::Sparse:TextPagerDocument::LoadAll; - textPager_->load(fileName, mode); - } - else - { - changeIndex(BasicIndex,fSize); - - adjustHighlighter(fileName); - - QString str=file.readAll(); - textEdit_->document()->setPlainText(str); - } -} - -void OutputBrowser::loadText(QString txt,QString fileName,bool resetFile) -{ - // prior to the ability to save local copies of files, we reset the file_ member here; - // but now we need to keep it so that we can save a copy of it - //if(resetFile) - //file_.reset(); - - //We estimate the size in bytes - qint64 txtSize=txt.size()*2; - - if(isHtmlFile(fileName)) - { - changeIndex(HtmlIndex,txtSize); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - htmlEdit_->document()->setHtml(txt); -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - } - else if(!isJobFile(fileName) && txtSize > minPagerTextSize_) - { - changeIndex(PagerIndex,txtSize); - textPager_->setText(txt); - } - else - { - changeIndex(BasicIndex,txtSize); - adjustHighlighter(fileName); - textEdit_->document()->setPlainText(txt); - } - - //Set the cursor position from the cache - //updateCursorFromCache(fileName.toStdString()); -} - -void OutputBrowser::saveCurrentFile(QString &fileNameToSaveTo) -{ - assert(file_); - - if (file_->storageMode() == VFile::DiskStorage) - { - // if we have the file on disk, then just copy it to the new location - std::string to = fileNameToSaveTo.toStdString(); - std::string errorMessage; - bool ok = DirectoryHandler::copyFile(file_->path(), to, errorMessage); - if (!ok) - { - UserMessage::message(UserMessage::ERROR,true,"Failed to copy file. Reason:\n " + errorMessage); - } - } - else - { - // file in memory - just dump it to file - QFile file(fileNameToSaveTo); - if (file.open(QFile::WriteOnly | QFile::Text)) - { - QTextStream out(&file); - QString s(file_->data()); - out << s; - } - else - { - UserMessage::message(UserMessage::ERROR,true,"Failed to save file to " + fileNameToSaveTo.toStdString()); - } - } -} - -bool OutputBrowser::isFileLoaded() -{ - return (file_ != 0); -} - -bool OutputBrowser::isJobFile(QString fileName) -{ - return fileName.contains(".job"); -} - -bool OutputBrowser::isHtmlFile(QString fileName) -{ - return fileName.endsWith(".html"); -} - -void OutputBrowser::adjustHighlighter(QString fileName) -{ - //For job files we set the proper highlighter - if(isJobFile(fileName)) - { - if(!jobHighlighter_) - { - jobHighlighter_=new Highlighter(textEdit_->document(),"job"); - } - else if(jobHighlighter_->document() != textEdit_->document()) - { - jobHighlighter_->setDocument(textEdit_->document()); - } - } - else if(jobHighlighter_) - { - jobHighlighter_->setDocument(NULL); - } -} - -void OutputBrowser::gotoLine() -{ - if(stacked_->currentIndex() == BasicIndex) - textEdit_->gotoLine(); - else if(stacked_->currentIndex() == PagerIndex) - textPager_->gotoLine(); -} - -void OutputBrowser::showSearchLine() -{ - if(searchLine_->hasInterface()) - { - searchLine_->setVisible(true); - searchLine_->setFocus(); - searchLine_->selectAll(); - } -} - -void OutputBrowser::searchOnReload(bool userClickedReload) -{ - if(searchLine_->hasInterface()) - { - searchLine_->searchOnReload(userClickedReload); - } -} - -void OutputBrowser::setFontProperty(VProperty* p) -{ - textEdit_->setFontProperty(p); - textPager_->setFontProperty(p); - htmlEdit_->setFontProperty(p); -} - -void OutputBrowser::updateFont() -{ - textEdit_->updateFont(); -} - -void OutputBrowser::zoomIn() -{ - textEdit_->slotZoomIn(); - textPager_->zoomIn(); - htmlEdit_->zoomIn(); -} - -void OutputBrowser::zoomOut() -{ - textEdit_->slotZoomOut(); - textPager_->zoomOut(); - htmlEdit_->zoomOut(); -} - -void OutputBrowser::showConfirmSearchLabel() -{ - if(searchLine_->isVisible() && searchLine_->confirmSearch()) - { - confirmSearchLabel_->showWarning(searchLine_->confirmSearchText()); - //confirmSearchLabel_->show(); - } - else - { - confirmSearchLabel_->hide(); - } -} - -void OutputBrowser::setCursorPos(qint64 pos) -{ - if(stacked_->currentIndex() == BasicIndex) - { - QTextCursor c=textEdit_->textCursor(); - c.setPosition(pos); - textEdit_->setTextCursor(c); - } - else if(stacked_->currentIndex() == PagerIndex) - { - textPager_->textEditor()->setCursorPosition(pos); - } -} - -void OutputBrowser::slotRunFilter(QString filter,bool matched,bool caseSensitive) -{ - if(stacked_->currentIndex() == HtmlIndex) - return; - - assert(file_); - - if(oriFile_) - file_=oriFile_; - - VFile_ptr fTarget=VFile::createTmpFile(true); - VFile_ptr fSrc=VFile_ptr(); - - // file is on disk - we use it as it is - if(file_->storageMode() == VFile::DiskStorage) - { - fSrc=file_; - } - // file in memory - just dump it to the tmp file - else - { - fSrc=VFile::createTmpFile(true); - QFile file(QString::fromStdString(fSrc->path())); - if(file.open(QFile::WriteOnly | QFile::Text)) - { - QTextStream out(&file); - QString s(file_->data()); - out << s; - } - else - { - } - } - - //At this point point fSrc must contain the text to filter - QProcess proc; - proc.setStandardOutputFile(QString::fromStdString(fTarget->path())); - - //Run grep to filter fSrc into fTarget - QString extraOptions; - if(!matched) - extraOptions+=" -v "; - if(!caseSensitive) - extraOptions+=" -i "; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - - proc.start("/bin/sh", - QStringList() << "-c" << "grep " + extraOptions + " -e \'" + filter + "\' " + - QString::fromStdString(fSrc->path())); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - UiLog().dbg() << "args=" << proc.arguments().join(" "); -#endif - - if(!proc.waitForStarted(1000)) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - QString errStr; - UI_FUNCTION_LOG -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - errStr="Failed to start text filter using command:
        \'" + - proc.program() + " " + proc.arguments().join(" ") + "\'"; -#else - errStr="Failed to start grep command!"; -#endif - UserMessage::message(UserMessage::ERROR,true,errStr.toStdString()); - fTarget.reset(); - return; - } - - proc.waitForFinished(60000); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - - QString errStr=proc.readAllStandardError(); - if(proc.exitStatus() == QProcess::NormalExit && errStr.isEmpty()) - { - oriFile_=file_; - textFilter_->setStatus(fTarget->isEmpty()?(TextFilterWidget::NotFoundStatus):(TextFilterWidget::FoundStatus)); - loadFilteredFile(fTarget); - } - else - { - QString msg; - UI_FUNCTION_LOG -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - msg="Failed to filter output file using command:
        \'" + - proc.program() + " " + proc.arguments().join(" ") + "\'"; -#else - msg="Failed to run grep command!"; -#endif - if(!errStr.isEmpty()) - msg+="
        Error message: " + errStr; - - UserMessage::message(UserMessage::ERROR,true,msg.toStdString()); - textFilter_->setStatus(TextFilterWidget::NotFoundStatus); - fTarget.reset(); //delete - } - - return; -} - -void OutputBrowser::slotRemoveFilter() -{ - if(stacked_->currentIndex() == HtmlIndex) - return; - - if(oriFile_) - { - loadFile(oriFile_); - oriFile_.reset(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/OutputBrowser.hpp ecflow-4.11.1/Viewer/src/OutputBrowser.hpp --- ecflow-4.9.0/Viewer/src/OutputBrowser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputBrowser.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_OUTPUTBROWSER_HPP_ -#define VIEWER_SRC_OUTPUTBROWSER_HPP_ - -#include -#include -#include - -#include "VFile.hpp" - -class Highlighter; -class MessageLabel; -class TextEditSearchLine; -class PlainTextEdit; -class PlainTextSearchInterface; -class TextPagerWidget; -class TextPagerSearchInterface; -class VProperty; -class TextFilterWidget; -class QToolButton; -class HtmlEdit; - -class OutputBrowser; - -class CursorCacheItem -{ - friend class OutputBrowser; -public: - CursorCacheItem() : pos_(0), verticalScrollValue_(0), viewportHeight_(0) {} -protected: - int pos_; - int verticalScrollValue_; - int viewportHeight_; -}; - -class OutputBrowser : public QWidget -{ -Q_OBJECT - -public: - explicit OutputBrowser(QWidget* parent); - ~OutputBrowser(); - - void clear(); - void loadFile(VFile_ptr file); - bool isFileLoaded(); - void saveCurrentFile(QString &fileNameToSaveTo); - void adjustHighlighter(QString fileName); - void setFontProperty(VProperty* p); - void updateFont(); - void gotoLine(); - void showSearchLine(); - void searchOnReload(bool userClickedReload); - void zoomIn(); - void zoomOut(); - void setSearchButtons(QToolButton* searchTb); - void setFilterButtons(QToolButton* statusTb,QToolButton* optionTb); - -protected Q_SLOTS: - void showConfirmSearchLabel(); - void slotRunFilter(QString,bool,bool); - void slotRemoveFilter(); - -private: - enum IndexType {BasicIndex=0,PagerIndex=1,HtmlIndex=2}; - void changeIndex(IndexType indexType,qint64 fileSize); - bool isJobFile(QString fileName); - bool isHtmlFile(QString fileName); - void loadFile(QString fileName); - void loadText(QString text,QString fileName,bool resetFile=true); - void loadFilteredFile(VFile_ptr file); - void setCursorPos(qint64 pos); - - QStackedWidget *stacked_; - PlainTextEdit* textEdit_; - TextPagerWidget* textPager_; - HtmlEdit* htmlEdit_; - TextEditSearchLine* searchLine_; - TextFilterWidget* textFilter_; - Highlighter* jobHighlighter_; - PlainTextSearchInterface *textEditSearchInterface_; - TextPagerSearchInterface *textPagerSearchInterface_; - MessageLabel *confirmSearchLabel_; - QToolButton* searchTb_; - - //we keep a reference to it to make sure that it does not get deleted while - //it is being displayed - VFile_ptr file_; - VFile_ptr oriFile_; - - static int minPagerTextSize_; - static int minPagerSparseSize_; - static int minConfirmSearchSize_; -}; - -#endif /* VIEWER_SRC_OUTPUTBROWSER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputCache.cpp ecflow-4.11.1/Viewer/src/OutputCache.cpp --- ecflow-4.9.0/Viewer/src/OutputCache.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputCache.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,234 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OutputCache.hpp" - -#include - -#include "UiLog.hpp" - -#define _UI_OUTPUTCACHE_DEBUG - -OutputCacheItem::OutputCacheItem(QString id, VFile_ptr file) : - id_(id), file_(file), used_(false) -{ -} - -bool OutputCacheItem::isAttached() const -{ - return used_; -} - -void OutputCacheItem::attach() -{ - used_=true; -} - -void OutputCacheItem::detach() -{ - used_=false; - inTimeOut_.start(); -} - -std::ostream& operator<<(std::ostream& stream,const OutputCacheItem& item) -{ - stream << "id:" << item.id_.toStdString() << " tmp:" << - item.file_->path() << " countdown:" << - (!item.isAttached()); - return stream; -} - -//======================================================== -// -// OutputCache -// -//======================================================== - -OutputCache::OutputCache(QObject *parent) : - QObject(parent), - timeOut_(1000*15), //we check the state every 15 sec - maxAttachedPeriod_(1000*120) //cache retention period is 2 min -{ - timer_=new QTimer(this); - connect(timer_,SIGNAL(timeout()), - this,SLOT(slotTimeOut())); -} - -OutputCache::~OutputCache() -{ - clear(); -} - -void OutputCache::adjustTimer() -{ - if(items_.isEmpty()) - stopTimer(); - else - startTimer(); -} - -void OutputCache::startTimer() -{ - if(!timer_->isActive()) - timer_->start(timeOut_); -} - -void OutputCache::stopTimer() -{ - timer_->stop(); -} - -void OutputCache::clear() -{ - Q_FOREACH(OutputCacheItem* item,items_.values()) - { - delete item; - } - stopTimer(); -} - -OutputCacheItem* OutputCache::add(VInfo_ptr info,const std::string& sourcePath,VFile_ptr file) -{ - if(!file) - return NULL; - -#ifdef _UI_OUTPUTCACHE_DEBUG - UI_FUNCTION_LOG - file->print(); - //print(); -#endif - - if(info && file && info->isNode() && info->server()) - { - //The key we would store for the item in the map - QString id=QString::fromStdString(info->path() + ":" + sourcePath); - - //The item exists - QMap::iterator it = items_.find(id); - if(it != items_.end()) - { - it.value()->attach(); - it.value()->file_=file; - startTimer(); - return it.value(); - } - //The item not yet exists - else - { - OutputCacheItem* item=new OutputCacheItem(id,file); - items_[id]=item; - item->attach(); -#ifdef _UI_OUTPUTCACHE_DEBUG - UiLog().dbg() << " add " << *item; - //print(); -#endif - startTimer(); - return item; - } - } - - return NULL; -} - -//Attach one item and detach all the others -OutputCacheItem* OutputCache::attachOne(VInfo_ptr info,const std::string& fileName) -{ - OutputCacheItem* attachedItem=0; - if(info && info->isNode() && info->server()) - { - QString inPath=QString::fromStdString(info->path() + ":"); - QString inFileName=QString::fromStdString(fileName); - - QMap::iterator it=items_.begin(); - while (it != items_.end() ) - { - if(it.key().startsWith(inPath) && it.key().endsWith(inFileName)) - { - it.value()->attach(); - attachedItem=it.value(); - } - else - { - it.value()->detach(); - } - ++it; - } - } - adjustTimer(); - return attachedItem; -} - -//Detach all the items -void OutputCache::detach() -{ - QMap::iterator it=items_.begin(); - while(it != items_.end()) - { -#ifdef _UI_OUTPUTCACHE_DEBUG - UI_FUNCTION_LOG - //print(); - UiLog().dbg() << " detach -> " << *(it.value()); -#endif - it.value()->detach(); - Q_ASSERT(!it.value()->isAttached()); - -#ifdef _UI_OUTPUTCACHE_DEBUG - //print(); - UiLog().dbg() << " detached -> " << *(it.value()); -#endif - ++it; - } - - adjustTimer(); -} - -void OutputCache::slotTimeOut() -{ -#ifdef _UI_OUTPUTCACHE_DEBUG -UI_FUNCTION_LOG -#endif - - QMap::iterator it=items_.begin(); - while(it != items_.end() ) - { - OutputCacheItem* item=it.value(); - if(!item->isAttached() && - item->inTimeOut_.elapsed() > maxAttachedPeriod_) - { -#ifdef _UI_OUTPUTCACHE_DEBUG - UiLog().dbg() << " remove " << it.value(); - UiLog().dbg() << " -> ref_count:" << it.value()->file_.use_count(); -#endif - it=items_.erase(it); - delete item; - //item->deleteLater(); -#ifdef _UI_OUTPUTCACHE_DEBUG - print(); -#endif - } - else - { - ++it; - } - } - - adjustTimer(); -} - -void OutputCache::print() -{ - UI_FUNCTION_LOG - QMap::iterator it = items_.begin(); - while (it != items_.end() ) - { - UiLog().dbg() << *(it.value()); - ++it; - } -} diff -Nru ecflow-4.9.0/Viewer/src/OutputCache.hpp ecflow-4.11.1/Viewer/src/OutputCache.hpp --- ecflow-4.9.0/Viewer/src/OutputCache.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputCache.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef OUTPUTCHACHE_HPP_ -#define OUTPUTCHACHE_HPP_ - -#include -#include -#include -#include - -#include - -#include "OutputClient.hpp" -#include "VFile.hpp" -#include "VInfo.hpp" - - -class OutputCache; - -struct OutputCacheItem -{ - friend class OutputCache; - -public: - OutputCacheItem(QString id,VFile_ptr file); - VFile_ptr file() const {return file_;} - bool isAttached() const; - friend std::ostream& operator<<(std::ostream& stream,const OutputCacheItem& item); - -protected: - void attach(); - void detach(); - - QString id_; - VFile_ptr file_; - bool used_; - QTime inTimeOut_; -}; - -class OutputCache: public QObject -{ - Q_OBJECT - -public: - OutputCache(QObject* parent=0); - ~OutputCache(); - - OutputCacheItem* add(VInfo_ptr info,const std::string& sourcePath,VFile_ptr file); - OutputCacheItem* attachOne(VInfo_ptr info,const std::string& fileName); - void detach(); - void clear(); - void print(); - -protected Q_SLOTS: - void slotTimeOut(); - -private: - OutputCache(const OutputClient&); - OutputCache& operator=(const OutputCache&); - void adjustTimer(); - void startTimer(); - void stopTimer(); - - QMap items_; - int timeOut_; - int maxAttachedPeriod_; - QTimer* timer_; -}; - -#endif // OUTPUTCHACHE_HPP - diff -Nru ecflow-4.9.0/Viewer/src/OutputClient.cpp ecflow-4.11.1/Viewer/src/OutputClient.cpp --- ecflow-4.9.0/Viewer/src/OutputClient.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputClient.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OutputClient.hpp" - -#include - -OutputClient::OutputClient(const std::string& host,const std::string& portStr,QObject* parent) : - QObject(parent), - soc_(NULL), - host_(host), - portStr_(portStr), - port_(19999), - timeout_(3000) -{ - if(!portStr_.empty()) - port_=atoi(portStr.c_str()); - - soc_=new QTcpSocket(0); - connect(soc_,SIGNAL(readyRead()), - this, SLOT(slotRead())); - - connect(soc_,SIGNAL(error(QAbstractSocket::SocketError)), - this,SLOT(slotError(QAbstractSocket::SocketError))); - - connect(soc_,SIGNAL(connected()), - this, SLOT(slotConnected())); - - if(char* timeoutStr = getenv ("ECFLOWVIEW_LOGTIMEOUT")) - timeout_ = atoi(timeoutStr)*1000; -} - -OutputClient::~OutputClient() -{ - soc_->abort(); -} - - -void OutputClient::connectToHost(std::string host,int port) -{ - stopper_.start(); - soc_->abort(); - soc_->connectToHost(QString::fromStdString(host),port); - - //We cannot change the timeout through the qt api so we need this hack. - QTimer::singleShot(timeout_, this, SLOT(slotCheckTimeout())); -} - -void OutputClient::slotCheckTimeout() -{ - if(soc_->state() == QAbstractSocket::HostLookupState || - soc_->state() == QAbstractSocket::ConnectingState) - { - soc_->abort(); - Q_EMIT error("Timeout error"); - } -} diff -Nru ecflow-4.9.0/Viewer/src/OutputClient.hpp ecflow-4.11.1/Viewer/src/OutputClient.hpp --- ecflow-4.9.0/Viewer/src/OutputClient.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputClient.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_OUTPUTCLIENT_HPP_ -#define VIEWER_SRC_OUTPUTCLIENT_HPP_ - -#include -#include -#include - -class QTcpSocket; - -class OutputClient : public QObject -{ - Q_OBJECT - -public: - OutputClient(const std::string& host,const std::string& port,QObject *parent); - ~OutputClient(); - - const std::string& host() const {return host_;} - int port() const {return port_;} - const std::string& portStr() const {return portStr_;} - - const std::string& remoteFile() const {return remoteFile_;} - bool ok() const { return soc_ != NULL; } - -protected Q_SLOTS: - virtual void slotError(QAbstractSocket::SocketError err)=0; - virtual void slotRead()=0; - virtual void slotConnected()=0; - void slotCheckTimeout(); - -Q_SIGNALS: - void error(QString); - void progress(QString,int); - void finished(); - -protected: - void connectToHost(std::string,int); - - QTcpSocket* soc_; - std::string host_; - std::string portStr_; - int port_; - int timeout_; - std::string remoteFile_; - QTime stopper_; - -private: - OutputClient(const OutputClient&); - OutputClient& operator=(const OutputClient&); -}; - - -#endif /* VIEWER_SRC_OUTPUTCLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputDirClient.cpp ecflow-4.11.1/Viewer/src/OutputDirClient.cpp --- ecflow-4.9.0/Viewer/src/OutputDirClient.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputDirClient.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OutputDirClient.hpp" - -#include "UiLog.hpp" - -#include -#include - -#define _UI_OUTPUTDIRCLIENT_DEBUG - -OutputDirClient::OutputDirClient(const std::string& host,const std::string& portStr,QObject* parent) : - OutputClient(host,portStr,parent) -{ -} - -VDir_ptr OutputDirClient::result() const -{ - return dir_; -} - -void OutputDirClient::slotCheckTimeout() -{ - if(soc_->state() == QAbstractSocket::HostLookupState || - soc_->state() == QAbstractSocket::ConnectingState) - { - soc_->abort(); - if(dir_) - dir_.reset(); - - Q_EMIT error("Timeout error"); - } -} - -void OutputDirClient::slotConnected() -{ - UiLog().dbg() << "OutputDirClient::slotConnected() connected to " << - soc_->peerName(); - - soc_->write("list ",5); - soc_->write(remoteFile_.c_str(),remoteFile_.size()); - soc_->write("\n",1); -} - -void OutputDirClient::slotError(QAbstractSocket::SocketError err) -{ -#ifdef _UI_OUTPUTDIRCLIENT_DEBUG - UiLog().dbg() << "OutputDirClient::slotError --> " << soc_->errorString(); -#endif - switch(err) - { - //The logserver does not notify us if the file trasfer finish. We simply get this error. - case QAbstractSocket::RemoteHostClosedError: - -#ifdef _UI_OUTPUTDIRCLIENT_DEBUG - UiLog().dbg() << " RemoteHostClosedError "; -#endif - //If no data was transferred we think it is a real error. - if(data_.isEmpty()) - { -#ifdef _UI_OUTPUTDIRCLIENT_DEBUG - UiLog().dbg() << " --> data is empty: file transfer failed"; -#endif - break; - } - //If there is some data we think the transfer succeeded. - else - { -#ifdef _UI_OUTPUTDIRCLIENT_DEBUG - UiLog().dbg() << " --> has data: file transfer succeeded"; -#endif - if(dir_) - { - parseData(); - } - - Q_EMIT finished(); - - if(dir_) - dir_.reset(); - - return; - } - - break; - case QAbstractSocket::UnknownSocketError: - if(soc_->state() != QAbstractSocket::ConnectedState) - { - break; - } - break; - default: - break; - } - - soc_->abort(); - if(dir_) - { - dir_.reset(); - } - Q_EMIT error(soc_->errorString()); -} - -void OutputDirClient::getDir(const std::string& name) -{ - connectToHost(host_,port_); - - boost::filesystem::path fp(name); - std::string dirName=fp.parent_path().string(); - - remoteFile_=name; - dir_.reset(); - dir_=VDir_ptr(new VDir(dirName)); - data_.clear(); - - //indicates the source of the files - dir_->where(host_ + "@" + portStr_); -} - -void OutputDirClient::slotRead() -{ - const qint64 size = 64*1024; - char buf[size+1]; - quint64 len = 0; - - while((len = soc_->read(buf,size)) > 0) - { - data_.append(buf,len); - } -} - -void OutputDirClient::parseData() -{ - if(!dir_) - return; - - QTextStream in(data_); - while(!in.atEnd()) - { - int mode,uid,gid; - unsigned int size; - unsigned int atime,mtime,ctime; - QString name; - - in >> mode >> uid >> gid >> size >> atime >> mtime >> ctime >> name; - - if(!name.isEmpty()) - { - boost::filesystem::path p(name.toStdString()); - std::string fileDirName=p.parent_path().string(); - std::string fileName=p.leaf().string(); - - //Adjust the path in the dir - if(dir_->path() != fileDirName) - { - dir_->path(fileDirName,false); - } - - dir_->addItem(fileName,size,mtime); - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/OutputDirClient.hpp ecflow-4.11.1/Viewer/src/OutputDirClient.hpp --- ecflow-4.9.0/Viewer/src/OutputDirClient.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputDirClient.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,46 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ -#define VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ - -#include "OutputClient.hpp" -#include "VDir.hpp" - -#include - -class OutputDirClient : public OutputClient -{ - Q_OBJECT - -public: - OutputDirClient(const std::string& host,const std::string& port,QObject *parent); - - VDir_ptr result() const; - void getDir(const std::string& name); - -protected Q_SLOTS: - void slotError(QAbstractSocket::SocketError err); - void slotRead(); - void slotConnected(); - void slotCheckTimeout(); - -private: - OutputDirClient(const OutputDirClient&); - OutputDirClient& operator=(const OutputDirClient&); - - void parseData(); - - VDir_ptr dir_; - QByteArray data_; -}; - - -#endif /* VIEWER_SRC_OUTPUTDIRCLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputDirProvider.cpp ecflow-4.11.1/Viewer/src/OutputDirProvider.cpp --- ecflow-4.9.0/Viewer/src/OutputDirProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputDirProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,399 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OutputDirProvider.hpp" - -#include "OutputDirClient.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" - -#include -#include -#include - -OutputDirProvider::OutputDirProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::NoTask), - outClient_(NULL), - currentTask_(-1) -{ -} - -void OutputDirProvider::clear() -{ - if(outClient_) - { - delete outClient_; - outClient_=NULL; - } - - queue_.clear(); - currentTask_=-1; - InfoProvider::clear(); -} - -//Node -void OutputDirProvider::visit(VInfoNode* info) -{ - //Reset the reply - reply_->reset(); - - //Clear the queue - queue_.clear(); - currentTask_=-1; - - if(!info) - { - owner_->infoFailed(reply_); - return; - } - - ServerHandler* server=info_->server(); - VNode *n=info->node(); - - if(!server || !n || !n->node()) - { - owner_->infoFailed(reply_); - return; - } - - std::string joboutFile=n->findVariable("ECF_JOBOUT",true); - queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::RemoteFetch); - - //With the readFromdisk option we always try read locally - if(server->readFromDisk()) - { - queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::LocalFetch); - } - //Otherwise we only read the local disk if the remote access (ie logserver) failed - else - { - queue_ << OutputDirProviderTask(joboutFile,OutputDirProviderTask::LocalFetch, - OutputDirProviderTask::RunIfPrevFailed); - } - - std::string outFile = n->findVariable("ECF_OUT",true); - std::string jobFile = n->findVariable("ECF_JOB",true); - if(!outFile.empty() && !jobFile.empty()) - { - queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::RemoteFetch); - - //With the readFromdisk option we always try read locally - if(server->readFromDisk()) - { - queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::LocalFetch); - } - //Otherwise we only read the local disk if the remote access (ie logserver) failed - else - { - queue_ << OutputDirProviderTask(jobFile,OutputDirProviderTask::LocalFetch, - OutputDirProviderTask::RunIfPrevFailed); - } - } - - //Start fetching the dirs - fetchNext(); -} - -void OutputDirProvider::fetchIgnored() -{ - if(hasNext()) - fetchNext(); - else - completed(); -} - -void OutputDirProvider::fetchFinished(VDir_ptr dir,QString msg) -{ - Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); - OutputDirProviderTask task=queue_[currentTask_]; - queue_[currentTask_].dir_=dir; - queue_[currentTask_].status_=OutputDirProviderTask::FinishedStatus; - - if(hasNext()) - fetchNext(); - else - completed(); -} - -void OutputDirProvider::fetchFailed(QString msg) -{ - Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); - OutputDirProviderTask task=queue_[currentTask_]; - queue_[currentTask_].status_=OutputDirProviderTask::FailedStatus; - queue_[currentTask_].error_=msg; - - if(hasNext()) - fetchNext(); - else - completed(); -} - -void OutputDirProvider::failed(QString msg) -{ - queue_.clear(); - currentTask_=-1; - - reply_->setInfoText(""); - reply_->setErrorText(msg.toStdString()); - owner_->infoFailed(reply_); -} - -void OutputDirProvider::completed() -{ - bool hasAllFailed=true; - std::vector dirVec; - Q_FOREACH(OutputDirProviderTask task,queue_) - { - if(task.dir_) - dirVec.push_back(task.dir_); - - if(!task.error_.isEmpty()) - reply_->appendErrorText(task.error_.toStdString()); - - if(task.status_ != OutputDirProviderTask::FailedStatus) - hasAllFailed=false; - } - - //clear the queue - queue_.clear(); - currentTask_=-1; - - //Notify owner - reply_->setDirectories(dirVec); - owner_->infoReady(reply_); - - reply_->setInfoText(""); - reply_->setDirectories(dirVec); - if(!hasAllFailed) - owner_->infoReady(reply_); - else - owner_->infoFailed(reply_); -} - -bool OutputDirProvider::hasNext() const -{ - return currentTask_ < queue_.count()-1; -} - -void OutputDirProvider::fetchNext() -{ - //point to the next task - currentTask_++; - - Q_ASSERT(currentTask_ >=0 && currentTask_ < queue_.count()); - if(currentTask_ >= queue_.count()) - return completed(); - - VDir_ptr dir; - if(!info_ || !info_->isNode() || !info_->node() || !info_->node()->node()) - { - failed("Node not found"); //fatal error - return; - } - - ServerHandler* server=info_->server(); - VNode *n=info_->node(); - if(!server || !n || !n->node()) - { - failed("Node not found"); //fatal error - return; - } - - //Get the current task - OutputDirProviderTask task=queue_[currentTask_]; - //Check conditions - if(currentTask_ > 0) - { - OutputDirProviderTask prevTask=queue_[currentTask_-1]; - if(task.condition_ == OutputDirProviderTask::RunIfPrevFailed && - prevTask.status_ != OutputDirProviderTask::FailedStatus) - { - fetchIgnored(); //we simply skip this task - return; - } - - } - - //Get the file name - std::string fileName=task.path_; - - //Jobout is empty: no dir path is availabale - if(fileName.empty()) - { - fetchFailed("Directory path is empty"); - return; - } - - //We try the output client, its asynchronous! Here we create an - //output client and ask to the fetch the dir asynchronously. The ouput client will call - //slotOutputClientFinished() or slotOutputClientError() eventually!! - if(task.fetchMode_ == OutputDirProviderTask::RemoteFetch) - { - if(!fetchDirViaOutputClient(n,fileName)) - { - //no logserver is defined - fetchIgnored(); - } - return; - } - - //We try to read it from disk - if(task.fetchMode_ == OutputDirProviderTask::LocalFetch) - { - //If there is no output client we try to read it from disk - std::string errStr; - dir=fetchLocalDir(fileName,errStr); - if(dir) - { - fetchFinished(dir); - } - else - { - fetchFailed(QString::fromStdString(errStr)); - } - return; - } - - //If we are here the error or warning is already set in reply - fetchFailed(); -} - -bool OutputDirProvider::fetchDirViaOutputClient(VNode *n,const std::string& fileName) -{ - std::string host, port; - if(n->logServer(host,port)) - { - outClient_=makeOutputClient(host,port); - outClient_->getDir(fileName); - return true; - } - - return false; -} - -void OutputDirProvider::slotOutputClientFinished() -{ - if(!info_ || queue_.isEmpty()) - return; - - Q_ASSERT(outClient_); - VDir_ptr dir = outClient_->result(); - if(dir) - { - dir->setFetchMode(VDir::LogServerFetchMode); - std::string method="served by " + outClient_->host() + "@" + outClient_->portStr(); - dir->setFetchModeStr(method); - dir->setFetchDate(QDateTime::currentDateTime()); - fetchFinished(dir); - return; - } - - fetchFailed(); -} - -void OutputDirProvider::slotOutputClientProgress(QString,int) -{ -} - -void OutputDirProvider::slotOutputClientError(QString msg) -{ - if(!info_ || queue_.isEmpty()) - return; - - QString sDesc; - if(outClient_) - { - sDesc="Failed to fetch from " + QString::fromStdString(outClient_->host()) + - "@" + QString::fromStdString(outClient_->portStr()); - if(!msg.isEmpty()) - sDesc+=" error: " + msg; - - } - else - { - sDesc="Failed to fetch from logserver"; - if(!msg.isEmpty()) - sDesc+=": " + msg; - } - - fetchFailed(sDesc); -} - -VDir_ptr OutputDirProvider::fetchLocalDir(const std::string& path,std::string& errorStr) -{ - VDir_ptr res; - - boost::filesystem::path p(path); - - //Is it a directory? - boost::system::error_code errorCode; - if(boost::filesystem::is_directory(p,errorCode)) - { - return res; - } - - try - { - if(boost::filesystem::exists(p.parent_path())) - { - std::string dirName=p.parent_path().string(); - if(info_ && info_->isNode() && info_->node()) - { - std::string nodeName=info_->node()->strName(); - std::string pattern=nodeName+"."; - res=VDir_ptr(new VDir(dirName,pattern)); - res->setFetchDate(QDateTime::currentDateTime()); - res->setFetchMode(VDir::LocalFetchMode); - res->setFetchModeStr("from disk"); - return res; - } - } - - errorStr="No access to path on disk!"; - return res; - } - catch (const boost::filesystem::filesystem_error& e) - { - errorStr="No access to path on disk! error: " + std::string(e.what()); - UiLog().warn() << "fetchLocalDir failed:" << std::string(e.what()); - } - - return res; -} - -OutputDirClient* OutputDirProvider::makeOutputClient(const std::string& host,const std::string& port) -{ - if(outClient_) - { - if(outClient_->host() != host || outClient_->portStr() != port) - { - delete outClient_; - outClient_=0; - } - } - - if(!outClient_) - { - outClient_=new OutputDirClient(host,port,this); - - connect(outClient_,SIGNAL(error(QString)), - this,SLOT(slotOutputClientError(QString))); - - connect(outClient_,SIGNAL(progress(QString,int)), - this,SLOT(slotOutputClientProgress(QString,int))); - - connect(outClient_,SIGNAL(finished()), - this,SLOT(slotOutputClientFinished())); - } - - return outClient_; -} diff -Nru ecflow-4.9.0/Viewer/src/OutputDirProvider.hpp ecflow-4.11.1/Viewer/src/OutputDirProvider.hpp --- ecflow-4.9.0/Viewer/src/OutputDirProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputDirProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,77 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ -#define VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ - -#include - -#include "VDir.hpp" -#include "VInfo.hpp" -#include "InfoProvider.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -class OutputDirClient; - -class OutputDirProviderTask -{ -public: - enum FetchMode {LocalFetch,RemoteFetch}; - enum Status {UnkownStatus,FinishedStatus,FailedStatus}; - enum Condition {NoCondition,RunIfPrevFailed}; - - OutputDirProviderTask(const std::string& path,FetchMode fetchMode,Condition cond=NoCondition) : - path_(path), fetchMode_(fetchMode), condition_(cond), status_(UnkownStatus) {} - - std::string path_; - VDir_ptr dir_; - QString error_; - FetchMode fetchMode_; - Condition condition_; - Status status_; -}; - - -class OutputDirProvider : public QObject, public InfoProvider -{ -Q_OBJECT - -public: - explicit OutputDirProvider(InfoPresenter* owner); - - void visit(VInfoNode*); - void clear(); - -private Q_SLOTS: - void slotOutputClientError(QString); - void slotOutputClientProgress(QString,int); - void slotOutputClientFinished(); - -private: - bool hasNext() const; - void fetchNext(); - void fetchIgnored(); - void fetchFinished(VDir_ptr,QString msg=QString()); - void fetchFailed(QString msg=QString()); - void failed(QString); - void completed(); - - bool fetchDirViaOutputClient(VNode *n,const std::string& fileName); - VDir_ptr fetchLocalDir(const std::string& path,std::string& errorStr); - OutputDirClient* makeOutputClient(const std::string& host,const std::string& port); - - OutputDirClient *outClient_; - QList queue_; - int currentTask_; -}; - - -#endif /* VIEWER_SRC_OUTPUTDIRPROVIDER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputFetchInfo.cpp ecflow-4.11.1/Viewer/src/OutputFetchInfo.cpp --- ecflow-4.9.0/Viewer/src/OutputFetchInfo.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFetchInfo.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,192 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OutputFetchInfo.hpp" - -#include -#include -#include -#include -#include - -#include - -#include "ServerHandler.hpp" - -OutputFetchInfo::OutputFetchInfo(QWidget* parent) : QWidget(parent) -{ - QVBoxLayout *vb=new QVBoxLayout(this); - label_=new QLabel(this); - label_->setText("Additional information"); - - te_=new QTextEdit(this); - te_->setReadOnly(true); - te_->setMinimumWidth(350); - - vb->addWidget(label_); - vb->addWidget(te_,1); -} - -void OutputFetchInfo::setInfo(VReply *reply,VInfo_ptr info) -{ - Q_ASSERT(reply); - - te_->clear(); - - static QMap nums; - if(nums.isEmpty()) - { - nums[1]="1st"; - nums[2]="2nd"; - nums[3]="3rd"; - nums[4]="4th"; - } - - QStringList options; - QStringList remarks; - QStringList msg; - QStringList tries; - QStringList other; - QString alg; - - QString html; - - int cnt=1; - for(std::vector::const_iterator it=reply->log().begin(); it != reply->log().end(); ++it) - { - QString s=QString::fromStdString(*it); - if(s.startsWith("REMARK>")) - { - remarks << s.remove(0,7); - continue; - } - else if(s.startsWith("OPTION>")) - { - options << s.remove(0,7); - continue; - } - else if(s.startsWith("MSG>")) - { - msg << s.remove(0,4); - continue; - } - else if(s.startsWith("TRY>")) - { - s.remove(0,4); - s.prepend("tried to "); - s.replace(" OK"," SUCCEEDED"); - s.replace(" FAILED"," FAILED"); - s.replace(" NO ACCESS"," NO ACCESS"); - s.replace(" NOT DEFINED"," NOT DEFINED"); - tries << s; - cnt++; - continue; - } - else - other << s; - } - - if(info && info->server()) - { - ServerHandler* server=info->server(); - bool rfd=server->readFromDisk(); - QString t; - - t="The following are tried in order:
          "; - - if(rfd) - { - t+="
        • Try to read the output files from the logserver \ - (if defined)
        • from disk
        • \ - through the ecflow server (if the current job output)
        • "; - } - else - { - t+="
        • Try to read the output files from the logserver \ - (if defined)
        • from disk (if not the current job output)
        • \ -
        • from the ecflow server (if the current job output)
        • "; - } - t+="
        (To change this behaviour go Tools -> Preferences -> Server settings -> Fetching files)"; - - alg=t; - - if(reply->tmpFile() && reply->fileReadMode() == VReply::LocalReadMode && - !server->isLocalHost()) - { - remarks << "The output file was read from disk but the server (" + - QString::fromStdString(server->host()) + - ") is not running on the local machine. If the path is machine-specific (e.g. /tmp) \ - and there exists a file with the same path on the local machine, then\ - this will have been read instead."; - } - } - - if(!msg.isEmpty()) - { - html+="

        Messages

        "; - html+=buildList(msg); - } - - if(!options.isEmpty()) - { - html+="

        Options

        "; - html+=buildList(options); - } - - if(!tries.isEmpty()) - { - html+="

        How was this file fetched?

        "; - html+=buildList(tries,true); - } - - if(!alg.isEmpty()) - { - html+="

        Algorithm:

        "+alg; - } - - if(!remarks.isEmpty()) - { - html+="

        Remarks

        "; - html+=buildList(remarks); - } - - if(!other.isEmpty()) - { - html+=buildList(other); - - } - - te_->setHtml(html); - - QTextCursor cursor=te_->textCursor(); - cursor.movePosition(QTextCursor::Start); - te_->setTextCursor(cursor); -} - -QString OutputFetchInfo::buildList(QStringList lst,bool ordered) -{ - QString t; - - if(lst.count() > 0) - { - t+=(ordered)?"
          ":"
            "; - Q_FOREACH(QString s,lst) - { - t+="
          • " + s + "
          • "; - } - t+=(ordered)?"
        ":"
      "; - } - - return t; -} - -void OutputFetchInfo::clearInfo() -{ - te_->clear(); -} diff -Nru ecflow-4.9.0/Viewer/src/OutputFetchInfo.hpp ecflow-4.11.1/Viewer/src/OutputFetchInfo.hpp --- ecflow-4.9.0/Viewer/src/OutputFetchInfo.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFetchInfo.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ -#ifndef OUTPUTFETCHINFO_HPP -#define OUTPUTFETCHINFO_HPP - -#include - -#include "VInfo.hpp" -#include "VReply.hpp" - -class QLabel; -class QTextEdit; - -class OutputFetchInfo : public QWidget -{ -public: - OutputFetchInfo(QWidget* parent=0); - void setInfo(VReply*,VInfo_ptr); - void clearInfo(); - -private: - QString buildList(QStringList,bool ordered=false); - - QLabel* label_; - QTextEdit* te_; - -}; - -#endif // OUTPUTFETCHINFO_HPP - diff -Nru ecflow-4.9.0/Viewer/src/OutputFileClient.cpp ecflow-4.11.1/Viewer/src/OutputFileClient.cpp --- ecflow-4.9.0/Viewer/src/OutputFileClient.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFileClient.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,198 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "OutputFileClient.hpp" - -#include "UiLog.hpp" - -#define _UI_OUTPUTFILECLIENT_DEBUG - -OutputFileClient::OutputFileClient(const std::string& host,const std::string& portStr,QObject* parent) : - OutputClient(host,portStr,parent), - total_(0), - expected_(0), - lastProgress_(0), - progressUnits_("MB"), - progressChunk_(1024*1024) -{ -} - -void OutputFileClient::clearResult() -{ - if(out_) - { - out_->close(); - out_.reset(); - } -} - -void OutputFileClient::slotConnected() -{ - soc_->write("get ",4); - soc_->write(remoteFile_.c_str(),remoteFile_.size()); - soc_->write("\n",1); -} - -void OutputFileClient::slotError(QAbstractSocket::SocketError err) -{ - switch(err) - { - case QAbstractSocket::RemoteHostClosedError: - - if(total_ == 0) - { - break; - } - //Probably there was no error and the connection was closed because all - //the data was transferred. Unfortunately the logserver does not send anything - //at the end of the data transfer. - else - { - soc_->abort(); - if(out_) - { - out_->setTransferDuration(stopper_.elapsed()); - out_->setFetchDate(QDateTime::currentDateTime()); - out_->close(); - } - - Q_EMIT finished(); - - if(out_) - out_.reset(); - - return; - - } - - break; - case QAbstractSocket::UnknownSocketError: - if(soc_->state() != QAbstractSocket::ConnectedState) - { - break; - } - break; - default: - break; - } - - soc_->abort(); - if(out_) - { - out_->close(); - out_.reset(); - } - Q_EMIT error(soc_->errorString()); - -} - -void OutputFileClient::getFile(const std::string& name) -{ - connectToHost(host_,port_); - - remoteFile_=name; - out_.reset(); - out_=VFile_ptr(VFile::create(true)); //we will delete the file from disk - out_->setSourcePath(name); - total_=0; - lastProgress_=0; - estimateExpectedSize(); -} - -void OutputFileClient::slotRead() -{ - const qint64 size = 64*1024; - char buf[size]; - quint64 len = 0; - - while((len = soc_->read(buf,size)) > 0) - { - std::string err; - if(!out_->write(buf,len,err)) - { - soc_->abort(); - Q_EMIT error("write failed"); - } - total_ += len; - - if(total_/progressChunk_ > lastProgress_) - { - lastProgress_=total_/progressChunk_; - - int prog=0; - if(expected_ > 0) - { - prog=static_cast(100.*static_cast(total_)/static_cast(expected_)); - if(prog>100) prog=100; - Q_EMIT progress(QString::number(lastProgress_) + "/" + QString::number(expected_/progressChunk_) + - " " + progressUnits_ + " transferred",prog); - } - else - Q_EMIT progress(QString::number(lastProgress_) + " " + progressUnits_ + " transferred",prog); - } - } -} - -VFile_ptr OutputFileClient::result() const -{ - return out_; -} - -void OutputFileClient::setExpectedSize(qint64 v) -{ - expected_=v; -} - -int OutputFileClient::maxProgress() const -{ - return expected_/progressChunk_; -} - -void OutputFileClient::setDir(VDir_ptr dir) -{ - if(dir_ && dir && dir_.get() == dir.get()) - return; - - dir_=dir; - //if(expected_ == 0) - estimateExpectedSize(); -} - -void OutputFileClient::estimateExpectedSize() -{ - if(!dir_) - { - expected_=0; - return; - } - -#ifdef _UI_OUTPUTFILECLIENT_DEBUG - UiLog().dbg() << "OutputFileClient::estimateExpectedSize -->"; -#endif - for(int i=0; i < dir_->count(); i++) - { -#ifdef _UI_OUTPUTFILECLIENT_DEBUG - UiLog().dbg() << "file: " << dir_->fullName(i); -#endif - if(dir_->fullName(i) == remoteFile_) - { - expected_=dir_->items().at(i)->size_; -#ifdef _UI_OUTPUTFILECLIENT_DEBUG - UiLog().dbg() << " expected size=" << expected_; -#endif - return; - } - } - - expected_=0; -#ifdef _UI_OUTPUTFILECLIENT_DEBUG - UiLog().dbg() << " expected size=" << QString::number(expected_); -#endif -} diff -Nru ecflow-4.9.0/Viewer/src/OutputFileClient.hpp ecflow-4.11.1/Viewer/src/OutputFileClient.hpp --- ecflow-4.9.0/Viewer/src/OutputFileClient.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFileClient.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_OUTPUTFILECLIENT_HPP_ -#define VIEWER_SRC_OUTPUTFILECLIENT_HPP_ - -#include "OutputClient.hpp" -#include "VDir.hpp" -#include "VFile.hpp" - -class OutputFileClient : public OutputClient -{ - Q_OBJECT - -public: - OutputFileClient(const std::string& host,const std::string& port,QObject *parent); - - VFile_ptr result() const; - void clearResult(); - void getFile(const std::string& name); - void setExpectedSize(qint64 v); - int maxProgress() const; - void setDir(VDir_ptr); - -protected Q_SLOTS: - void slotError(QAbstractSocket::SocketError err); - void slotRead(); - void slotConnected(); - -private: - OutputFileClient(const OutputClient&); - OutputFileClient& operator=(const OutputClient&); - void estimateExpectedSize(); - - qint64 total_; - qint64 expected_; - VFile_ptr out_; - VDir_ptr dir_; - qint64 lastProgress_; - const QString progressUnits_; - const qint64 progressChunk_; -}; - -#endif /* VIEWER_SRC_OUTPUTFILECLIENT_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/OutputFileProvider.cpp ecflow-4.11.1/Viewer/src/OutputFileProvider.cpp --- ecflow-4.9.0/Viewer/src/OutputFileProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFileProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,472 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OutputFileProvider.hpp" - -#include "OutputCache.hpp" -#include "OutputFileClient.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" - -#include - -#include -#include -#include - -OutputFileProvider::OutputFileProvider(InfoPresenter* owner) : - InfoProvider(owner,VTask::OutputTask), - outClient_(NULL) -{ - outCache_=new OutputCache(this); -} - -void OutputFileProvider::clear() -{ - //Detach all the outputs registered for this instance in cache - outCache_->detach(); - - if(outClient_) - { - delete outClient_; - outClient_=NULL; - } - InfoProvider::clear(); - - dirs_.clear(); -} - -//This is called when we load a new node in the Output panel. In this -//case we always try to load the current jobout file. -void OutputFileProvider::visit(VInfoNode* infoNode) -{ - assert(info_->node() == infoNode->node()); - - //Reset the reply - reply_->reset(); - - if(!info_) - { - owner_->infoFailed(reply_); - return; - } - - ServerHandler* server=info_->server(); - VNode *n=infoNode->node(); - - if(!n || !n->node()) - { - owner_->infoFailed(reply_); - return; - } - - //Get the filename - std::string jobout=joboutFileName(); - - //We always try to use the cache in this case - outCache_->attachOne(info_,jobout); - fetchFile(server,n,jobout,true,true); -} - -//Get a file -void OutputFileProvider::file(const std::string& fileName,bool useCache) -{ - //If we do not want to use the cache we detach all the output - //attached to this instance - if(!useCache) - outCache_->detach(); - - //Check if the task is already running - if(task_) - { - task_->status(VTask::CANCELLED); - task_.reset(); - } - - //Reset the reply - reply_->reset(); - - if(!info_->isNode() || !info_->node() || !info_->node()->node()) - { - owner_->infoFailed(reply_); - return; - } - - ServerHandler* server=info_->server(); - VNode *n=info_->node(); - - //Get the filename - std::string jobout=joboutFileName(); //n->findVariable("ECF_JOBOUT",true); - - fetchFile(server,n,fileName,(fileName==jobout),useCache); -} - -void OutputFileProvider::fetchFile(ServerHandler *server,VNode *n,const std::string& fileName,bool isJobout,bool useCache) -{ - //If we do not want to use the cache we detach all the output - //attached to this instance - if(!useCache) - outCache_->detach(); - - if(!n || !n->node() || !server) - { - owner_->infoFailed(reply_); - return; - } - - //Set the filename in reply - reply_->fileName(fileName); - - //No filename is available - if(fileName.empty()) - { - //Joubout variable is not defined or empty - if(isJobout) - { - reply_->setErrorText("Variable ECF_JOBOUT is not defined!"); - reply_->addLog("MSG>Variable ECF_JOBOUT is not defined!."); - owner_->infoFailed(reply_); - } - else - { - reply_->setErrorText("Output file is not defined!"); - reply_->addLog("MSG>Output file is not defined."); - owner_->infoFailed(reply_); - } - - return; - } - - //Check if tryno is 0. ie. the file is the current jobout file and ECF_TRYNO = 0 - if(isTryNoZero(fileName)) - { - reply_->setInfoText("Current job output does not exist yet (TRYNO is 0)!)"); - reply_->addLog("MSG>Current job output does not exist yet (TRYNO is 0)!"); - owner_->infoReady(reply_); - return; - } - - //---------------------------------- - // The host is the localhost - //---------------------------------- - - if(isJobout) - { - if(n->isSubmitted()) - reply_->addLog("REMARK>This file is the current job output (defined by variable ECF_JOBOUT), but \ - because the node status is submitted it may contain the ouput from a previous run!"); - else - reply_->addLog("REMARK>This file is the current job output (defined by variable ECF_JOBOUT)."); - } - else - reply_->addLog("REMARK>This file is not the current job output (defined by ECF_JOBOUT)."); - -#if 0 - if(server->readFromDisk()) - { - if(server->isLocalHost()) - { - //We try to read the file directly from the disk - if(server->readFromDisk()) - { - if(fetchLocalFile(fileName)) - return; - } - } -#endif - -#if 0 - //if(server->isLocalHost()) - // { - //We try to read the file directly from the disk - if(server->readFromDisk()) - { - if(fetchLocalFile(fileName)) - return; - } - //} - -#endif - - - //We try the output client (aka logserver), its asynchronous! - if(fetchFileViaOutputClient(n,fileName,useCache)) - { - //If we are here we created an output client and asked it to the fetch the - //file asynchronously. The ouput client will call slotOutputClientFinished() or - //slotOutputClientError eventually!! - return; - } - - //If we are here there is no outpt client defined - reply_->addLog("TRY>fetch file from logserver: NOT DEFINED"); - - //If there is no output client we try - //to read it from the disk (if the settings allow it) - if(server->readFromDisk() || !isJobout) - { - //Get the fileName - if(fetchLocalFile(fileName)) - return; - } - - //If we are here no output client is defined and we could not read the file from - //the local disk, so we try the server if it is the jobout file. - if(isJobout) - { - fetchJoboutViaServer(server,n,fileName); - return; - } - - //If we are here we coud not get the file - if(n->isFlagSet(ecf::Flag::JOBCMD_FAILED)) - { - reply_->setErrorText("Submission command failed! Check .sub file, ssh, or queueing system error."); - } - owner_->infoFailed(reply_); -} - -bool OutputFileProvider::fetchFileViaOutputClient(VNode *n,const std::string& fileName,bool useCache) -{ - UI_FUNCTION_LOG - UiLog().dbg() << "OutputFileProvider::fetchFileViaOutputClient <-- file: " << fileName; - - std::string host, port; - assert(n); - - //We try use the cache - if(useCache) - { - //Check if the given output is already in the cache - if(OutputCacheItem* item=outCache_->attachOne(info_,fileName)) - { - VFile_ptr f=item->file(); - assert(f); - f->setCached(true); - - UiLog().dbg() << " File found in cache"; - - reply_->setInfoText(""); - reply_->fileReadMode(VReply::LogServerReadMode); - - reply_->setLog(f->log()); - reply_->addLog("REMARK>File was read from cache."); - - reply_->tmpFile(f); - owner_->infoReady(reply_); - return true; - } - } - - //We did/could not use the cache - if(n->logServer(host,port)) - { - //host=host + "baaad"; - - UiLog().dbg() << "OutputFileProvider::fetchFileViaOutputClient --> host:" << host << - " port:" << port << " file: " << fileName; - - //reply_->setInfoText("Getting file through log server: " + host + "@" + port); - //owner_->infoProgress(reply_); - owner_->infoProgressStart("Getting file " + fileName + " from log server " + host + "@" + port +"",0); - - if(!outClient_) - { - outClient_=new OutputFileClient(host,port,this); - - connect(outClient_,SIGNAL(error(QString)), - this,SLOT(slotOutputClientError(QString))); - - connect(outClient_,SIGNAL(progress(QString,int)), - this,SLOT(slotOutputClientProgress(QString,int))); - - connect(outClient_,SIGNAL(finished()), - this,SLOT(slotOutputClientFinished())); - } - - VDir_ptr dir=dirToFile(fileName); - outClient_->setDir(dir); - outClient_->getFile(fileName); - - return true; - } - - return false; -} - -void OutputFileProvider::slotOutputClientFinished() -{ - VFile_ptr tmp = outClient_->result(); - assert(tmp); - - outClient_->clearResult(); - - //Files retrieved from the log server are automatically added to the cache! - outCache_->add(info_,tmp->sourcePath(),tmp); - - reply_->setInfoText(""); - reply_->fileReadMode(VReply::LogServerReadMode); - reply_->addLog("TRY> fetch file from logserver: " + outClient_->host() + "@" + outClient_->portStr() + ": OK"); - - tmp->setFetchMode(VFile::LogServerFetchMode); - tmp->setLog(reply_->log()); - std::string method="served by " + outClient_->host() + "@" + outClient_->portStr(); - tmp->setFetchModeStr(method); - - reply_->tmpFile(tmp); - owner_->infoReady(reply_); -} - -void OutputFileProvider::slotOutputClientProgress(QString msg,int value) -{ - //UiLog().dbg() << "OutputFileProvider::slotOutputClientProgress " << msg; - - owner_->infoProgress(msg.toStdString(),value); - - //reply_->setInfoText(msg.toStdString()); - //owner_->infoProgress(reply_); - //reply_->setInfoText(""); -} - -void OutputFileProvider::slotOutputClientError(QString msg) -{ - UiLog().dbg() << "OutputFileProvider::slotOutputClientError error:" << msg; - reply_->addLog("TRY> fetch file from logserver: " + outClient_->host() + "@" + outClient_->portStr() + " FAILED"); - - if(info_) - { - ServerHandler* server=info_->server(); - VNode *n=info_->node(); - - if(server && n) - { - std::string jobout=n->findVariable("ECF_JOBOUT",true); - bool isJobout=(outClient_->remoteFile() == jobout); - - //We try to read the file directly from the disk - if(server->readFromDisk() || !isJobout) - { - if(fetchLocalFile(outClient_->remoteFile())) - return; - } - - //Then we try the server - if(isJobout) - { - fetchJoboutViaServer(server,n,jobout); - return; - } - } - } - - reply_->setErrorText("Failed to fetch file from logserver " + - outClient_->host() + "@" + outClient_->portStr() + - "Error: " + msg.toStdString()); - owner_->infoFailed(reply_); -} - -void OutputFileProvider::fetchJoboutViaServer(ServerHandler *server,VNode *n,const std::string& fileName) -{ - assert(server); - assert(n); - - //Define a task for getting the info from the server. - task_=VTask::create(taskType_,n,this); - - task_->reply()->fileReadMode(VReply::ServerReadMode); - task_->reply()->fileName(fileName); - task_->reply()->setLog(reply_->log()); - - //owner_->infoProgressStart("Getting file " + fileName + " from server",0); - - //Run the task in the server. When it finish taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - server->run(task_); -} - -bool OutputFileProvider::fetchLocalFile(const std::string& fileName) -{ - //we do not want to delete the file once the VFile object is destroyed!! - VFile_ptr f(VFile::create(fileName,false)); - if(f->exists()) - { - reply_->fileReadMode(VReply::LocalReadMode); - reply_->addLog("TRY> read file from disk: OK"); - - f->setSourcePath(f->path()); - f->setFetchMode(VFile::LocalFetchMode); - f->setFetchDate(QDateTime::currentDateTime()); - f->setLog(reply_->log()); - - reply_->tmpFile(f); - owner_->infoReady(reply_); - return true; - } - reply_->addLog("TRY> read file from disk: NO ACCESS"); - return false; -} - - -std::string OutputFileProvider::joboutFileName() const -{ - if(info_ && info_->isNode() && info_->node() && info_->node()->node()) - { - return info_->node()->findVariable("ECF_JOBOUT",true); - } - - return std::string(); -} - -//Returns true if -// -the file is the current jobout -// -id contains the string ".0" -// -ECF_TRYNO = 0 - -bool OutputFileProvider::isTryNoZero(const std::string& filename) const -{ - if(filename.find(".0") != std::string::npos && - joboutFileName() == filename && - info_ && info_->isNode() && info_->node() && info_->node()->node()) - { - return (info_->node()->findVariable("ECF_TRYNO",true) == "0"); - } - return false; -} - -//We use directories to figure out the size of the file to be transfered -void OutputFileProvider::setDirectories(const std::vector& dirs) -{ -#if 0 - if(outClient_) - outClient_->setDir(dir); - - if(dir != dir_) - dir_=dir; -#endif - - dirs_=dirs; -} - -VDir_ptr OutputFileProvider::dirToFile(const std::string& fileName) const -{ - VDir_ptr dir; - - if(fileName.empty()) - return dir; - - for(std::size_t i=0; i < dirs_.size(); i++) - { - if(dirs_[i] && fileName.find(dirs_[i]->path()) == 0) - return dirs_[i]; - - } - return dir; -} diff -Nru ecflow-4.9.0/Viewer/src/OutputFileProvider.hpp ecflow-4.11.1/Viewer/src/OutputFileProvider.hpp --- ecflow-4.9.0/Viewer/src/OutputFileProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputFileProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef OUTPUTFILEPROVIDER_HPP_ -#define OUTPUTFILEPROVIDER_HPP_ - -#include - -#include "VDir.hpp" -#include "VInfo.hpp" -#include "InfoProvider.hpp" -#include "VTask.hpp" -#include "VTaskObserver.hpp" - -class OutputFileClient; -class OutputCache; - -class OutputFileProvider : public QObject, public InfoProvider -{ -Q_OBJECT - -public: - explicit OutputFileProvider(InfoPresenter* owner); - - void visit(VInfoNode*); - void clear(); - - //Get a particular jobout file - void file(const std::string& fileName,bool useCache=true); - void setDirectories(const std::vector&); - - std::string joboutFileName() const; - bool isTryNoZero(const std::string& fileName) const; - -private Q_SLOTS: - void slotOutputClientError(QString); - void slotOutputClientProgress(QString,int); - void slotOutputClientFinished(); - -private: - VDir_ptr dirToFile(const std::string& fileName) const; - void fetchFile(ServerHandler *server,VNode *n,const std::string& fileName,bool isJobout,bool detachCache); - void fetchJoboutViaServer(ServerHandler *server,VNode *n,const std::string&); - bool fetchFileViaOutputClient(VNode *n,const std::string& fileName,bool useCache); - bool fetchLocalFile(const std::string& fileName); - - OutputFileClient *outClient_; - std::vector dirs_; - OutputCache* outCache_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/OutputItemWidget.cpp ecflow-4.11.1/Viewer/src/OutputItemWidget.cpp --- ecflow-4.9.0/Viewer/src/OutputItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,765 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OutputItemWidget.hpp" - -#include "OutputDirProvider.hpp" -#include "OutputFetchInfo.hpp" -#include "OutputFileProvider.hpp" -#include "OutputModel.hpp" -#include "PlainTextEdit.hpp" -#include "ServerHandler.hpp" -#include "TextFormat.hpp" -#include "TextPagerEdit.hpp" -#include "VConfig.hpp" -#include "VNode.hpp" -#include "VReply.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" - -#include -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#else -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define _UI_OUTPUTITEMWIDGET_DEBUG - -int OutputItemWidget::updateDirTimeout_=1000*60; - -OutputItemWidget::OutputItemWidget(QWidget *parent) : - QWidget(parent), - userClickedReload_(false), - ignoreOutputSelection_(false), - dirColumnsAdjusted_(false), - submittedWarning_(false) -{ - //We try to keep the contents when clicking away - //tryToKeepContents_=true; - - setupUi(this); - - //-------------------------------- - // The file contents - //-------------------------------- - - messageLabel_->hide(); - messageLabel_->setShowTypeTitle(false); - warnLabel_->hide(); - fileLabel_->setProperty("fileInfo","1"); - - infoProvider_=new OutputFileProvider(this); - - //-------------------------------- - // The dir contents - //-------------------------------- - - dirMessageLabel_->hide(); - dirMessageLabel_->setShowTypeTitle(false); - //dirLabel_->hide(); - dirLabel_->setProperty("fileInfo","1"); - - dirProvider_=new OutputDirProvider(this); - - //The view - OutputDirLitsDelegate* dirDelegate=new OutputDirLitsDelegate(this); - dirView_->setItemDelegate(dirDelegate); - dirView_->setRootIsDecorated(false); - dirView_->setAllColumnsShowFocus(true); - dirView_->setUniformRowHeights(true); - dirView_->setAlternatingRowColors(true); - dirView_->setSortingEnabled(true); - dirView_->sortByColumn(3, Qt::DescendingOrder); // sort with latest files first (0-based) - - //The models - dirModel_=new OutputModel(this); - dirSortModel_=new OutputSortModel(this); - dirSortModel_->setSourceModel(dirModel_); - dirSortModel_->setSortRole(Qt::UserRole); - dirSortModel_->setDynamicSortFilter(true); - - dirView_->setModel(dirSortModel_); - - //When the selection changes in the view - connect(dirView_->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this,SLOT(slotOutputSelected(QModelIndex,QModelIndex))); - - - //Set splitter's initial size. - int wHeight=size().height(); - if(wHeight > 100) - { - QList sizes; - sizes << wHeight-80 << 80; - splitter_->setSizes(sizes); - } - - //Dir contents update timer - updateDirTimer_=new QTimer(this); - updateDirTimer_->setInterval(updateDirTimeout_); - - connect(updateDirTimer_,SIGNAL(timeout()), - this,SLOT(slotUpdateDir())); - - //Editor font - browser_->setFontProperty(VConfig::instance()->find("panel.output.font")); - - fetchInfo_=new OutputFetchInfo(this); - QWidgetAction* fetchInfoAction=new QWidgetAction(this); - fetchInfoAction->setDefaultWidget(fetchInfo_); - fetchInfoTb_->addAction(fetchInfoAction); - - filterTb_->setProperty("strip","first"); - filterOptionTb_->setProperty("strip","last"); - - QMenu *menu=new QMenu(this); - menu->addAction(actionSaveFileAs_); - menu->addAction(actionGotoLine_); - - //TODO: needs proper implementation - gotoLineTb_->hide(); - - //Sets the menu on the toolbutton - moreActionTb_->setMenu(menu); - moreActionTb_->hide(); - moreActionTb_->setEnabled(false); - actionSaveFileAs_->setEnabled(false); - actionGotoLine_->setEnabled(false); - - //Init filter in output browser - browser_->setFilterButtons(filterTb_,filterOptionTb_); - - // - browser_->setSearchButtons(searchTb_); -} - -OutputItemWidget::~OutputItemWidget() -{ -} - -QWidget* OutputItemWidget::realWidget() -{ - return this; -} - -void OutputItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - - //set the info - adjust(info); - - userClickedReload_ = false; - - //info must be a node - if(info_ && info_->isNode() && info_->node()) - { - //Get file contents - infoProvider_->info(info_); - - //Get dir contents - dirProvider_->info(info_); - - //Start contents update timer - updateDirTimer_->start(); - } -} - -std::string OutputItemWidget::currentFullName() const -{ - QModelIndex current=dirSortModel_->mapToSource(dirView_->currentIndex()); - - std::string fullName; - if(current.isValid()) - { - fullName=dirModel_->fullName(current); - } - else - { - OutputFileProvider* op=static_cast(infoProvider_); - fullName=op->joboutFileName(); - } - - return fullName; -} - -void OutputItemWidget::getLatestFile() -{ - messageLabel_->hide(); - messageLabel_->stopProgress(); - fileLabel_->clear(); - browser_->clear(); - dirMessageLabel_->hide(); - fetchInfo_->clearInfo(); - - //Get the latest file contents - infoProvider_->info(info_); - - updateDir(false); // get the directory listing -} - -void OutputItemWidget::getCurrentFile(bool doReload) -{ - messageLabel_->hide(); - messageLabel_->stopLoadLabel(); - messageLabel_->stopProgress(); - fileLabel_->clear(); - browser_->clear(); - fetchInfo_->clearInfo(); - - if(info_) - { - std::string fullName=currentFullName(); -#ifdef _UI_OUTPUTITEMWIDGET_DEBUG - UiLog().dbg() << "output selected: " << fullName; -#endif - OutputFileProvider* op=static_cast(infoProvider_); - op->file(fullName,!doReload); - - updateDir(false); // get the directory listing - } -} - -void OutputItemWidget::clearContents() -{ - updateDirTimer_->stop(); - InfoPanelItem::clear(); - enableDir(false); - messageLabel_->hide(); - messageLabel_->stopProgress(); - fileLabel_->clear(); - browser_->clear(); - reloadTb_->setEnabled(true); - userClickedReload_ = false; - fetchInfo_->clearInfo(); - submittedWarning_=false; -} - -void OutputItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SelectedChanged)) - { - if(selected_ && !suspended_) - { - slotUpdateDir(); - updateDirTimer_->start(); - } - //If unselected we stop the dir update - else - { - updateDirTimer_->stop(); - } - } - - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - updateDirTimer_->stop(); - reloadTb_->setEnabled(false); - enableDir(false); - } - //Resume - else - { - if(info_ && info_->node()) - { - reloadTb_->setEnabled(true); - enableDir(true); - if(selected_) - { - slotUpdateDir(); - updateDirTimer_->start(); - } - } - else - { - clearContents(); - } - } - } -} - -void OutputItemWidget::infoReady(VReply* reply) -{ - //------------------------ - // From output provider - //------------------------ - - if(reply->sender() == infoProvider_) - { - messageLabel_->stopProgress(); - - //For some unknown reason the textedit font, although it is properly set in the constructor, - //is reset to default when we first call infoready. So we need to set it again!! - browser_->updateFont(); - - //TODO: make it possible to show warning and info at the same time - bool hasMessage=false; - submittedWarning_=false; - OutputFileProvider* op=static_cast(infoProvider_); - if(reply->fileName() == op->joboutFileName() && !op->isTryNoZero(reply->fileName()) && - info_ && info_->isNode() && info_->node() && info_->node()->isSubmitted()) - { - hasMessage=true; - submittedWarning_=true; - messageLabel_->showWarning("This is the current job output (as defined by variable ECF_JOBOUT), but \ - because the node status is submitted it may contain the ouput from a previous run!"); - } - else - { - if(reply->hasWarning()) - { - messageLabel_->showWarning(QString::fromStdString(reply->warningText())); - hasMessage=true; - } - else if(reply->hasInfo()) - { - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); - hasMessage=true; - } - } - - browser_->adjustHighlighter(QString::fromStdString(reply->fileName())); - - VFile_ptr f=reply->tmpFile(); - - //If the info is stored in a tmp file - if(f) - { - browser_->loadFile(f); - if(f->storageMode() == VFile::DiskStorage) - hasMessage=false; - - } - //If the info is stored as a string in the reply object - else - { - //QString s=QString::fromStdString(reply->text()); - //browser_->loadText(s,QString::fromStdString(reply->fileName())); - } - - if(!hasMessage) - { - messageLabel_->hide(); - } - //messageLabel_->stopLoadLabel(); - - //Update the file label - fileLabel_->update(reply); - - //Search for some keywords in the current jobout - - if(f) - { - //We do not have dir info so the file must be the jobout - if(dirModel_->isEmpty()) - searchOnReload(); - - //We have dir info - else - { - OutputFileProvider* op=static_cast(infoProvider_); - if(reply->fileName() == op->joboutFileName()) - { - searchOnReload(); - } - } - } - - userClickedReload_ = false; - reloadTb_->setEnabled(true); - - //If we got a local file or a file via the logserver we restart the dir update timer - if(!suspended_ && - (reply->fileReadMode() == VReply::LocalReadMode || - reply->fileReadMode() == VReply::LogServerReadMode)) - { - updateDirTimer_->start(); - } - //Update the selection in the dir list according to the file - if(f) - { - setCurrentInDir(f->sourcePath()); - } -#if 0 - if(reply->tmpFile() && reply->fileReadMode() == VReply::LocalReadMode && - info_ && !info_->server()->isLocalHost()) - { - QString msg="The output file was read from disk but the server's \ - host (" + QString::fromStdString(info_->server()->host()) + - ") is not running on the local machine. If the path is machine-specific (e.g. /tmp) \ - and there exists a file with the same path on the local machine, then\ - this will have been read instead."; - - warnLabel_->showWarning(msg); - } -#endif - - - fetchInfo_->setInfo(reply,info_); - } - - //------------------------ - // From output dir provider - //------------------------ - else - { - //We do not display info/warning here! The dirMessageLabel_ is not part of the dirWidget_ - //and is only supposed to display error messages! - - enableDir(true); - - //Update the dir widget and select the proper file in the list - updateDir(reply->directories(),true); - - //Update the dir label - dirLabel_->update(reply); - - //Enable the update button - dirReloadTb_->setEnabled(true); -#if 0 - //Even though infoReady is called there could be some errors since we could - //try to read multiple directories - displayDirErrors(reply->errorTextVec()); -#endif - } -} - -void OutputItemWidget::infoProgress(VReply* reply) -{ - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); - //messageLabel_->startLoadLabel(); - //updateDir(true); -} - -void OutputItemWidget::infoProgressStart(const std::string& text,int max) -{ - messageLabel_->showInfo(QString::fromStdString(text)); - messageLabel_->startProgress(max); -} - -void OutputItemWidget::infoProgress(const std::string& text,int value) -{ - messageLabel_->progress(QString::fromStdString(text),value); -} - -void OutputItemWidget::infoFailed(VReply* reply) -{ - //File - if(reply->sender() == infoProvider_) - { - QString s=QString::fromStdString(reply->errorText()); - messageLabel_->showError(s); - messageLabel_->stopProgress(); - submittedWarning_=false; - - //Update the file label - fileLabel_->update(reply); - - userClickedReload_ = false; - reloadTb_->setEnabled(true); - - fetchInfo_->setInfo(reply,info_); - } - //Directories - else - { - //We do not have directories - enableDir(false); - - displayDirErrors(reply->errorTextVec()); - - dirReloadTb_->setEnabled(true); - - //the timer is stopped. It will be restarted again if we get a local file or - //a file via the logserver - updateDirTimer_->stop(); - } -} - -void OutputItemWidget::on_reloadTb__clicked() -{ - userClickedReload_ = true; - reloadTb_->setEnabled(false); - getCurrentFile(true); - //userClickedReload_ = false; -} - -//------------------------------------ -// Directory contents -//------------------------------------ - -void OutputItemWidget::on_dirReloadTb__clicked() -{ - dirReloadTb_->setEnabled(false); - updateDir(false); // get the directory listing -} - -void OutputItemWidget::setCurrentInDir(const std::string& fullName) -{ - if(!dirModel_->isEmpty()) - { - //Try to preserve the selection - ignoreOutputSelection_=true; - dirView_->setCurrentIndex(dirSortModel_->fullNameToIndex(fullName)); - ignoreOutputSelection_=false; - } -} - -void OutputItemWidget::updateDir(const std::vector& dirs,bool restartTimer) -{ -UI_FUNCTION_LOG - - if(restartTimer) - updateDirTimer_->stop(); - - bool status=false; - for(std::size_t i=0; i < dirs.size(); i++) - { - if(dirs[i] && dirs[i]->count() > 0) - { - status=true; - break; - } - } - - if(status) - { - OutputFileProvider* op=static_cast(infoProvider_); - op->setDirectories(dirs); - - std::string fullName=currentFullName(); - - dirView_->selectionModel()->clearSelection(); - dirModel_->setData(dirs,op->joboutFileName()); - //dirWidget_->show(); - - //Adjust column width - if(!dirColumnsAdjusted_) - { - dirColumnsAdjusted_=true; - for(int i=0; i< dirModel_->columnCount()-1; i++) - dirView_->resizeColumnToContents(i); - - if(dirModel_->columnCount() > 1) - dirView_->setColumnWidth(1,dirView_->columnWidth(0)); - - } -#ifdef _UI_OUTPUTITEMWIDGET_DEBUG - UiLog().dbg() << " dir item count=" << dirModel_->rowCount(); -#endif - //Try to preserve the selection - ignoreOutputSelection_=true; - dirView_->setCurrentIndex(dirSortModel_->fullNameToIndex(fullName)); - ignoreOutputSelection_=false; - } - else - { - //dirWidget_->hide(); - dirModel_->clearData(); - } - - if(restartTimer) - updateDirTimer_->start(updateDirTimeout_); -} - -void OutputItemWidget::updateDir(bool restartTimer) -{ - dirProvider_->info(info_); - - //Remember the selection - //std::string fullName=currentFullName(); - //updateDir(restartTimer,fullName); -} - -void OutputItemWidget::slotUpdateDir() -{ - updateDir(false); -} - -void OutputItemWidget::enableDir(bool status) -{ - if(status) - { - dirWidget_->show(); - dirMessageLabel_->hide(); - reloadTb_->setEnabled(true); - } - else - { - dirWidget_->hide(); - dirModel_->clearData(); - dirMessageLabel_->show(); - } -} - -void OutputItemWidget::displayDirErrors(const std::vector& errorVec) -{ - QString s; - if(errorVec.size() > 0) - { - QColor col(70,71,72); - s=Viewer::formatBoldText("Output directory: ",col); - - if(errorVec.size() > 1) - { - for(size_t i=0; i < errorVec.size(); i++) - s+=Viewer::formatBoldText("[" + QString::number(i+1) + "] ",col) + - QString::fromStdString(errorVec[i]) + ".   "; - } - else if(errorVec.size() == 1) - s+=QString::fromStdString(errorVec[0]); - } - - if(!s.isEmpty()) - dirMessageLabel_->showError(s); - else - dirMessageLabel_->hide(); -} - -//--------------------------------------------- -// Search -//--------------------------------------------- - -void OutputItemWidget::on_searchTb__clicked() -{ - browser_->showSearchLine(); -} - -void OutputItemWidget::on_gotoLineTb__clicked() -{ - browser_->gotoLine(); -} - - -// Called when we load a new node's information into the panel, or -// when we move to the panel from another one. -// If the search box is open, then search for the first matching item; -// otherwise, search for a pre-configured list of keywords. If none -// are found, and the user has clicked on the 'reload' button then -// we just go to the last line of the output -void OutputItemWidget::searchOnReload() -{ - browser_->searchOnReload(userClickedReload_); -} - -//This slot is called when a file item is selected in the output view. -void OutputItemWidget::slotOutputSelected(QModelIndex idx1,QModelIndex idx2) -{ - if(!ignoreOutputSelection_) - getCurrentFile(false); -} - -//----------------------------------------- -// Fontsize management -//----------------------------------------- - -void OutputItemWidget::on_fontSizeUpTb__clicked() -{ - //We need to call a custom slot here instead of "zoomIn"!!! - browser_->zoomIn(); -} - -void OutputItemWidget::on_fontSizeDownTb__clicked() -{ - //We need to call a custom slot here instead of "zoomOut"!!! - browser_->zoomOut(); -} - -//----------------------------------------- -// Save local copy of file -//----------------------------------------- - -void OutputItemWidget::on_saveFileAsTb__clicked() -{ - if (browser_->isFileLoaded()) - { - QString fileName = QFileDialog::getSaveFileName(this); - if (fileName.isEmpty()) - return; - - browser_->saveCurrentFile(fileName); - } - else - { - UserMessage::message(UserMessage::INFO,true,"No file loaded!"); - } -} - -//----------------------------------------- -// Copy file path -//----------------------------------------- - -void OutputItemWidget::on_copyPathTb__clicked() -{ - std::string fullName=currentFullName(); - - if(!fullName.empty()) - { - QString txt=QString::fromStdString(fullName); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QClipboard* cb=QGuiApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#else - QClipboard* cb=QApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#endif - } -} - -//------------------------- -// Update -//------------------------- - -void OutputItemWidget::nodeChanged(const VNode* n, const std::vector& aspect) -{ - //Changes in the nodes - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::DEFSTATUS || - *it == ecf::Aspect::SUSPENDED) - { - if(submittedWarning_) - getLatestFile(); - else if(info_ && info_->node() == n && info_->node()->isSubmitted()) - { - OutputFileProvider* op=static_cast(infoProvider_); - Q_ASSERT(op); - if(currentFullName() == op->joboutFileName()) - getLatestFile(); - } - return; - } - } -} - -static InfoPanelItemMaker maker1("output"); diff -Nru ecflow-4.9.0/Viewer/src/OutputItemWidget.hpp ecflow-4.11.1/Viewer/src/OutputItemWidget.hpp --- ecflow-4.9.0/Viewer/src/OutputItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef OUTPUTITEMWIDGET_HPP_ -#define OUTPUTITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" - -#include "VFile.hpp" -#include "VDir.hpp" - -#include "ui_OutputItemWidget.h" - -class OutputDirProvider; -class OutputFetchInfo; -class OutputModel; -class OutputSortModel; - -class OutputItemWidget : public QWidget, public InfoPanelItem, protected Ui::OutputItemWidget -{ -Q_OBJECT - -public: - explicit OutputItemWidget(QWidget *parent=0); - ~OutputItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - void infoProgressStart(const std::string& text,int max); - void infoProgress(const std::string& text,int value); - - void nodeChanged(const VNode*, const std::vector&); - void defsChanged(const std::vector&) {} - -protected Q_SLOTS: - void slotOutputSelected(QModelIndex,QModelIndex); - void slotUpdateDir(); - void on_searchTb__clicked(); - void on_gotoLineTb__clicked(); - void on_reloadTb__clicked(); - void on_fontSizeUpTb__clicked(); - void on_fontSizeDownTb__clicked(); - void on_saveFileAsTb__clicked(); - void on_copyPathTb__clicked(); - void on_dirReloadTb__clicked(); - -protected: - void setCurrentInDir(const std::string&); - void updateDir(bool); - void updateDir(const std::vector&,bool); - void enableDir(bool); - void updateState(const FlagSet&); - void searchOnReload(); - void getCurrentFile(bool doReload); - void getLatestFile(); - std::string currentFullName() const; - void updateHistoryLabel(const std::vector&); - void displayDirErrors(const std::vector& errorVec); - - OutputDirProvider* dirProvider_; - OutputModel* dirModel_; - OutputSortModel* dirSortModel_; - - bool userClickedReload_; - bool ignoreOutputSelection_; - QTimer* updateDirTimer_; - static int updateDirTimeout_; - OutputFetchInfo* fetchInfo_; - bool dirColumnsAdjusted_; - bool submittedWarning_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/OutputItemWidget.ui ecflow-4.11.1/Viewer/src/OutputItemWidget.ui --- ecflow-4.9.0/Viewer/src/OutputItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,432 +0,0 @@ - - - OutputItemWidget - - - - 0 - 0 - 494 - 449 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - - 1 - - - - - - - - 0 - - - - - - - - - - - - Copy file path - - - Copy file path - - - - :/viewer/copy_path.svg:/viewer/copy_path.svg - - - true - - - - - - - Save a local copy of the current file to disk - - - ... - - - - :/viewer/filesaveas.svg:/viewer/filesaveas.svg - - - true - - - - - - - Additional information about the fetched output file - - - ... - - - - :/viewer/grey_info.svg:/viewer/grey_info.svg - - - QToolButton::InstantPopup - - - true - - - - - - - Increase font size in text browser <br><code>Ctrl++ or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_up.svg:/viewer/fontsize_up.svg - - - Ctrl++ - - - true - - - - - - - Descrease font size in text browser <br><code>Ctrl+- or Ctrl+wheel</code> - - - ... - - - - :/viewer/fontsize_down.svg:/viewer/fontsize_down.svg - - - Ctrl+- - - - true - - - - - - - Show search bar (CTRL-F) - - - ... - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - Ctrl+F - - - false - - - true - - - - - - - Goto line number (CTRL-L) - - - ... - - - - :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg - - - Ctrl+L - - - true - - - - - - - Qt::Horizontal - - - - 1 - 20 - - - - - - - - Toggle to <b>show</b> text filter bar - - - ... - - - - :/viewer/filter_decor.svg:/viewer/filter_decor.svg - - - true - - - QToolButton::InstantPopup - - - true - - - - - - - Text filter menu - - - - - - - :/viewer/down_arrow.svg:/viewer/down_arrow.svg - - - QToolButton::InstantPopup - - - true - - - - - - - Qt::Horizontal - - - - 1 - 20 - - - - - - - - Reload the selected output file - - - ... - - - - :/viewer/sync.svg:/viewer/sync.svg - - - Ctrl+R - - - true - - - - - - - ... - - - - :/viewer/cogwheel.svg:/viewer/cogwheel.svg - - - QToolButton::InstantPopup - - - true - - - - - - - - - - - - - - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - Reload directory listing - - - ... - - - - :/viewer/sync_black.svg:/viewer/sync_black.svg - - - true - - - - - - - - - - 0 - 0 - - - - QFrame::Sunken - - - - - - - - - - - - - - - :/viewer/filesaveas.svg:/viewer/filesaveas.svg - - - Save file as ... - - - Save a local copy of the current task to disk - - - - - - :/viewer/images/goto_line.svg:/viewer/images/goto_line.svg - - - Go to line ... - - - Go to line number - - - Ctrl+L - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      - - FileInfoLabel - QLabel -
      FileInfoLabel.hpp
      -
      - - DirInfoLabel - QLabel -
      FileInfoLabel.hpp
      -
      - - OutputBrowser - QWidget -
      OutputBrowser.hpp
      - 1 -
      -
      - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/OutputModel.cpp ecflow-4.11.1/Viewer/src/OutputModel.cpp --- ecflow-4.9.0/Viewer/src/OutputModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,381 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OutputModel.hpp" - -#include -#include -#include -#include -#include - -QColor OutputModel::joboutCol_=QColor(0,115,48); - -//======================================================================= -// -// OutputModel -// -//======================================================================= - -OutputModel::OutputModel(QObject *parent) : - QAbstractItemModel(parent), - joboutRow_(-1) - -{ -} - -void OutputModel::setData(const std::vector& dirs,const std::string& jobout) -{ - beginResetModel(); - dirs_=dirs; - joboutRow_=-1; - - for(std::size_t i=0; i < dirs_.size(); i++) - { - if(dirs_[i]) - { - int idx=dirs_[i]->findByFullName(jobout); - if(idx != -1) - { - joboutRow_=idx; - break; - } - } - } - - endResetModel(); -} - -void OutputModel::clearData() -{ - beginResetModel(); - dirs_.clear(); - endResetModel(); -} - -int OutputModel::columnCount( const QModelIndex& parent ) const -{ - return 6; -} - -int OutputModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - if(!parent.isValid()) - { - int cnt=0; - for(std::size_t i=0; i < dirs_.size(); i++) - { - if(dirs_[i]) - cnt+=dirs_[i]->count(); - } - return cnt; - } - - return 0; -} - -QVariant OutputModel::data(const QModelIndex& index, int role) const -{ - if(!hasData() || (role != Qt::DisplayRole && role != Qt::UserRole && - role != Qt::ForegroundRole && role != Qt::ToolTipRole && role != Qt::FontRole)) - return QVariant(); - - int row=index.row(); - VDir_ptr dir; - VDirItem *item=itemAt(row,dir); - - if(!item || !dir) - return QVariant(); - - if(role == Qt::DisplayRole) - { - switch(index.column()) - { - case 0: - return QString::fromStdString(item->name_); - case 1: - return QString::fromStdString(dir->path()); - case 2: - return formatSize(item->size_); - case 3: - return formatAgo(item->mtime_); - case 4: - return formatDate(item->mtime_); - case 5: - return QString::fromStdString(dir->fetchModeStr()); - default: - break; - } - } - else if(role == Qt::ForegroundRole) - { - if(row == joboutRow_) - { - return joboutCol_; - } - } - else if(role == Qt::FontRole) - { - if(row == joboutRow_) - { - QFont f; - f.setBold(true); - return f; - } - } - else if(role == Qt::ToolTipRole) - { - if(row == joboutRow_) - { - return QString::fromStdString(item->name_) + " is the current job output file."; - } - } - //Used for sorting - else if(role == Qt::UserRole) - { - switch(index.column()) - { - case 0: - return QString::fromStdString(item->name_); - case 1: - return QString::fromStdString(dir->path()); - case 2: - return item->size_; - case 3: - return secsToNow(item->mtime_); - case 4: - return item->mtime_.toTime_t(); - case 5: - return QString::fromStdString(dir->fetchModeStr()); - default: - break; - } - } - - - return QVariant(); -} - -QVariant OutputModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || role != Qt::DisplayRole ) - return QAbstractItemModel::headerData( section, orient, role ); - - switch ( section ) - { - case 0: return tr("Name"); - case 1: return tr("Path"); - case 2: return tr("Size"); - case 3: return tr("Modified (ago)"); - case 4: return tr("Modified"); - case 5: return tr("Source"); - default: return QVariant(); - } - - return QVariant(); -} - -QModelIndex OutputModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column,static_cast(0)); - } - - return QModelIndex(); - -} - -QModelIndex OutputModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); - -} - -VDirItem* OutputModel::itemAt(int row,VDir_ptr& dir) const -{ - for(std::size_t i=0; i < dirs_.size(); i++) - { - if(dirs_[i]) - { - int cnt=dirs_[i]->count(); - if(row < cnt) - { - Q_ASSERT(row>=0); - dir=dirs_[i]; - return dirs_[i]->items()[row]; - } - - row-=cnt; - } - } - - return 0; -} - -bool OutputModel::hasData() const -{ - for(std::size_t i=0; i < dirs_.size(); i++) - if(dirs_[i]) - return true; - - return false; -} - -std::string OutputModel::fullName(const QModelIndex& index) const -{ - if(!hasData()) - return std::string(); - - int row=index.row(); - for(std::size_t i=0; i < dirs_.size(); i++) - { - if(dirs_[i]) - { - int cnt=dirs_[i]->count(); - if(row < cnt) - { - Q_ASSERT(row >=0); - return dirs_[i]->fullName(row); - } - row-=cnt; - } - } - - return std::string(); -} - -QString OutputModel::formatSize(unsigned int size) const -{ - if(size < 1024) - return QString::number(size) + " B"; - else if(size < 1024*1024) - return QString::number(size/1024) + " KB"; - else if(size < 1024*1024*1024) - return QString::number(size/(1024*1024)) + " MB"; - else - return QString::number(size/(1024*1024*1024)) + " GB"; - - return QString(); -} - -QString OutputModel::formatDate(QDateTime dt) const -{ - //QDateTime dt=QDateTime::fromTime_t(t); - return dt.toString("yyyy-MM-dd hh:mm:ss"); -} - -QString OutputModel::formatAgo(QDateTime dt) const -{ - QString str=tr("Right now"); - - QDateTime now=QDateTime::currentDateTime(); - - int delta = dt.secsTo(now); - if(delta<0) delta = 0; - - if(delta ==1) - str=tr("1 second ago"); - - else if(delta >=1 && delta < 60) - { - str=QString::number(delta) + tr(" second") + ((delta==1)?tr(""):tr("s")) + tr(" ago"); - } - - else if(delta >= 60 && delta < 60*60) - { - int val=delta/60; - str=QString::number(val) + tr(" minute") + ((val==1)?tr(""):tr("s")) + tr(" ago"); - } - - else if(delta >= 60*60 && delta < 60*60*24) - { - int val=delta/(60*60); - str=QString::number(val) + tr(" hour") + ((val==1)?tr(""):tr("s")) + tr(" ago"); - } - - else if(delta >= 60*60*24) - { - int val=delta/(60*60*24); - str=QString::number(val) + tr(" day") + ((val==1)?tr(""):tr("s")) + tr(" ago"); - } - - return str; -} - -qint64 OutputModel::secsToNow(QDateTime dt) const -{ - QDateTime now=QDateTime::currentDateTime(); - - qint64 delta = dt.secsTo(now); - return (delta<0)?0:delta; -} - -//======================================================================= -// -// OutputSortModel -// -//======================================================================= - -OutputSortModel::OutputSortModel(QObject* parent) : - QSortFilterProxyModel(parent) -{ - -} - -QModelIndex OutputSortModel::fullNameToIndex(const std::string& fullName) -{ - QString name=QString::fromStdString(fullName); - - for(int i=0; i < rowCount(QModelIndex()); i++) - { - QModelIndex idx=index(i,0); - if(name.endsWith(data(idx,Qt::DisplayRole).toString())) - { - return idx; - } - } - return QModelIndex(); -} - -//======================================================== -// -// OutputDirLitsDelegate -// -//======================================================== - -OutputDirLitsDelegate::OutputDirLitsDelegate(QWidget *parent) : QStyledItemDelegate(parent) -{ -} - -void OutputDirLitsDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ - if(index.column()==1) - { - QStyleOptionViewItem vopt(option); - initStyleOption(&vopt, index); - vopt.textElideMode=Qt::ElideRight; - QStyledItemDelegate::paint(painter,vopt,index); - } - else - { - QStyledItemDelegate::paint(painter,option,index); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/OutputModel.hpp ecflow-4.11.1/Viewer/src/OutputModel.hpp --- ecflow-4.9.0/Viewer/src/OutputModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OutputModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -#ifndef OUTPUTMODEL_H -#define OUTPUTMODEL_H - -#include -#include -#include -#include - -#include - -#include "NodeObserver.hpp" -#include "VDir.hpp" -#include "VInfo.hpp" - -class OutputModel : public QAbstractItemModel -{ -public: - explicit OutputModel(QObject *parent=0); - - void setData(const std::vector&,const std::string& jobout); - void clearData(); - bool isEmpty() const {return (!hasData());} - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - //Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - std::string fullName(const QModelIndex& index) const; - -protected: - VDirItem* itemAt(int row,VDir_ptr& dir) const; - bool hasData() const; - QString formatSize(unsigned int size) const; - QString formatDate(QDateTime) const; - QString formatAgo(QDateTime) const; - qint64 secsToNow(QDateTime dt) const; - - std::vector dirs_; - int joboutRow_; - static QColor joboutCol_; -}; - -//Filters and sorts the output -class OutputSortModel : public QSortFilterProxyModel -{ -public: - explicit OutputSortModel(QObject *parent=0); - ~OutputSortModel() {} - - QModelIndex fullNameToIndex(const std::string& fullName); -}; - -class OutputDirLitsDelegate : public QStyledItemDelegate -{ -public: - explicit OutputDirLitsDelegate(QWidget *parent=0); - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - - //QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/OverviewItemWidget.cpp ecflow-4.11.1/Viewer/src/OverviewItemWidget.cpp --- ecflow-4.9.0/Viewer/src/OverviewItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OverviewItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,187 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OverviewItemWidget.hpp" -#include "Highlighter.hpp" -#include "OverviewProvider.hpp" -#include "VConfig.hpp" -#include "VReply.hpp" - -#include - - -//======================================================== -// -// InfoItemWidget -// -//======================================================== - -OverviewItemWidget::OverviewItemWidget(QWidget *parent) : - CodeItemWidget(parent), - lastScrollPos_(0) -{ - fileLabel_->hide(); - externalTb_->hide(); - copyPathTb_->hide(); - messageLabel_->setShowTypeTitle(false); - - textEdit_->setShowLineNumbers(false); - - //The document becomes the owner of the highlighter - new Highlighter(textEdit_->document(),"info"); - - infoProvider_=new OverviewProvider(this); - - //Editor font - textEdit_->setFontProperty(VConfig::instance()->find("panel.overview.font")); -} - -OverviewItemWidget::~OverviewItemWidget() -{ -} - -QWidget* OverviewItemWidget::realWidget() -{ - return this; -} - -void OverviewItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - - //set the info - adjust(info); - - //Info must be a node - if(info_) - { - reloadTb_->setEnabled(false); - infoProvider_->info(info_); - } -} - -void OverviewItemWidget::reload() -{ - //Save the vertical scrollbar pos - lastScrollPos_=textEdit_->verticalScrollBar()->value(); - - textEdit_->clear(); - reloadTb_->setEnabled(false); - infoProvider_->info(info_); -} - -void OverviewItemWidget::clearContents() -{ - InfoPanelItem::clear(); - textEdit_->clear(); - reloadTb_->setEnabled(true); -} - -void OverviewItemWidget::infoReady(VReply* reply) -{ - Q_ASSERT(reply); - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); - - //Restore the vertical scrollbar pos - textEdit_->verticalScrollBar()->setValue(lastScrollPos_); - - reloadTb_->setEnabled(true); -} - -void OverviewItemWidget::infoProgress(VReply* reply) -{ - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); -} - -void OverviewItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); - textEdit_->setPlainText(s); - - reloadTb_->setEnabled(true); -} - -//At this point we can be sure that the node is handled by this item. -void OverviewItemWidget::nodeChanged(const VNode* node, const std::vector& aspect) -{ - if(frozen_) - return; - - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::STATE || *it == ecf::Aspect::ADD_REMOVE_NODE || *it == ecf::Aspect::ADD_REMOVE_ATTR || - *it == ecf::Aspect::DEFSTATUS || *it == ecf::Aspect::SUSPENDED || *it == ecf::Aspect::NODE_VARIABLE) - { - reload(); - return; - } - } -} - -void OverviewItemWidget::defsChanged(const std::vector& aspect) -{ - if(frozen_) - return; - - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::SERVER_STATE || *it == ecf::Aspect::SERVER_VARIABLE || *it == ecf::Aspect::ADD_REMOVE_ATTR) - { - reload(); - return; - } - } -} - -void OverviewItemWidget::connectStateChanged() -{ - if(frozen_) - return; - - reload(); -} - - -void OverviewItemWidget::reloadRequested() -{ - reload(); -} - -void OverviewItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - reloadTb_->setEnabled(false); - } - //Resume - else - { - if(info_ && info_->node()) - { - reloadTb_->setEnabled(true); - } - else - { - clearContents(); - } - } - } -} - -static InfoPanelItemMaker maker1("overview"); diff -Nru ecflow-4.9.0/Viewer/src/OverviewItemWidget.hpp ecflow-4.11.1/Viewer/src/OverviewItemWidget.hpp --- ecflow-4.9.0/Viewer/src/OverviewItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OverviewItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef OVERVIEWITEMWIDGET_HPP_ -#define OVERVIEWITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" -#include "CodeItemWidget.hpp" - -class OverviewItemWidget : public CodeItemWidget, public InfoPanelItem -{ -public: - explicit OverviewItemWidget(QWidget *parent=0); - ~OverviewItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&); - void defsChanged(const std::vector&); - void connectStateChanged(); - -protected: - void reload(); - void updateState(const ChangeFlags&); - void reloadRequested(); - - int lastScrollPos_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/OverviewProvider.cpp ecflow-4.11.1/Viewer/src/OverviewProvider.cpp --- ecflow-4.9.0/Viewer/src/OverviewProvider.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OverviewProvider.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,303 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "OverviewProvider.hpp" - -#include "ConnectState.hpp" -#include "ServerHandler.hpp" -#include "VAttribute.hpp" -#include "VNode.hpp" -#include "VNState.hpp" -#include "VSState.hpp" -#include "VFileInfo.hpp" - -OverviewProvider::OverviewProvider(InfoPresenter* owner) : InfoProvider(owner,VTask::NoTask) -{ - -} - -//================================================== -// Server -//================================================== - -void OverviewProvider::visit(VInfoServer* info) -{ - reply_->reset(); - - //Build the second part of the server info. We do not need - //information from the ClientInvoker for this!! - std::stringstream ss; - serverInfo(info,ss); - reply_->text(ss.str()); - - //If not connected we reply immediately! - if(info->server()->connectState()->state() != ConnectState::Normal) - { - owner_->infoReady(reply_); - return; - } - - //Define a task for getting the stats from the server. - //We need ClientInvoker for this - task_=VTask::create(VTask::StatsTask,this); - - //Run the task in the server. When it finish taskFinished() is called. The text returned - //in the reply will be prepended to the string we generated above. - info->server()->run(task_); -} - -//Node -void OverviewProvider::visit(VInfoNode* ni) -{ - reply_->reset(); - std::stringstream ss; - nodeInfo(ni,ss); - reply_->text(ss.str()); - owner_->infoReady(reply_); -} - -void OverviewProvider::visit(VInfoAttribute* ptr) -{ -} - -void OverviewProvider::taskChanged(VTask_ptr task) -{ - if(task_ != task) - return; - - switch(task->status()) - { - case VTask::FINISHED: - //We prepend the results to the existing text - reply_->prependText(task->reply()->text()); - owner_->infoReady(reply_); - //We do not need the task anymore. - task_.reset(); - break; - case VTask::ABORTED: - case VTask::CANCELLED: - case VTask::REJECTED: - reply_->prependText(task->reply()->text()); - owner_->infoFailed(reply_); - //We do not need the task anymore. - task_.reset();break; - default: - break; - } -} - -void OverviewProvider::serverInfo(VInfoServer* info,std::stringstream& f) -{ - static const std::string inc = " "; - - ServerHandler *server=info->server(); - if(!server) return; - VServer *snode=server->vRoot(); - - ConnectState* cst=server->connectState(); - - //If the server is not connected!! - if(cst->state() != ConnectState::Normal) - { - f << cst->describe() << "\n"; - f << inc << "Name : " << server->name() << "\n"; - f << inc << "Host : " << server->host() << "\n"; - f << inc << "Port : " << server->port() << "\n"; - - if(cst->state() == ConnectState::Lost) - { - f << inc << "Last connection attempt : " << VFileInfo::formatDate(cst->lastLostTime()).toStdString() << "\n"; - f << "\n"; - if(!cst->errorMessage().empty()) - { - f << "Error message:\n"; - f << cst->errorMessage(); - } - } - else if(cst->state() == ConnectState::Disconnected) - { - f << inc << "Disconnected : " << VFileInfo::formatDate(cst->lastDisconnectTime()).toStdString() << "\n"; - } - return; - } - - //if(!ServerDefsAccess(server).defs()) return; - - using namespace boost::posix_time; - using namespace boost::gregorian; - - std::string typeName="server"; - std::string nodeName=server->name(); - std::string statusName(VSState::toName(server).toStdString()); - std::string flags(snode->flagsAsStr()); - - //Header - f << "name : " << nodeName << "\n"; - f << "type : " << typeName << "\n"; - f << "status : " << statusName << "\n"; - - if(!flags.empty()) - f << "flags : " << flags << "\n"; - - f << "----------\n"; - //Start block: Type, name - f << typeName << " " << server->name() << "\n"; - - //Generated variables - std::vector gvar; - snode->genVariables(gvar); - for(std::vector::const_iterator it = gvar.begin(); it != gvar.end(); ++it) - { - f << inc << "# edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; - } - - //Variables - std::vector var; - snode->variables(var); - for(std::vector::const_iterator it = var.begin(); it != var.end(); ++it) - { - f << inc << "edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; - } - - //Print children - VNode *vr=server->vRoot(); - for(int i=0; i < vr->numOfChildren(); i++) - { - f << inc << vr->childAt(i)->nodeType() << " " << - vr->childAt(i)->strName() << "\n"; - } - - //End block - f << "end" << typeName << " # " << nodeName << "\n"; -} - -void OverviewProvider::nodeInfo(VInfoNode* info,std::stringstream& f) -{ - ServerHandler *server=info->server(); - if(!server) return; - //if(!ServerDefsAccess(server).defs()) return; - - VNode* node=info->node(); - if(!node) return; - - static const std::string inc = " "; - - using namespace boost::posix_time; - using namespace boost::gregorian; - - std::string typeName=node->nodeType(); - std::string nodeName(node->name().toStdString()); - std::string statusName(node->stateName().toStdString()); - std::string flags(node->flagsAsStr()); - - //Header - f << "name : " << nodeName << "\n"; - f << "type : " << typeName << "\n"; - f << "status : " << statusName << "\n"; - - if(!flags.empty()) - f << "flags : " << flags << "\n"; - - node_ptr nn=node->node(); - - boost::posix_time::ptime state_change_time = nn->state_change_time(); - if(!state_change_time.is_special()) - { - f << "at : " << boost::posix_time::to_simple_string(state_change_time) << "\n"; - } - - f << "----------\n"; - - if(node->isAborted()) - { - const std::string& abTxt=node->abortedReason(); - if(!abTxt.empty()) - { - f << "aborted reason : " + abTxt + "\n"; - f << "----------\n"; - } - } - - //Start block: Type, name - f << typeName << " " << nodeName << "\n"; - - //Clock information for suites - if(Suite *suite=nn->isSuite()) - { - //Suite* suite = dynamic_cast(nn); - // f << "clock : "; - if (suite->clockAttr()) - { - suite->clockAttr().get()->print(f); // f << "\n"; - } - } - - //Default status: the status the node should have when the begin/re-queue is called - //if(st != DState::QUEUED && st != DState::UNKNOWN) - f << inc << "defstatus " << node->defaultStateName().toStdString() << "\n"; - - //Zombies attribute - const std::vector & vect = nn->zombies(); - for (std::vector::const_iterator it = vect.begin(); it != vect.end(); ++it) - f << inc << it->toString() << "\n"; - - //Autocancel - if(nn->hasAutoCancel() && nn->get_autocancel()) - f << inc << nn->get_autocancel()->toString() << "\n"; - - //For suspended nodes - if(nn->isSuspended()) - { - f << inc << "# " << typeName << " " << nodeName << " is " << statusName << "\n"; - } - - if(nn->hasTimeDependencies()) - { - f << inc << "# time-date-dependencies: "; - if (nn->isTimeFree()) f << "free\n"; - else f << "holding\n"; - } - - //Generated variables - std::vector gvar; - node->genVariables(gvar); - for(std::vector::const_iterator it = gvar.begin(); it != gvar.end(); ++it) - { - f << inc << "# edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; - } - - //Variables - std::vector var; - node->variables(var); - for(std::vector::const_iterator it = var.begin(); it != var.end(); ++it) - { - f << inc << "edit " << (*it).name() << " '" << (*it).theValue() << "'\n"; - } - - //Other attributes - const std::vector& attr=node->attr(); - for(std::vector::const_iterator it=attr.begin(); it != attr.end(); ++it) - { - if((*it)->typeName() != "var" && (*it)->typeName() != "genvar") - f << inc << (*it)->definition().toStdString() << "\n"; - } - - //Print children - for(int i=0; i < node->numOfChildren(); i++) - { - f << inc << node->childAt(i)->nodeType() << " " << - node->childAt(i)->strName() << "\n"; - } - - - //Here we should print some additional information from the attributes as well. It is not clear exactly what! - - //End block - f << "end" << typeName << " # " << nodeName << "\n"; -} diff -Nru ecflow-4.9.0/Viewer/src/OverviewProvider.hpp ecflow-4.11.1/Viewer/src/OverviewProvider.hpp --- ecflow-4.9.0/Viewer/src/OverviewProvider.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/OverviewProvider.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef OVERVIEWPROVIDER_HPP_ -#define OVERVIEWPROVIDER_HPP_ - -#include "InfoProvider.hpp" - -class InfoPanelItem; - -class OverviewProvider : public InfoProvider -{ -public: - explicit OverviewProvider(InfoPresenter* owner); - - //From VInfoVisitor - void visit(VInfoServer*); - void visit(VInfoNode*); - void visit(VInfoAttribute*); - - //From VTaskObserver - void taskChanged(VTask_ptr); - -protected: - void serverInfo(VInfoServer*,std::stringstream& f); - void nodeInfo(VInfoNode*,std::stringstream& f); - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/Palette.cpp ecflow-4.11.1/Viewer/src/Palette.cpp --- ecflow-4.9.0/Viewer/src/Palette.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Palette.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,126 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "Palette.hpp" - -#include "UserMessage.hpp" -#include "VProperty.hpp" - -#include -#include -#include -#include - -#include -#include - -static QMap paletteId; - -Palette::Palette() -{ -} - -void Palette::load(const std::string& parFile) -{ - - if(paletteId.isEmpty()) - { - paletteId["window"]=QPalette::Window; - paletteId["windowtext"]=QPalette::WindowText; - paletteId["base"]=QPalette::Base; - paletteId["alternatebase"]=QPalette::AlternateBase; - paletteId["tooltipbase"]=QPalette::ToolTipBase; - paletteId["tooltiptext"]=QPalette::ToolTipText; - paletteId["text"]=QPalette::Text; - paletteId["button"]=QPalette::Button; - paletteId["buttontext"]=QPalette::ButtonText; - paletteId["brighttext"]=QPalette::BrightText; - paletteId["light"]=QPalette::Light; - paletteId["midlight"]=QPalette::Midlight; - paletteId["dark"]=QPalette::Dark; - paletteId["mid"]=QPalette::Mid; - paletteId["shadow"]=QPalette::Shadow; - paletteId["higlight"]=QPalette::Highlight; - paletteId["highlightedtext"]=QPalette::HighlightedText; - paletteId["link"]=QPalette::Link; - paletteId["linkvisited"]=QPalette::LinkVisited; - } - - //Parse param file using the boost JSON property tree parser - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(parFile,pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, - std::string("Error! Palette::load() unable to parse definition file: " + parFile + " Message: " +errorMessage)); - return; - } - - QPalette palette=qApp->palette(); - - for(ptree::const_iterator it = pt.begin(); it != pt.end(); ++it) - { - std::string name=it->first; - ptree ptItem=it->second; - - QPalette::ColorGroup group; - if(name == "active") - group=QPalette::Active; - else if(name == "inactive") - group=QPalette::Inactive; - else if(name == "disabled") - group=QPalette::Disabled; - else - { - UserMessage::message(UserMessage::ERROR, true, - std::string("Error! Palette::load() unable to identify group: " + name)); - continue; - } - - for(ptree::const_iterator itItem = ptItem.begin(); itItem != ptItem.end(); ++itItem) - { - std::string role=itItem->first; - std::string val=itItem->second.get_value(); - - QMap::const_iterator itP=paletteId.find(role); - if(itP != paletteId.end()) - { - QColor col=VProperty::toColour(val); - if(col.isValid()) - { - palette.setColor(group,itP.value(),col); - } - } - } - } - - qApp->setPalette(palette); -} - -void Palette::statusColours(QColor bg,QColor &bgLight,QColor &border) -{ - int lighter=150; - if(bg.value() < 235) - bgLight=bg.lighter(130); - else - bgLight=bg.lighter(lighter); - - //if(bg.hsvHue() < 58 && bg.hsvHue() > 50) - // bgLight=bg.lighter(170); - - border=bg.darker(120); //125 150 -} - diff -Nru ecflow-4.9.0/Viewer/src/Palette.hpp ecflow-4.11.1/Viewer/src/Palette.hpp --- ecflow-4.9.0/Viewer/src/Palette.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Palette.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_PALETTE_HPP_ -#define VIEWER_SRC_PALETTE_HPP_ - -#include -#include - -class Palette -{ -public: - Palette(); - static void load(const std::string& parFile); - static void statusColours(QColor bg,QColor &bgLight,QColor &border); - -}; - - -#endif /* VIEWER_SRC_PALETTE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/PlainTextEdit.cpp ecflow-4.11.1/Viewer/src/PlainTextEdit.cpp --- ecflow-4.9.0/Viewer/src/PlainTextEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,593 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "PlainTextEdit.hpp" - -#include "GotoLineDialog.hpp" - -#include -#include -#include -#include -#include -#include - -#include "VConfig.hpp" -#include "UiLog.hpp" - -PlainTextEdit::PlainTextEdit(QWidget * parent) : - QPlainTextEdit(parent), - showLineNum_(true), - rightMargin_(2), - hyperlinkEnabled_(false), - gotoLineDialog_(0), - numAreaBgCol_(232,231,230), - numAreaFontCol_(102,102,102), - numAreaSeparatorCol_(210,210,210), - numAreaCurrentCol_(212,212,255), - fontProp_(0) -{ - lineNumArea_ = new LineNumberArea(this); - - connect(this,SIGNAL(blockCountChanged(int)), - this,SLOT(updateLineNumberAreaWidth(int))); - - connect(this,SIGNAL(updateRequest(QRect,int)), - this,SLOT(updateLineNumberArea(QRect,int))); - - connect(this,SIGNAL(cursorPositionChanged()), - lineNumArea_,SLOT(update())); - - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaBackground")) - numAreaBgCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaFontColour")) - numAreaFontCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaSeparator")) - numAreaSeparatorCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaCurrent")) - numAreaCurrentCol_=p->value().value(); - - updateLineNumberAreaWidth(0); - - QFont f("Courier"); - //QFont f("Monospace"); - //f.setStyleHint(QFont::TypeWriter); - f.setFixedPitch(true); - f.setPointSize(10); - //f.setStyleStrategy(QFont::PreferAntialias); - setFont(f); -} - -PlainTextEdit::~PlainTextEdit() -{ - if (gotoLineDialog_) - delete gotoLineDialog_; - - if(fontProp_) - fontProp_->removeObserver(this); -} - -bool PlainTextEdit::setHyperlinkEnabled(bool h) -{ - hyperlinkEnabled_ = h; - setMouseTracking(h); - return true; -} - -void PlainTextEdit::setShowLineNumbers(bool b) -{ - showLineNum_ = b; - lineNumArea_->setVisible(b); - updateLineNumberAreaWidth(0); -} - - -// --------------------------------------------------------------------------- -// TextEdit::cursorRowCol -// returns the row and column position of the cursor -// - note that the first row and column are (1,1) -// --------------------------------------------------------------------------- - -void PlainTextEdit::cursorRowCol(int *row, int *col) -{ - const QTextCursor cursor = textCursor(); - - QTextBlock cb, b; - int column, line = 1; - cb = cursor.block(); - column = (cursor.position() - cb.position()) + 1; - - // find the line number - is there a better way than this? - - for (b = document()->begin(); b != document()->end(); b = b.next()) - { - if( b==cb ) break; - line++; - } - - *row = line; - *col = column; -} - - -// --------------------------------------------------------------------------- -// TextEdit::characterBehindCursor -// returns the character to the left of the text cursor -// --------------------------------------------------------------------------- - -QChar PlainTextEdit::characterBehindCursor(QTextCursor *cursor) -{ - QTextCursor docTextCursor = textCursor(); - QTextCursor *theCursor = (cursor == 0) ? &docTextCursor : cursor; - return document()->characterAt(theCursor->position() - 1); -} - -// --------------------------------------------------------------------------- -// TextEdit::numLinesSelected -// returns the number of lines in the current selection -// yes - all this code to do that! -// --------------------------------------------------------------------------- - -int PlainTextEdit::numLinesSelected() -{ - QTextCursor cursor = textCursor(); // get the document's cursor - int selStart = cursor.selectionStart(); - int selEnd = cursor.selectionEnd(); - QTextBlock bStart = document()->findBlock(selStart); - QTextBlock bEnd = document()->findBlock(selEnd); - int lineStart = bStart.firstLineNumber(); - int lineEnd = bEnd.firstLineNumber(); - int numLines = (lineEnd - lineStart) + 1; - - return numLines; -} - -// --------------------------------------------------------------------------- -// TextEdit::lineNumberAreaWidth -// returns the required width to display the line numbers. This adapts to the -// maximum number of digits we need to display. -// --------------------------------------------------------------------------- - -int PlainTextEdit::lineNumberAreaWidth() -{ - if (showLineNumbers()) - { - int digits = 1; - int max = qMax(1, blockCount()); - while (max >= 10) - { - max /= 10; - ++digits; - } - - int space = 3 + fontMetrics().width(QLatin1Char('9')) * digits + rightMargin_; - - return space; - } - else - { - return 0; - } -} - -// --------------------------------------------------------------------------- -// TextEdit::updateLineNumberAreaWidth -// called when the number of lines in the document changes. The argument is -// the new number of lines (blocks). -// --------------------------------------------------------------------------- - -void PlainTextEdit::updateLineNumberAreaWidth(int) -{ - setViewportMargins(lineNumberAreaWidth(), 0, 0, 0); -} - - -// --------------------------------------------------------------------------- -// TextEdit::updateLineNumberArea -// called when the editor is updated. We want to ensure that the line number -// widget stays in sync with it. -// --------------------------------------------------------------------------- - -void PlainTextEdit::updateLineNumberArea(const QRect &rect, int dy) -{ - if (dy) - lineNumArea_->scroll(0, dy); - else - lineNumArea_->update(0, rect.y(), lineNumArea_->width(), rect.height()); - - if (rect.contains(viewport()->rect())) - updateLineNumberAreaWidth(0); -} - - -// --------------------------------------------------------------------------- -// TextEdit::resizeEvent -// called when a resize event is triggered. Reset the size of the line widget. -// --------------------------------------------------------------------------- - -void PlainTextEdit::resizeEvent(QResizeEvent *e) -{ - QPlainTextEdit::resizeEvent(e); - - QRect cr = contentsRect(); - lineNumArea_->setGeometry(QRect(cr.left(), cr.top(), lineNumberAreaWidth(), cr.height())); -} - - -// --------------------------------------------------------------------------- -// TextEdit::focusInEvent -// called when the widget gains input focus -// --------------------------------------------------------------------------- - -void PlainTextEdit::focusInEvent(QFocusEvent *event) -{ - Q_EMIT focusRegained(); - QPlainTextEdit::focusInEvent(event); -} - - -// --------------------------------------------------------------------------- -// TextEdit::focusOutEvent -// called when the widget loses input focus - -// --------------------------------------------------------------------------- - -void PlainTextEdit::focusOutEvent(QFocusEvent *event) -{ - Q_EMIT focusLost(); - QPlainTextEdit::focusOutEvent(event); -} - -// --------------------------------------------------------------------------- -// TextEdit::lineNumberAreaPaintEvent -// called when the line number widget needs to be repainted. This is where we -// actually draw the numbers on the widget. -// --------------------------------------------------------------------------- - -void PlainTextEdit::lineNumberAreaPaintEvent(QPaintEvent *event) -{ - int currentRow, currentCol; - cursorRowCol (¤tRow, ¤tCol); // get the current line number so we can highlight it - - - QPainter painter(lineNumArea_); - painter.fillRect(event->rect(), numAreaBgCol_); // light grey background - - painter.setPen(QPen(numAreaSeparatorCol_)); - painter.drawLine(event->rect().topRight(),event->rect().bottomRight()); - - QTextBlock block = firstVisibleBlock(); - int blockNumber = block.blockNumber(); - int top = (int) blockBoundingGeometry(block).translated(contentOffset()).top(); - int bottom = top + (int) blockBoundingRect(block).height(); - QFont fontNormal(font()); // the font to use for most line numbers - QFont fontBold(fontNormal); // the font to use for the current line number - fontBold.setBold(true); - //painter.setPen(Qt::blue); - painter.setPen(numAreaFontCol_); - - painter.setFont(fontNormal); - - while (block.isValid() && top <= event->rect().bottom()) - { - if (block.isVisible() && bottom >= event->rect().top()) - { - QString number = QString::number(blockNumber + 1); - - if (blockNumber == currentRow-1) // is this the current line? - { - painter.setFont(fontBold); - painter.fillRect(0, top, lineNumArea_->width()-rightMargin_, fontMetrics().height(), numAreaCurrentCol_); // highlight the background - } - - - painter.drawText(0, top, lineNumArea_->width()-rightMargin_, fontMetrics().height(), // draw the line number - Qt::AlignRight, number); - - - if (blockNumber == currentRow-1) // is this the current line? - { - painter.setFont(fontNormal); // reset the font to normal - } - } - - block = block.next(); - top = bottom; - bottom = top + (int) blockBoundingRect(block).height(); - ++blockNumber; - } -} - - -QString PlainTextEdit::emptyString_ ; // used as a default argument to findString() - -bool PlainTextEdit::findString(const QString &s,QTextDocument::FindFlags flags, bool replace, const QString &r) -{ - - lastFindString_ = s; // store for repeat searches - lastFindFlags_ = flags; // store for repeat searches - bool found = false; - - if (find(s,flags)) // find and select the string - were we successful? - { - //statusMessage("", 0); - found = true; - } - else // did not find the string - { - if (1) // 'wraparound' search - on by default, we can add a user option if it might be useful to turn it off - { - QTextCursor original_cursor = textCursor(); // get the document's cursor - QTextCursor cursor(original_cursor); - - if (flags & QTextDocument::FindBackward) // move to the start or end of the document to continue the search - cursor.movePosition(QTextCursor::End); - else - cursor.movePosition(QTextCursor::Start); - - setTextCursor(cursor); // send the cursor back to the document - - if (find(s,flags)) // search again, from the new position - { - //statusMessage("", 0); - found = true; - } - else - { - setTextCursor(original_cursor); // not found - restore the cursor to its original position - } - } - } - - - if (found) - { - if (replace) - { - // perform the 'replace' - insertPlainText (r); - - // highlight the replaced text - the current text cursor will be - // at the end of the replaced text, so we move it back to the start - // (anchored so that the text is selected) - QTextCursor cursor = textCursor(); // get the document's cursor - cursor.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, r.length()); - setTextCursor(cursor); // send the cursor back to the document - } - ensureCursorVisible(); - } - - else - { - //statusMessage(tr("Searched whole file, string not found"), 5000); - } - - return found; -} - - -// --------------------------------------------------------------------------- -// TextEdit::gotoLine -// triggered when the user asks to bring up the 'go to line' dialog -// --------------------------------------------------------------------------- - -void PlainTextEdit::gotoLine() -{ - // create the dialog if it does not already exist - - if (!gotoLineDialog_) - { - gotoLineDialog_ = new GotoLineDialog(this); - - connect(gotoLineDialog_, SIGNAL(gotoLine(int)), this, SLOT(gotoLine(int))); - } - - - // if created, set it up and display it - - if (gotoLineDialog_) - { - gotoLineDialog_->show(); - gotoLineDialog_->raise(); - gotoLineDialog_->activateWindow(); - gotoLineDialog_->setupUIBeforeShow(); - } -} - -// --------------------------------------------------------------------------- -// TextEdit::gotoLine -// triggered from the GotoLine dialog when the user wants to go to that line -// --------------------------------------------------------------------------- - -void PlainTextEdit::gotoLine(int line) -{ - int bn = 0; - QTextBlock b; - - if (line <= document()->blockCount()) - { - for (b = document()->begin(); b != document()->end(); b = b.next()) - { - if (bn == line-1) - { - QTextCursor cursor = textCursor(); // get the document's cursor - cursor.setPosition (b.position()); // set it to the right position - cursor.select(QTextCursor::LineUnderCursor); // select the whole line - setTextCursor(cursor); // send the cursor back to the document - break; - } - bn++; - } - } - - else - { - // line number outside range of line numbers - // TODO: disable the 'ok' button if the number is out of range - } -} - -//--------------------------------------------- -// Fontsize management -//--------------------------------------------- - -void PlainTextEdit::setFontProperty(VProperty* p) -{ - fontProp_=p; - fontProp_->addObserver(this); - updateFont(); -} - -void PlainTextEdit::wheelEvent(QWheelEvent *event) -{ - int fps=font().pointSize(); - - if(isReadOnly()) - { - QPlainTextEdit::wheelEvent(event); - if(font().pointSize() != fps) - fontSizeChangedByZoom(); - } - //For readOnly document the zoom does not work so we - //need this custom code! - else - { - if(event->modifiers() & Qt::ControlModifier) - { - const int delta = event->delta(); - if (delta < 0) - slotZoomOut(); - else if (delta > 0) - slotZoomIn(); - return; - } - - QPlainTextEdit::wheelEvent(event); - } -} - -void PlainTextEdit::slotZoomIn() -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 1) - zoomIn(); -#else - QFont f=font(); - int fps=f.pointSize(); - f.setPointSize(fps+1); - setFont(f); -#endif - fontSizeChangedByZoom(); -} - -void PlainTextEdit::slotZoomOut() -{ - int oriSize=font().pointSize(); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 1, 1) - zoomOut(); -#else - QFont f=font(); - int fps=f.pointSize(); - if(fps > 1) - { - f.setPointSize(fps-1); - setFont(f); - } -#endif - - if(font().pointSize() != oriSize) - fontSizeChangedByZoom(); -} - -void PlainTextEdit::fontSizeChangedByZoom() -{ - if(fontProp_) - fontProp_->setValue(font()); -} - -void PlainTextEdit::updateFont() -{ - if(fontProp_) - { - QFont f=fontProp_->value().value(); - if(font() != f) - setFont(f); - } -} - -void PlainTextEdit::notifyChange(VProperty* p) -{ - if(fontProp_ ==p) - { - setFont(p->value().value()); - } -} - - -void PlainTextEdit::mousePressEvent(QMouseEvent *e) -{ - if (hyperlinkEnabled_) - { - // left button only - if pressed, we just store the link that was clicked on - // - we don't want to do anything until the mouse button has been released and - // we know it hasd not been moved away from the hyperlinked text - - if (e->button() & Qt::LeftButton) - currentLink_ = anchorAt(e->pos()); - else - currentLink_ = QString(); - } - - QPlainTextEdit::mousePressEvent(e); -} - -void PlainTextEdit::mouseReleaseEvent(QMouseEvent *e) -{ - if (hyperlinkEnabled_) - { - // only activate the hyperlink if the user releases the left mouse button on the - // same link they were on when they pressed the button - - if ((e->button() & Qt::LeftButton) && !currentLink_.isEmpty()) - { - if (currentLink_ == anchorAt(e->pos())) - { - Q_EMIT hyperlinkActivated(currentLink_); - UiLog().dbg() << "clicked:" << currentLink_; - } - } - } - - QPlainTextEdit::mouseReleaseEvent(e); -} - - -void PlainTextEdit::mouseMoveEvent(QMouseEvent *e) -{ - if (hyperlinkEnabled_) - { - QString thisAnchor = anchorAt(e->pos()); - - if (!thisAnchor.isEmpty()) - { - viewport()->setCursor(Qt::PointingHandCursor); - } - else - { - viewport()->unsetCursor(); - } - } - - QPlainTextEdit::mouseMoveEvent(e); -} - diff -Nru ecflow-4.9.0/Viewer/src/PlainTextEdit.hpp ecflow-4.11.1/Viewer/src/PlainTextEdit.hpp --- ecflow-4.9.0/Viewer/src/PlainTextEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,106 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TEXTEDIT_HPP_ -#define TEXTEDIT_HPP_ - -#include - -#include "VProperty.hpp" - -class LineNumberArea; -class GotoLineDialog; - -class PlainTextEdit : public QPlainTextEdit, public VPropertyObserver -{ -Q_OBJECT - -public: - explicit PlainTextEdit(QWidget* parent = 0); - ~PlainTextEdit(); - - void lineNumberAreaPaintEvent(QPaintEvent *event); - int lineNumberAreaWidth(); - - bool showLineNumbers() {return showLineNum_;} - void setShowLineNumbers(bool b); - - void cursorRowCol(int *row, int *col); - QChar characterBehindCursor(QTextCursor *cursor=0); - - int numLinesSelected(); - bool findString(const QString &,QTextDocument::FindFlags,bool replace=false,const QString &r=emptyString_); - - void setFontProperty(VProperty* p); - void updateFont(); - void notifyChange(VProperty* p); - bool isHyperlinkEnabled() {return hyperlinkEnabled_;} - bool setHyperlinkEnabled(bool h); - -public Q_SLOTS: - void gotoLine(); - void slotZoomIn(); - void slotZoomOut(); - -private Q_SLOTS: - void updateLineNumberAreaWidth(int newBlockCount); - void updateLineNumberArea(const QRect &, int); - void gotoLine(int line); - -Q_SIGNALS: - void focusRegained (); - void focusLost(); - void fontSizeChangedByWheel(); - void hyperlinkActivated(QString link); - -protected: - void resizeEvent(QResizeEvent *event); - void focusInEvent(QFocusEvent *event); - void focusOutEvent(QFocusEvent *event); - void wheelEvent(QWheelEvent *event); - void mousePressEvent(QMouseEvent *e); - void mouseReleaseEvent(QMouseEvent *e); - void mouseMoveEvent(QMouseEvent *e); - -private: - void fontSizeChangedByZoom(); - - bool showLineNum_; - QWidget *lineNumArea_; - int rightMargin_; - bool hyperlinkEnabled_; - QString lastFindString_; - QString currentLink_; - QTextDocument::FindFlags lastFindFlags_; - GotoLineDialog *gotoLineDialog_; - static QString emptyString_; - - QColor numAreaBgCol_; - QColor numAreaFontCol_; - QColor numAreaSeparatorCol_; - QColor numAreaCurrentCol_; - VProperty *fontProp_; -}; - - -class LineNumberArea : public QWidget -{ -public: - explicit LineNumberArea(PlainTextEdit *editor) : QWidget(editor) {textEditor = editor;} - QSize sizeHint() const {return QSize(textEditor->lineNumberAreaWidth(), 0);} - -protected: - void paintEvent(QPaintEvent *event) { textEditor->lineNumberAreaPaintEvent(event);} - -private: - PlainTextEdit *textEditor; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/PlainTextSearchInterface.cpp ecflow-4.11.1/Viewer/src/PlainTextSearchInterface.cpp --- ecflow-4.9.0/Viewer/src/PlainTextSearchInterface.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,232 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "PlainTextSearchInterface.hpp" - -#include - - -PlainTextSearchInterface::PlainTextSearchInterface() : editor_(NULL) -{ -} - - -bool PlainTextSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, - QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode) -{ - if(!editor_) - return false; - - if(editor_->document()->isEmpty()) - return false; - - QTextCursor cursor(editor_->textCursor()); - - if (highlightAll) // if highlighting all matches, start from the start of the document - cursor.movePosition(QTextCursor::Start); - - else // move the cursor? - cursor.movePosition(move); - - - QList extraSelections; - bool found = false; - bool keepGoing = true; - int numMatches = 0; - - Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; - - while (keepGoing) - { - switch (matchMode) - { - case StringMatchMode::ContainsMatch: - { - cursor = editor_->document()->find(str, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - case StringMatchMode::WildcardMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - regexp.setPatternSyntax(QRegExp::Wildcard); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - case StringMatchMode::RegexpMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - - default: - { - break; - } - } - - - if (found) - { - if (highlightAll) - { - QTextEdit::ExtraSelection highlight; - highlight.cursor = cursor; - highlight.format.setBackground(highlightColour_); - extraSelections << highlight; - numMatches++; - } - else - { - editor_->setTextCursor(cursor); // mark the selection of the match - } - } - - - if (found && !highlightAll) // found a match and we only want one - stop here and select it - keepGoing = false; - - else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around - keepGoing = false; - - if (!found && highlightAll) // want to highlight all, but no more matches found - keepGoing = false; - - - - // not found, and we only want one match, then we need to wrap around and keep going - if (keepGoing) - { - if (!highlightAll) - { - cursor=editor_->textCursor(); - if (flags & QTextDocument::FindBackward) - cursor.movePosition(QTextCursor::End); - else - cursor.movePosition(QTextCursor::Start); - iteration = 1; // iteration=1 to avoid infinite wraparound! - } - } - } - - - if (highlightAll) - { - //char num[64]; - //sprintf(num, "%d", numMatches); - //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); - - editor_->setExtraSelections( extraSelections ); - } - - return (found); -} - -void PlainTextSearchInterface::automaticSearchForKeywords(bool userClickedReload) -{ - if(editor_->document()->isEmpty()) - return; - - bool performSearch = vpPerformAutomaticSearch_->value().toBool(); - - if (performSearch) - { - // search direction - QTextDocument::FindFlags findFlags; - QTextCursor cursor(editor_->textCursor()); - std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); - QTextCursor::MoveOperation move; - if (searchFrom == "bottom") - { - findFlags = QTextDocument::FindBackward; - move = QTextCursor::End; - } - else - { - move = QTextCursor::Start; - } - - // case sensitivity - bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); - if (caseSensitive) - findFlags = findFlags | QTextDocument::FindCaseSensitively; - - // string match mode - std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); - StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); - - // the term to be searched for - std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); - QString searchTerm = QString::fromStdString(searchTerm_s); - - // perform the search - bool found = findString (searchTerm, false, findFlags, move, 1, mode); - - if(!found) - { - if(userClickedReload) - { - // move the cursor to the start of the last line - gotoLastLine(); - } - } - } - else - { - // move the cursor to the start of the last line - gotoLastLine(); - } -} - -void PlainTextSearchInterface::refreshSearch() -{ - if(!editor_) - return; - - QTextCursor cursor(editor_->textCursor()); - if (cursor.hasSelection()) - { - cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); - editor_->setTextCursor(cursor); - } -} - - -void PlainTextSearchInterface::clearHighlights() -{ - if(!editor_) - return; - - QList empty; - editor_->setExtraSelections(empty); -} - -void PlainTextSearchInterface::disableHighlights() -{ - clearHighlights(); -} - - -void PlainTextSearchInterface::gotoLastLine() -{ - // move the cursor to the start of the last line - QTextCursor cursor = editor_->textCursor(); - cursor.movePosition(QTextCursor::End); - cursor.movePosition(QTextCursor::StartOfLine); - editor_->setTextCursor(cursor); - editor_->ensureCursorVisible(); -} diff -Nru ecflow-4.9.0/Viewer/src/PlainTextSearchInterface.hpp ecflow-4.11.1/Viewer/src/PlainTextSearchInterface.hpp --- ecflow-4.9.0/Viewer/src/PlainTextSearchInterface.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ -#define VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ - -#include "AbstractTextEditSearchInterface.hpp" - -class QPlainTextEdit; - -class PlainTextSearchInterface : public AbstractTextEditSearchInterface -{ -public: - PlainTextSearchInterface(); - void setEditor(QPlainTextEdit* e) {editor_=e;} - - bool findString (QString str, bool highlightAll, QTextDocument::FindFlags findFlags, - QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); - - void automaticSearchForKeywords(bool); - void refreshSearch(); - void clearHighlights(); - void disableHighlights(); - void enableHighlights() {} - bool highlightsNeedSearch() {return true;} - void gotoLastLine(); - -protected: - - QPlainTextEdit *editor_; - -}; - -#endif /* VIEWER_SRC_PLAINTEXTSEARCHINTERFACE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/PlainTextSearchLine.cpp ecflow-4.11.1/Viewer/src/PlainTextSearchLine.cpp --- ecflow-4.9.0/Viewer/src/PlainTextSearchLine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,333 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "PlainTextSearchLine.hpp" -#include "PlainTextSearchInterface.hpp" - -#include - -PlainTextSearchLine::PlainTextSearchLine(QWidget *parent) : - TextEditSearchLine(parent) -{ - interface_=new PlainTextSearchInterface; - TextEditSearchLine::setSearchInterface(interface_); -} - -PlainTextSearchLine::~PlainTextSearchLine() -{ - delete interface_; -} - -void PlainTextSearchLine::setEditor(QPlainTextEdit *e) -{ - PlainTextSearchInterface *pti=static_cast(interface_); - assert(pti); - pti->setEditor(e); -} - - -#if 0 -PlainTextSearchLine::PlainTextSearchLine(QWidget *parent) : - AbstractSearchLine(parent), - editor_(0), - highlightColour_(QColor(200, 255, 200)) -{ - connect(matchModeCb_,SIGNAL(currentIndexChanged(int)), - this, SLOT(matchModeChanged(int))); - - if(VProperty *p=VConfig::instance()->find("panel.search.highlightColour")) - { - highlightColour_=p->value().value(); - } -} - -PlainTextSearchLine::~PlainTextSearchLine() -{ - -} - -void PlainTextSearchLine::setEditor(QPlainTextEdit *e) -{ - editor_=e; -} - - -bool PlainTextSearchLine::findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, bool gotoStartOfWord, int iteration) -{ - QTextDocument::FindFlags flags = findFlags() | extraFlags; - - QTextCursor cursor(editor_->textCursor()); - - if (highlightAll) // if highlighting all matches, start from the start of the document - cursor.movePosition(QTextCursor::Start); - - else if (gotoStartOfWord) // go to start of word? - cursor.movePosition(QTextCursor::StartOfWord); - - - QList extraSelections; - bool found = false; - bool keepGoing = true; - int numMatches = 0; - - Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; - - while (keepGoing) - { - switch (matchModeCb_->currentMatchMode()) - { - case StringMatchMode::ContainsMatch: - { - cursor = editor_->document()->find(str, cursor, flags); // perform the search - found = (!cursor.isNull()); - - break; - } - case StringMatchMode::WildcardMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - regexp.setPatternSyntax(QRegExp::Wildcard); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - - break; - } - case StringMatchMode::RegexpMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - - break; - } - - default: - { - break; - } - } - - - if (found) - { - if (highlightAll) - { - QTextEdit::ExtraSelection highlight; - highlight.cursor = cursor; - highlight.format.setBackground(highlightColour_); - extraSelections << highlight; - numMatches++; - } - else - { - editor_->setTextCursor(cursor); // mark the selection of the match - } - } - - - if (found && !highlightAll) // found a match and we only want one - stop here and select it - keepGoing = false; - - else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around - keepGoing = false; - - if (!found && highlightAll) // want to highlight all, but no more matches found - keepGoing = false; - - - - // not found, and we only want one match, then we need to wrap around and keep going - if (keepGoing) - { - if (!highlightAll) - { - cursor=editor_->textCursor(); - if (extraFlags & QTextDocument::FindBackward) - cursor.movePosition(QTextCursor::End); - else - cursor.movePosition(QTextCursor::Start); - editor_->setTextCursor(cursor); - iteration = 1; // iteration=1 to avoid infinite wraparound! - } - } - } - - - if (highlightAll) - { - //char num[64]; - //sprintf(num, "%d", numMatches); - //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); - - editor_->setExtraSelections( extraSelections ); - } - - return (found); -} - - - -void PlainTextSearchLine::highlightMatches(QString txt) -{ - if (!txt.isEmpty()) - findString(txt, true, 0, true, 0); // highlight all matches -} - - -void PlainTextSearchLine::slotHighlight() -{ - //UserMessage::message(UserMessage::DBG, false," highlight: " + searchLine_->text().toStdString()); - - highlightAllTimer_.stop(); - - if (highlightAll()) - highlightMatches(searchLine_->text()); -} - - -void PlainTextSearchLine::slotFind(QString txt) -{ - if(!editor_) - return; - - highlightAllTimer_.stop(); - bool found = findString(txt, false, 0, true, 0); // find the next match - - if (!isEmpty()) // there is a search term supplied by the user - { - // don't highlight the matches immediately - this can be expensive for large files, - // and we don't want to highlight each time the user types a new character; wait - // a moment and then start the highlight - highlightAllTimer_.setInterval(500); - highlightAllTimer_.disconnect(); - connect(&highlightAllTimer_, SIGNAL(timeout()), this, SLOT(slotHighlight())); - highlightAllTimer_.start(); - } - else - { - clearHighlights(); - } - - updateButtons(found); -} - -void PlainTextSearchLine::slotFindNext() -{ - if(!editor_) - return; - - bool found = findString(searchLine_->text(), false, 0, false, 0); - updateButtons(found); -} - -void PlainTextSearchLine::slotFindPrev() -{ - if(!editor_) - return; - - bool found = findString(searchLine_->text(), false, QTextDocument::FindBackward, false, 0); - updateButtons(found); -} - -QTextDocument::FindFlags PlainTextSearchLine::findFlags() -{ - QTextDocument::FindFlags flags; - - if(caseSensitive()) - { - flags = flags | QTextDocument::FindCaseSensitively; - } - - if(wholeWords()) - { - flags = flags | QTextDocument::FindWholeWords; - } - - return flags; -} - - - - - -// PlainTextSearchLine::refreshSearch -// performed when the user changes search parameters such as case sensitivity - we want to -// re-do the search from the current point, but if the current selection still matches then -// we'd like it to be found first. - -void PlainTextSearchLine::refreshSearch() -{ - // if there's something selected already then move the cursor to the start of the line and search again - QTextCursor cursor(editor_->textCursor()); - if (cursor.hasSelection()) - { - cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); - editor_->setTextCursor(cursor); - } - slotFindNext(); - slotHighlight(); -} - - -void PlainTextSearchLine::clearHighlights() -{ - QList empty; - editor_->setExtraSelections(empty); -} - - -void PlainTextSearchLine::matchModeChanged(int notUsed) -{ - if(matchModeCb_->currentMatchMode() == StringMatchMode::ContainsMatch) - actionWholeWords_->setEnabled(true); - else - actionWholeWords_->setEnabled(false); - - refreshSearch(); -} - - -void PlainTextSearchLine::on_actionCaseSensitive__toggled(bool b) -{ - AbstractSearchLine::on_actionCaseSensitive__toggled(b); - - refreshSearch(); -} - - -void PlainTextSearchLine::on_actionWholeWords__toggled(bool b) -{ - AbstractSearchLine::on_actionWholeWords__toggled(b); - - refreshSearch(); -} - -void PlainTextSearchLine::on_actionHighlightAll__toggled(bool b) -{ - AbstractSearchLine::on_actionHighlightAll__toggled(b); - - if (b) // user switched on the highlights - slotHighlight(); - else // user switched off the highlights - clearHighlights(); - - - refreshSearch(); -} - -void PlainTextSearchLine::slotClose() -{ - AbstractSearchLine::slotClose(); - clearHighlights(); -} -#endif diff -Nru ecflow-4.9.0/Viewer/src/PlainTextSearchLine.hpp ecflow-4.11.1/Viewer/src/PlainTextSearchLine.hpp --- ecflow-4.9.0/Viewer/src/PlainTextSearchLine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PlainTextSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef PLAINTEXTSEARCHLINE_HPP_ -#define PLAINTEXTSEARCHLINE_HPP_ - -#include - -#include "TextEditSearchLine.hpp" - -class AbstractTextSearchInterface; - -class PlainTextSearchLine : public TextEditSearchLine -{ -public: - explicit PlainTextSearchLine(QWidget *parent=0); - ~PlainTextSearchLine(); - void setEditor(QPlainTextEdit*); - -private: - //The interface is set internally - void setSearchInterface(AbstractTextSearchInterface*) {} - -}; - - -#if 0 -class PlainTextSearchLine : public AbstractSearchLine -{ - Q_OBJECT - -public: - explicit PlainTextSearchLine(QWidget *parent); - ~PlainTextSearchLine(); - void setEditor(QPlainTextEdit*); - -public Q_SLOTS: - void slotFind(QString); - void slotFindNext(); - void slotFindPrev(); - void slotFindNext(bool) {slotFindNext();} - void slotFindPrev(bool) {slotFindPrev();} - void matchModeChanged(int newIndex); - void on_actionCaseSensitive__toggled(bool); - void on_actionWholeWords__toggled(bool); - void on_actionHighlightAll__toggled(bool); - void slotClose(); - void slotHighlight(); - -protected: - QTextDocument::FindFlags findFlags(); - bool findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, bool gotoStartOfWord, int iteration); - void refreshSearch(); - void highlightMatches(QString txt); - void clearHighlights(); - QTimer highlightAllTimer_; - QPlainTextEdit* editor_; - QColor highlightColour_; -}; -#endif - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/PropertyDialog.cpp ecflow-4.11.1/Viewer/src/PropertyDialog.cpp --- ecflow-4.9.0/Viewer/src/PropertyDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,237 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "PropertyDialog.hpp" - -#include -#include -#include - -#include "ConfigListDelegate.hpp" -#include "IconProvider.hpp" -#include "PropertyEditor.hpp" -#include "SessionHandler.hpp" -#include "VConfig.hpp" -#include "VConfigLoader.hpp" -#include "VProperty.hpp" -#include "WidgetNameProvider.hpp" - -VProperty* PropertyDialog::prop_=0; - -PropertyDialog::PropertyDialog(QWidget* parent) : - QDialog(parent), - configChanged_(false) -{ - setupUi(this); - - QString wt=windowTitle(); - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - QFont f; - f.setBold(true); - QFontMetrics fm(f); - int maxW=fm.width("Server options ATAT"); - list_->setMaximumWidth(maxW+6); - list_->setFont(f); - - list_->setItemDelegate(new ConfigListDelegate(32,maxW,this)); - - connect(list_,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this, SLOT(slotChangePage(QListWidgetItem*,QListWidgetItem*))); - - connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), - this,SLOT(slotButton(QAbstractButton*))); - - build(); - - readSettings(); - - if(list_->count() >0 && list_->currentRow() == -1) - list_->setCurrentRow(0); - - //Assign name to each object - WidgetNameProvider::nameChildren(this); -} - -void PropertyDialog::closeEvent(QCloseEvent * event) -{ - event->accept(); - writeSettings(); -} - -//Build the property tree from the the definitions -void PropertyDialog::build() -{ - if(prop_) - { - Q_FOREACH(VProperty* vPage,prop_->children()) - { - if(vPage->param("visible") == "false") - continue; - - QPixmap pix(32,32); - QPixmap edPix; - QString iconStr=vPage->param("icon"); - - if(!iconStr.isEmpty()) - { - IconProvider::add(":/viewer/" + iconStr,iconStr); - pix=IconProvider::pixmap(iconStr,32); - edPix=IconProvider::pixmap(iconStr,20); - } - - PropertyEditor* ed=new PropertyEditor(this); - ed->setObjectName(vPage->param("label")); - ed->edit(vPage,edPix); - - addPage(ed,pix,vPage->param("label")); - editors_ << ed; - } - } -} - -void PropertyDialog::apply() -{ - manageChange(true); -} - -void PropertyDialog::accept() -{ - manageChange(false); - writeSettings(); - QDialog::accept(); -} - - -void PropertyDialog::reject() -{ - writeSettings(); - QDialog::reject(); -} - -void PropertyDialog::addPage(QWidget *w,QPixmap pix,QString txt) -{ - QListWidgetItem *item = new QListWidgetItem(list_); - item->setData(Qt::DecorationRole, pix); - item->setData(Qt::DisplayRole,txt); - item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - page_->addWidget(w); -} - -void PropertyDialog::slotChangePage(QListWidgetItem *current, QListWidgetItem *previous) -{ - if (!current) - current = previous; - - page_->setCurrentIndex(list_->row(current)); -} - -void PropertyDialog::slotButton(QAbstractButton* pb) -{ - if(buttonBox_->buttonRole(pb) == QDialogButtonBox::ApplyRole) - { - apply(); - } -} - -void PropertyDialog::showPage(QString path) -{ - QStringList lst=path.split("."); - if(lst.count() > 0) - { - QString pageName=lst[0]; - for(int i=0; i < editors_.count(); i++) - { - PropertyEditor *ed=editors_[i]; - if(VProperty* prop=ed->property()) - { - if(prop->name() == pageName) - { - list_->setCurrentRow(i); - return; - } - } - } - } -} - -void PropertyDialog::manageChange(bool inApply) -{ - bool hasChange=false; - Q_FOREACH(PropertyEditor* ed,editors_) - { - if(ed->applyChange()) - { - hasChange=true; - VProperty* p=ed->property(); - if(p && p->name() != "server") - { - if(inApply) - Q_EMIT configChanged(); - else - configChanged_=true; - } - } - } - - if(hasChange) - VConfig::instance()->saveSettings(); -} - -void PropertyDialog::load(VProperty* p) -{ - prop_=p; -} - -void PropertyDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("PropertyDialog")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.setValue("current",list_->currentRow()); - settings.endGroup(); -} - -void PropertyDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("PropertyDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(550,540)); - } - - if(settings.contains("current")) - { - int current=settings.value("current").toInt(); - if(current >=0) - list_->setCurrentRow(current); - } - settings.endGroup(); -} - - -static SimpleLoader loader("gui"); diff -Nru ecflow-4.9.0/Viewer/src/PropertyDialog.hpp ecflow-4.11.1/Viewer/src/PropertyDialog.hpp --- ecflow-4.9.0/Viewer/src/PropertyDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef PROPERTYDIALOG_INC_ -#define PROPERTYDIALOG_INC_ - -#include "ui_PropertyDialog.h" - -#include - -class QAbstractButton; - -class PropertyEditor; -class VProperty; - -class PropertyDialog : public QDialog, private Ui::PropertyDialog -{ - -Q_OBJECT - -public: - explicit PropertyDialog(QWidget *parent=0); - ~PropertyDialog() {} - - bool isConfigChanged() const {return configChanged_;} - void showPage(QString); - - //Called from VConfigLoader - static void load(VProperty*); - -public Q_SLOTS: - void accept(); - void reject(); - void slotChangePage(QListWidgetItem *current, QListWidgetItem *previous); - void slotButton(QAbstractButton*); - -Q_SIGNALS: - void configChanged(); - -private: - void build(); - void addPage(QWidget *w,QPixmap pix,QString txt); - void manageChange(bool); - void apply(); - - void closeEvent(QCloseEvent * event); - void readSettings(); - void writeSettings(); - - QList editors_; - bool configChanged_; - - static VProperty* prop_; - -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/PropertyDialog.ui ecflow-4.11.1/Viewer/src/PropertyDialog.ui --- ecflow-4.9.0/Viewer/src/PropertyDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ - - - PropertyDialog - - - - 0 - 0 - 924 - 695 - - - - Preferences - - - true - - - - 2 - - - 2 - - - 2 - - - 2 - - - 2 - - - - - 4 - - - 2 - - - 2 - - - 2 - - - 2 - - - - - - 0 - 0 - - - - - - - - - 1 - 0 - - - - - - - - - - QDialogButtonBox::Apply|QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox_ - accepted() - PropertyDialog - accept() - - - 199 - 278 - - - 199 - 149 - - - - - buttonBox_ - rejected() - PropertyDialog - reject() - - - 199 - 278 - - - 199 - 149 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/PropertyEditor.cpp ecflow-4.11.1/Viewer/src/PropertyEditor.cpp --- ecflow-4.9.0/Viewer/src/PropertyEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,609 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "PropertyEditor.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "ChangeNotifyEditor.hpp" -#include "IconProvider.hpp" -#include "PropertyLine.hpp" -#include "VConfig.hpp" -#include "VProperty.hpp" - -PropertyEditor::PropertyEditor(QWidget* parent) : QWidget(parent), - group_(0), - currentGrid_(0), - holder_(0), - lineLabelLen_(-1) -{ - setupUi(this); - - headerWidget_->setProperty("editorHeader","1"); - scArea_->setProperty("editor","1"); - scAreaContents_->setProperty("editorArea","1"); - - pixLabel_->clear(); -} - -PropertyEditor::~PropertyEditor() -{ -} - -void PropertyEditor::edit(VProperty * vGroup,QPixmap pix) -{ - clear(); - - group_=vGroup; - - QString txt=group_->param("desc"); - headerLabel_->setText(txt); - - pixLabel_->setPixmap(pix); - - build(); -} - -void PropertyEditor::edit(VProperty * vGroup,QString serverName) -{ - clear(); - - group_=vGroup; - - headerWidget_->hide(); - - serverName_=serverName; - - build(); -} - -void PropertyEditor::clear() -{ - if(holder_) - { - vBox_->removeWidget(holder_); - delete holder_; - holder_=NULL; - } - - currentGrid_=0; - lineItems_.clear(); -} - - -//Build the property tree from the the definitions -void PropertyEditor::build() -{ - if(!group_) - return; - - assert(holder_==NULL); - - holder_=new QWidget(scAreaContents_); - holder_->setObjectName("h"); - QVBoxLayout *vb=new QVBoxLayout(holder_); - vb->setContentsMargins(0,0,0,0); - vBox_->addWidget(holder_); - - //Loop over the children of the group - Q_FOREACH(VProperty* vProp,group_->children()) - { - addItem(vProp,vb,holder_); - } - - addRules(); - addHelpers(); -} - -void PropertyEditor::addRules() -{ - Q_FOREACH(PropertyLine* line,lineItems_) - { - if(VProperty* ruleProp=line->ruleProperty()) - { - Q_FOREACH(PropertyLine* ll,lineItems_) - { - if(ll->property() == ruleProp) - { - line->addRuleLine(ll); - break; - } - } - } - } -} - - -void PropertyEditor::addHelpers() -{ - QMap lineMap; - Q_FOREACH(PropertyLine* line,lineItems_) - { - lineMap[line->property()->path()]=line; - } - - Q_FOREACH(PropertyLine* line,lineItems_) - { - QString h=line->guiProperty()->param("helpers"); - if(!h.isEmpty()) - { - Q_FOREACH(QString s,h.split("/")) - { - if(PropertyLine* hl=lineMap.value(s.toStdString(),NULL)) - { - line->addHelper(hl); - } - } - } - } -} - - -void PropertyEditor::addItem(VProperty* vProp,QVBoxLayout *layout,QWidget *parent) -{ - if(vProp->name() == "line") - { - if (!currentGrid_) - { - currentGrid_=new QGridLayout(); - layout->addLayout(currentGrid_); - } - addLine(vProp,currentGrid_,parent); - } - - else if(vProp->name() == "group") - { - currentGrid_=0; - addGroup(vProp,layout,parent); - } - else if(vProp->name() == "grid") - { - currentGrid_=0; - addGrid(vProp,layout,parent); - } - else if(vProp->name() == "custom-notification") - { - currentGrid_=0; - addNotification(vProp,layout,parent); - } - else if(vProp->name() == "note") - { - if(currentGrid_) - { - addNote(vProp,currentGrid_,parent); - } - else - { - addNote(vProp,layout,parent); - } - } - else if(vProp->name() == "tabs") - { - currentGrid_=0; - addTabs(vProp,layout,parent); - } - -} - -PropertyLine* PropertyEditor::addLine(VProperty *vProp,QGridLayout *gridLayout,QWidget *parent) -{ - PropertyLine* item = PropertyLineFactory::create(vProp,true,parent); - - if(item) - { - item->init(); - //item->reset(vProp->link()->value()); - - int row=gridLayout->rowCount(); - - QLabel* lw=item->label(); - QLabel* slw=item->suffixLabel(); - - if(lw) - { - //If lineLabelLen_ is set we adjust the size of the - //line labels so that the editor widgets could be aligned - if(lineLabelLen_ > 0) - { - QFont f; - QFontMetrics fm(f); - QString s; - s=s.leftJustified(lineLabelLen_,'A'); - lw->setMinimumWidth(fm.width(s)); - } - - gridLayout->addWidget(lw,row,0,Qt::AlignLeft); - - if(slw) - { - QHBoxLayout* hb=new QHBoxLayout; - hb->addWidget(item->item()); - hb->addWidget(slw); - gridLayout->addLayout(hb,row,1,Qt::AlignLeft); - } - else - { - if(item->canExpand()) - { - QHBoxLayout* hb=new QHBoxLayout; - hb->addWidget(item->item()); - gridLayout->addLayout(hb,row,1,Qt::AlignLeft); - } - else - gridLayout->addWidget(item->item(),row,1,Qt::AlignLeft); - } - } - else - { - gridLayout->addWidget(item->item(),row,0,1,2,Qt::AlignLeft); - } - - QWidget *bw=item->button(); - if(bw) - gridLayout->addWidget(bw,row,2); - - - QToolButton* defTb=item->defaultTb(); - if(defTb) - { - gridLayout->addWidget(defTb,row,3); - } - - QToolButton* masterTb=item->masterTb(); - if(masterTb) - { - gridLayout->addWidget(masterTb,row,4); - } - - connect(item,SIGNAL(changed()), - this,SIGNAL(changed())); - - lineItems_ << item; - } - - return item; -} - -void PropertyEditor::addGroup(VProperty* vProp,QVBoxLayout * layout,QWidget *parent) -{ - if(vProp->name() != "group") - return; - - QGroupBox *groupBox = new QGroupBox(vProp->param("title"),parent); - groupBox->setObjectName("editorGroupBox"); - QGridLayout *grid=new QGridLayout(); - grid->setColumnStretch(1,1); - groupBox->setLayout(grid); - layout->addWidget(groupBox); - - currentGrid_=grid; - - //Loop over the children of the group - Q_FOREACH(VProperty* chProp,vProp->children()) - { - //Add each item to the the editor - addItem(chProp,layout,groupBox); - } - currentGrid_=0; -} - -void PropertyEditor::addGrid(VProperty* vProp,QVBoxLayout *layout,QWidget *parent) -{ - if(vProp->name() != "grid") - return; - - QGroupBox *groupBox = new QGroupBox(vProp->param("title"),parent); - groupBox->setObjectName("editorGroupBox"); - QGridLayout* grid=new QGridLayout(); - groupBox->setLayout(grid); - - layout->addWidget(groupBox); - - //Add header - for(int i=1; i < 10; i++) - { - QString h=vProp->param("h" + QString::number(i)); - - if(h.isEmpty()) - { - grid->setColumnStretch(i+1,1); - break; - } - - h+=" "; - QLabel* hLabel=new QLabel(h,groupBox); - grid->addWidget(hLabel,0,i,Qt::AlignHCenter); - } - - //Add rows - Q_FOREACH(VProperty* chProp,vProp->children()) - { - addGridRow(chProp,grid,groupBox); - } -} - - -void PropertyEditor::addGridRow(VProperty* vProp,QGridLayout *grid,QWidget *parent) -{ - if(vProp->name() != "row") - { - if(vProp->name() == "note") - { - QLabel *empty=new QLabel(" ",parent); - grid->addWidget(empty,grid->rowCount(),0,1,-1,Qt::AlignVCenter); - QLabel *label=new QLabel("   Note: " + vProp->value().toString(),parent); - grid->addWidget(label,grid->rowCount(),0,1,-1,Qt::AlignVCenter); - } - return; - } - - int row=grid->rowCount(); - QString labelText=vProp->param("label"); - QLabel* label=new QLabel(labelText,parent); - grid->addWidget(label,row,0); - - int col=1; - Q_FOREACH(VProperty* chProp,vProp->children()) - { - if(chProp->name() == "line") - { - PropertyLine* item = PropertyLineFactory::create(chProp,false,parent); - - if(item) - { - item->init(); - //item->reset(chProp->link()->value()); - - //QLabel* lw=item->label(); - //QLabel* slw=item->suffixLabel(); - - //gridLayout->addWidget(item->item(),row,col,Qt::AlignLeft); - - /*QWidget *bw=item->button(); - if(bw) - gridLayout->addWidget(bw,row,2);*/ - - - QToolButton* defTb=item->defaultTb(); - QToolButton* masterTb=item->masterTb(); - if(defTb || masterTb) - { - QHBoxLayout *hb=new QHBoxLayout(); - hb->addWidget(item->item()); - if(defTb) - hb->addWidget(defTb); - if(masterTb) - hb->addWidget(masterTb); - - hb->addSpacing(15); - hb->addStretch(1); - grid->addLayout(hb,row,col); - } - else - { - grid->addWidget(item->item(),row,col,Qt::AlignLeft); - } - - connect(item,SIGNAL(changed()), - this,SIGNAL(changed())); - lineItems_ << item; - col++; - } - } - } - - -} - -void PropertyEditor::addNotification(VProperty* vProp,QVBoxLayout* layout,QWidget *parent) -{ - if(vProp->name() != "custom-notification") - return; - - //ChangeNotifyEditor* ne=new ChangeNotifyEditor(parent); - - QTabWidget* tab=new QTabWidget(parent); - - bool useGroup=(vProp->param("group") == "true"); - - if(useGroup) - { - QString labelText=vProp->param("title"); - QGroupBox *groupBox = new QGroupBox(labelText,parent); - groupBox->setObjectName("editorGroupBox"); - QVBoxLayout* vb=new QVBoxLayout(); - groupBox->setLayout(vb); - vb->addWidget(tab); - layout->addWidget(groupBox); - - } - else - { - layout->addWidget(tab); - } - - //Add rows - Q_FOREACH(VProperty* chProp,vProp->children()) - { - if(chProp->name() == "row") - { - QString labelText=chProp->param("label"); - - QList lineLst; - - QWidget* w=new QWidget(parent); - QVBoxLayout* vb=new QVBoxLayout(w); - //vb->setContentsMargins(4,4,4,4); - - currentGrid_=0; - - if(VProperty *root=VConfig::instance()->find(chProp->param("root").toStdString())) - { - QLabel *labelDesc=new QLabel(tr("Description: ") + root->param("description") + "",w); - //labelDesc->setProperty("editorNotifyHeader","1"); - vb->addWidget(labelDesc); - vb->addSpacing(5); - } - - int lineLstPos=lineItems_.count(); - Q_FOREACH(VProperty* lineProp,chProp->children()) - { - addItem(lineProp,vb,w); - } - for(int i=lineLstPos; i < lineItems_.count(); i++) - lineLst << lineItems_[i]; - - tab->addTab(w,labelText); - - //Connect up different components - PropertyLine* enabledLine=0; - PropertyLine* popupLine=0; - PropertyLine* soundLine=0; - Q_FOREACH(PropertyLine* pl,lineLst) - { - if(pl->property()->name() == "enabled") - { - enabledLine=pl; - } - if(pl->property()->name() == "popup") - { - popupLine=pl; - } - if(pl->property()->name() == "sound") - { - soundLine=pl; - } - } - - if(enabledLine) - { - if(popupLine) - { - connect(enabledLine,SIGNAL(changed(QVariant)), - popupLine,SLOT(slotEnabled(QVariant))); - //init - popupLine->slotEnabled(enabledLine->property()->value()); - } - if(soundLine) - { - connect(enabledLine,SIGNAL(changed(QVariant)), - soundLine,SLOT(slotEnabled(QVariant))); - //init - soundLine->slotEnabled(enabledLine->property()->value()); - } - } - - //ne->addRow(labelText,lineLst,w); - } - } -} - -void PropertyEditor::addTabs(VProperty* vProp,QVBoxLayout *layout,QWidget* parent) -{ - if(vProp->name() != "tabs") - return; - - QTabWidget *t=new QTabWidget(parent); - t->setObjectName("tab"); - layout->addWidget(t); - - Q_FOREACH(VProperty* chProp,vProp->children()) - { - if(chProp->name() == "tab") - { - addTab(chProp,t); - } - } -} - -void PropertyEditor::addTab(VProperty* vProp,QTabWidget* tab) -{ - if(vProp->name() != "tab") - return; - - QWidget *w=new QWidget(tab); - QVBoxLayout* vb=new QVBoxLayout(); - w->setLayout(vb); - - tab->addTab(w,vProp->param("label")); - - - if(!vProp->param("adjustLineLabel").isEmpty()) - { - lineLabelLen_=vProp->param("adjustLineLabel").toInt(); - if(lineLabelLen_ <= 0 || lineLabelLen_ > 300) - lineLabelLen_=-1; - } - - Q_FOREACH(VProperty* chProp,vProp->children()) - { - addItem(chProp,vb,w); - } - - vb->addStretch(1); -} - -void PropertyEditor::addNote(VProperty* vProp,QVBoxLayout* layout,QWidget *parent) -{ - if(vProp->name() != "note") - return; - - QString txt=vProp->value().toString(); - txt.replace("%SERVER%",(serverName_.isEmpty())?"?":"" + serverName_ + ""); - - layout->addSpacing(5); - QLabel *label=new QLabel("Note: " + txt,parent); - layout->addWidget(label); -} - -void PropertyEditor::addNote(VProperty* vProp,QGridLayout* layout,QWidget *parent) -{ - if(vProp->name() != "note") - return; - - QString txt=vProp->value().toString(); - txt.replace("%SERVER%",(serverName_.isEmpty())?"?":"" + serverName_ + ""); - - //QLabel *empty=new QLabel(" ",parent); - //layout->addWidget(empty,layout->rowCount(),0,1,-1,Qt::AlignVCenter); - //QLabel *label=new QLabel("   Note: " + txt,parent); - - //QFrame* fr=new QFrame(parent); - //fr->setFrameShape(QFrame::HLine); - //layout->addWidget(fr,layout->rowCount(),0,1,-1,Qt::AlignVCenter); - - QLabel *label=new QLabel("
         Note: " + txt + "
      ",parent); - label->setWordWrap(true); - layout->addWidget(label,layout->rowCount(),0,1,-1,Qt::AlignVCenter); -} - - -bool PropertyEditor::applyChange() -{ - bool changed=false; - //Loop over the top level properties (groups) in the browser - Q_FOREACH(PropertyLine* item, lineItems_) - { - //Sync the changes to VConfig - if(item->applyChange()) - { - changed=true; - } - } - - return changed; -} - diff -Nru ecflow-4.9.0/Viewer/src/PropertyEditor.hpp ecflow-4.11.1/Viewer/src/PropertyEditor.hpp --- ecflow-4.9.0/Viewer/src/PropertyEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,67 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef PROPERTYEDITOR_INC_ -#define PROPERTYEDITOR_INC_ - -#include - -#include "ui_PropertyEditor.h" - -class QGridLayout; -class QTabWidget; - -class PropertyLine; -class VProperty; - -class PropertyEditor : public QWidget, protected Ui::PropertyEditor -{ -Q_OBJECT - -public: - explicit PropertyEditor(QWidget *parent=0); - ~PropertyEditor(); - - void edit(VProperty*,QPixmap pixmap); - void edit(VProperty*,QString label); - bool applyChange(); - VProperty* property() const {return group_;} - -Q_SIGNALS: - void changed(); - -private: - void clear(); - void build(); - void addRules(); - void addHelpers(); - - void addItem(VProperty*,QVBoxLayout*,QWidget*); - PropertyLine* addLine(VProperty* vProp,QGridLayout* grid,QWidget*); - void addGroup(VProperty*,QVBoxLayout*,QWidget*); - void addGrid(VProperty*,QVBoxLayout*,QWidget*); - void addGridRow(VProperty* prop,QGridLayout *grid,QWidget*); - void addNotification(VProperty* prop,QVBoxLayout*,QWidget*); - void addTabs(VProperty*,QVBoxLayout*,QWidget*); - void addTab(VProperty*,QTabWidget*); - void addNote(VProperty* vProp,QVBoxLayout*,QWidget*); - void addNote(VProperty* vProp,QGridLayout* layout,QWidget*); - - VProperty* group_; - QGridLayout* currentGrid_; - QList lineItems_; - QString serverName_; - QWidget* holder_; - int lineLabelLen_; -}; - - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/PropertyEditor.ui ecflow-4.11.1/Viewer/src/PropertyEditor.ui --- ecflow-4.9.0/Viewer/src/PropertyEditor.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyEditor.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,132 +0,0 @@ - - - PropertyEditor - - - - 0 - 0 - 502 - 506 - - - - Form - - - - 2 - - - 0 - - - - - - 1 - - - - - false - - - QFrame::StyledPanel - - - TextLabel - - - 4 - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - TextLabel - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 5 - 20 - - - - - - - - - - - true - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - - 0 - 0 - 498 - 472 - - - - true - - - - QLayout::SetMinAndMaxSize - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/PropertyLine.cpp ecflow-4.11.1/Viewer/src/PropertyLine.cpp --- ecflow-4.9.0/Viewer/src/PropertyLine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyLine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1010 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "PropertyLine.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ComboMulti.hpp" -#include "Sound.hpp" - -#include - -static std::map* makers = 0; - -FontSizeSpin::FontSizeSpin(QWidget *parent) : QSpinBox(parent) -{ -} - -void FontSizeSpin::setFamily(QString family) -{ - QFontDatabase db; - vals_=db.pointSizes(family); - setRange(0,vals_.count()-1); -} - -QString FontSizeSpin::textFromValue(int value) const -{ - if(value >=0 && value < vals_.count()) - return QString::number(vals_.at(value)); - - return QString(); -} - - - -//========================================================================= -// -// PropertyLineFactory -// -//========================================================================= - -PropertyLineFactory::PropertyLineFactory(VProperty::GuiType type) -{ - if(makers == 0) - makers = new std::map; - - (*makers)[type] = this; -} - -PropertyLineFactory::~PropertyLineFactory() -{ - // Not called -} - -PropertyLine* PropertyLineFactory::create(VProperty* p,bool addLabel,QWidget* parent) -{ - if(!p || !p->link()) - return 0; - - VProperty::GuiType t=p->link()->guiType(); - std::map::iterator j = makers->find(t); - if(j != makers->end()) - return (*j).second->make(p,addLabel,parent); - - return 0; -} - -//========================================================================= -// -// PropertyLine -// -//========================================================================= - -PropertyLine::PropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : - QObject(parent), - prop_(NULL), - guiProp_(guiProp), - label_(0), - suffixLabel_(0), - defaultTb_(0), - masterTb_(0), - enabled_(true), - doNotEmitChange_(false), - ruleLine_(0) -{ - prop_=guiProp_->link(); - assert(prop_); - - setObjectName(guiProp->name()); - - oriVal_=prop_->value(); - - if(addLabel) - { - label_=new QLabel(prop_->param("label"),parent); - label_->setToolTip(prop_->param("tooltip")); - } - - QString suffixText=prop_->param("suffix"); - if(!suffixText.isEmpty()) - { - suffixLabel_=new QLabel(suffixText,parent); - } - - defaultTb_= new QToolButton(parent); - defaultTb_->setObjectName("default_" + prop_->name()); - defaultTb_->setToolTip(tr("Reset to default value")); - defaultTb_->setIcon(QPixmap(":/viewer/reset_to_default.svg")); - defaultTb_->setAutoRaise(true); - - connect(defaultTb_,SIGNAL(clicked(bool)), - this,SLOT(slotResetToDefault(bool))); - - if(prop_->master()) - { - masterTb_=new QToolButton(parent); - masterTb_->setObjectName("master_" + prop_->name()); - masterTb_->setCheckable(true); - masterTb_->setText("Use global"); - masterTb_->setToolTip(tr("Use global server settings")); - masterTb_->setIcon(QPixmap(":/viewer/chain.svg")); - masterTb_->setAutoRaise(true); - masterTb_->setChecked(prop_->useMaster()); - - connect(masterTb_,SIGNAL(toggled(bool)), - this,SLOT(slotMaster(bool))); - } -} - -PropertyLine::~PropertyLine() -{ -} - -void PropertyLine::init() -{ - doNotEmitChange_=true; - if(prop_->master()) - { - if(masterTb_->isChecked() != prop_->useMaster()) - masterTb_->setChecked(prop_->useMaster()); - else - slotMaster(prop_->useMaster()); - } - else - { - slotReset(prop_->value()); - } - doNotEmitChange_=false; - - if(item()) - item()->setToolTip(prop_->param("tooltip")); -} - -void PropertyLine::slotResetToDefault(bool) -{ - slotReset(prop_->defaultValue()); - checkState(); -} - -void PropertyLine::slotEnabled(QVariant v) -{ - if(enabled_ != v.toBool()) - { - if(!masterTb_ || !masterTb_->isChecked()) - { - enabled_=v.toBool(); - checkState(); - } - } -} - -void PropertyLine::checkState() -{ - if(label_) - { - label_->setEnabled(enabled_); - } - if(masterTb_) - { - masterTb_->setEnabled(enabled_); - } - if(suffixLabel_) - { - suffixLabel_->setEnabled(enabled_); - } - - defaultTb_->setEnabled(enabled_); - - setEnabledEditable(enabled_); - - if(masterTb_ && masterTb_->isChecked()) - return; - - if(enabled_) - { - if(prop_->defaultValue() != currentValue()) - defaultTb_->setEnabled(true); - else - defaultTb_->setEnabled(false); - } -} - -bool PropertyLine::applyMaster() -{ - if(masterTb_ && prop_->useMaster() != masterTb_->isChecked()) - { - prop_->setUseMaster(masterTb_->isChecked()); - return true; - } - return false; -} - - -void PropertyLine::slotMaster(bool b) -{ - if(b) - { - slotReset(prop_->master()->value()); - defaultTb_->setEnabled(false); - setEnabledEditable(false); - } - else - { - slotReset(prop_->value()); - defaultTb_->setEnabled(true); - checkState(); - setEnabledEditable(true); - } - - Q_EMIT masterChanged(b); - - valueChanged(); -} - -void PropertyLine::slotReset(VProperty* prop,QVariant v) -{ - if(prop == prop_) - slotReset(v); -} - -void PropertyLine::valueChanged() -{ - if(!doNotEmitChange_) - Q_EMIT changed(); -} - -void PropertyLine::addHelper(PropertyLine* line) -{ - if(line) - helpers_[line->property()->name()]=line; -} - -//A simple dependency on other properties' values - -VProperty* PropertyLine::ruleProperty() -{ - if(!prop_->master()) - { - QStringList disabledFor=prop_->param("disabledRule").split("="); - if(disabledFor.count() == 2) - { - QString key=disabledFor[0].simplified(); - QString val=disabledFor[1].simplified(); - if(key.isEmpty() == false && prop_->parent()) - { - if(VProperty* rp=prop_->parent()->findChild(key)) - { - ruleValue_=val; - return rp; - } - } - } - } - return 0; -} - -void PropertyLine::addRuleLine(PropertyLine *r) -{ - ruleLine_=r; - Q_ASSERT(ruleLine_); - connect(ruleLine_,SIGNAL(changed()), - this,SLOT(slotRule())); - - //init - slotRule(); -} - -void PropertyLine::slotRule() -{ - Q_ASSERT(ruleLine_); - slotEnabled(ruleLine_->currentValue().toString() != ruleValue_); -} - - -//========================================================================= -// -// StringPropertyLine -// -//========================================================================= - -StringPropertyLine::StringPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - le_=new QLineEdit(parent); - le_->setObjectName(prop_->name()); - - connect(le_,SIGNAL(textEdited(QString)), - this,SLOT(slotEdited(QString))); -} - -QWidget* StringPropertyLine::item() -{ - return le_; -} - -QWidget* StringPropertyLine::button() -{ - return NULL; -} - -void StringPropertyLine::slotReset(QVariant v) -{ - le_->setText(v.toString()); - PropertyLine::checkState(); - valueChanged(); -} - -bool StringPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - QString v=oriVal_.toString(); - if(v != le_->text()) - { - prop_->setValue(le_->text()); - oriVal_=prop_->value(); - return true; - } - return false; -} - -QVariant StringPropertyLine::currentValue() -{ - return le_->text(); -} - -void StringPropertyLine::slotEdited(QString) -{ - PropertyLine::checkState(); - valueChanged(); -} - -void StringPropertyLine::setEnabledEditable(bool b) -{ - le_->setEnabled(b); -} - -//========================================================================= -// -// ColourPropertyLine -// -//========================================================================= - -ColourPropertyLine::ColourPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - QFont f; - QFontMetrics fm(f); - int height=fm.height(); - int width=fm.width("AAAAAAA"); - - cb_=new QToolButton(parent); - cb_->setObjectName(prop_->name()); - cb_->setFixedWidth(width); - cb_->setFixedHeight(height+2); - cb_->setToolTip(tr("Click to select a colour")); - - styleSheet_="QToolButton { background: BG; border: 1px solid rgb(120,120,120); border-radius: 2px;}"; - - connect(cb_,SIGNAL(clicked(bool)), - this,SLOT(slotEdit(bool))); -} - -QWidget* ColourPropertyLine::item() -{ - return cb_; -} - -QWidget* ColourPropertyLine::button() -{ - return NULL; -} - -void ColourPropertyLine::slotReset(QVariant v) -{ - QColor c=v.value(); - - QString st=styleSheet_; - st.replace("BG","rgb(" + QString::number(c.red()) + "," + - QString::number(c.green()) + "," + QString::number(c.blue()) + ")"); - - cb_->setStyleSheet(st); - - currentCol_=c; - - PropertyLine::checkState(); - valueChanged(); -} - -void ColourPropertyLine::slotEdit(bool) -{ - QColor currentCol=currentValue().value(); - QColor col=QColorDialog::getColor(currentCol,cb_->parentWidget()); - - if(col.isValid()) - { - slotReset(col); - } -} - -bool ColourPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - QColor v=oriVal_.value(); - QColor c=currentValue().value(); - - if(v != c) - { - prop_->setValue(c); - oriVal_=prop_->value(); - return true; - } - - return false; -} - -QVariant ColourPropertyLine::currentValue() -{ - return currentCol_; -} - -void ColourPropertyLine::setEnabledEditable(bool b) -{ - cb_->setEnabled(b); - - QColor col; - if(b) - { - col=currentCol_; - } - else - { - if(label_) - { - QPalette pal=label_->palette(); - col=pal.color(QPalette::Disabled,QPalette::Window); - } - else - { - col=QColor(200,200,200); - } - } - - QString st=styleSheet_; - st.replace("BG","rgb(" + QString::number(col.red()) + "," + - QString::number(col.green()) + "," + QString::number(col.blue()) + ")"); - - cb_->setStyleSheet(st); -} - -//========================================================================= -// -// FontPropertyLine -// -//========================================================================= - -FontPropertyLine::FontPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - holderW_=new QWidget(parent); - - QHBoxLayout* hb=new QHBoxLayout(holderW_); - hb->setContentsMargins(0,0,0,0); - - QFontDatabase db; - - familyCb_=new QComboBox(parent); - familyCb_->setObjectName(prop_->name()); - - hb->addWidget(familyCb_); - Q_FOREACH(QString s,db.families(QFontDatabase::Latin)) - familyCb_->addItem(s); - - sizeSpin_=new QSpinBox(parent); - sizeSpin_->setRange(1,200); - hb->addWidget(sizeSpin_); - - QLabel *sizeLabel=new QLabel("pt",parent); - hb->addWidget(sizeLabel); - - lName_=new QLabel(parent); - - connect(familyCb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotFamilyChanged(int))); - - connect(sizeSpin_,SIGNAL(valueChanged(int)), - this,SLOT(slotSizeChanged(int))); - - /*tbEdit_=new QToolButton(parent); - tbEdit_->setToolTip(tr("Edit")); - - connect(tbEdit_,SIGNAL(clicked(bool)), - this,SLOT(slotEdit(bool)));*/ -} - -QWidget* FontPropertyLine::item() -{ - return holderW_; -} - -QWidget* FontPropertyLine::button() -{ - return NULL; //tbEdit_; -} - -void FontPropertyLine::slotReset(QVariant v) -{ - font_=v.value(); - - for(int i=0; i < familyCb_->count(); i++) - if(familyCb_->itemText(i) == font_.family()) - familyCb_->setCurrentIndex(i); - - sizeSpin_->setValue(font_.pointSize()); - - PropertyLine::checkState(); - valueChanged(); -} - -void FontPropertyLine::slotEdit(bool) -{ - QFont c; - - bool ok; - QFont f = QFontDialog::getFont(&ok,c,lName_->parentWidget()); - - if(ok) - { - lName_->setText(f.toString()); - font_=f; - } - valueChanged(); -} - -void FontPropertyLine::slotFamilyChanged(int idx) -{ - if(idx != -1) - { - QString family=familyCb_->itemText(idx); - if(font_.family() != family) - { - font_.setFamily(family); - PropertyLine::checkState(); - valueChanged(); - } - } -} - -void FontPropertyLine::slotSizeChanged(int val) -{ - if(val != font_.pointSize()) - { - font_.setPointSize(val); - PropertyLine::checkState(); - valueChanged(); - } -} - - -bool FontPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - if(oriVal_.value() != font_) - { - prop_->setValue(font_); - oriVal_=prop_->value(); - return true; - } - return false; -} - -QVariant FontPropertyLine::currentValue() -{ - return font_; -} - -void FontPropertyLine::setEnabledEditable(bool b) -{ - //tbEdit_->setEnabled(b); -} - -//========================================================================= -// -// IntPropertyLine -// -//========================================================================= - -IntPropertyLine::IntPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - le_=new QLineEdit(parent); - le_->setObjectName(prop_->name()); - - QIntValidator* validator=new QIntValidator(le_); - - QString s=guiProp->param("max"); - if(!s.isEmpty()) - { - validator->setTop(s.toInt()); - } - - s=guiProp->param("min"); - if(!s.isEmpty()) - { - validator->setBottom(s.toInt()); - } - - le_->setValidator(validator); - - connect(le_,SIGNAL(textEdited(QString)), - this,SLOT(slotEdited(QString))); -} - -QWidget* IntPropertyLine::item() -{ - return le_; -} - -QWidget* IntPropertyLine::button() -{ - return NULL; -} - -void IntPropertyLine::slotReset(QVariant v) -{ - le_->setText(QString::number(v.toInt())); - PropertyLine::checkState(); - valueChanged(); -} - -bool IntPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - int cv=le_->text().toInt(); - if(oriVal_.toInt() != cv) - { - prop_->setValue(cv); - oriVal_=prop_->value(); - return true; - } - return false; -} - -QVariant IntPropertyLine::currentValue() -{ - return le_->text().toInt(); -} - -void IntPropertyLine::slotEdited(QString) -{ - PropertyLine::checkState(); - valueChanged(); -} - -void IntPropertyLine::setEnabledEditable(bool b) -{ - le_->setEnabled(b); -} - -//========================================================================= -// -// BoolPropertyLine -// -//========================================================================= - -BoolPropertyLine::BoolPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,false,parent) -{ - cb_=new QCheckBox(prop_->param("label"),parent); - cb_->setObjectName(prop_->name()); - - connect(cb_,SIGNAL(stateChanged(int)), - this,SLOT(slotStateChanged(int))); -} - -QWidget* BoolPropertyLine::item() -{ - return cb_; -} - -QWidget* BoolPropertyLine::button() -{ - return NULL; -} - -void BoolPropertyLine::slotReset(QVariant v) -{ - cb_->setChecked(v.toBool()); - PropertyLine::checkState(); - valueChanged(); - - //BoolLines emit this signal because they might control - //other lines' status - Q_EMIT changed(currentValue()); -} - -bool BoolPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - if(oriVal_.toBool() != cb_->isChecked()) - { - prop_->setValue(cb_->isChecked()); - oriVal_=prop_->value(); - return true; - } - return false; -} - -QVariant BoolPropertyLine::currentValue() -{ - return cb_->isChecked(); -} - -void BoolPropertyLine::slotStateChanged(int) -{ - PropertyLine::checkState(); - valueChanged(); - - //BoolLines emit this signal because they might control - //other lines' status - Q_EMIT changed(currentValue()); -} - -void BoolPropertyLine::setEnabledEditable(bool b) -{ - cb_->setEnabled(b); -} - -//========================================================================= -// -// ComboPropertyLine -// -//========================================================================= - -ComboPropertyLine::ComboPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - cb_=new QComboBox(parent);//(vProp->param("label")); - cb_->setObjectName(prop_->name()); - - connect(cb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotCurrentChanged(int))); - - QStringList lst=prop_->param("values_label").split("/"); - QStringList lstData=prop_->param("values").split("/"); - if(prop_->param("values_label").simplified().isEmpty()) - lst=lstData; - - assert(lst.count() == lstData.count()); - for(int i=0; i < lst.count(); i++) - cb_->addItem(lst[i],lstData[i]); -} - -QWidget* ComboPropertyLine::item() -{ - return cb_; -} - -QWidget* ComboPropertyLine::button() -{ - return NULL; -} - -void ComboPropertyLine::slotReset(QVariant v) -{ - QStringList lst=prop_->param("values").split("/"); - int idx=lst.indexOf(v.toString()); - if(idx != -1) - cb_->setCurrentIndex(idx); - - PropertyLine::checkState(); - valueChanged(); -} - -bool ComboPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - int idx=cb_->currentIndex(); - - if(idx != -1) - { - QString currentDataVal=cb_->itemData(idx).toString(); - if(oriVal_.toString() != currentDataVal) - { - prop_->setValue(currentDataVal); - oriVal_=prop_->value(); - return true; - } - } - - return false; -} - -QVariant ComboPropertyLine::currentValue() -{ - int idx=cb_->currentIndex(); - - if(idx != -1) - { - return cb_->itemData(idx).toString(); - } - - return QString(); -} - -void ComboPropertyLine::slotCurrentChanged(int) -{ - PropertyLine::checkState(); - valueChanged(); -} - -void ComboPropertyLine::setEnabledEditable(bool b) -{ - cb_->setEnabled(b); -} - -//========================================================================= -// -// ComboMultiPropertyLine -// -//========================================================================= - -ComboMultiPropertyLine::ComboMultiPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : PropertyLine(guiProp,addLabel,parent) -{ - if(label_) - label_->setText(label_->text() + ":"); - - cb_=new ComboMulti(parent);//(vProp->param("label")); - cb_->setObjectName(prop_->name()); - - cb_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed); - - connect(cb_,SIGNAL(currentIndexChanged(int)), - this,SLOT(slotCurrentChanged(int))); - - QStringList lst=prop_->param("values_label").split("/"); - QStringList lstData=prop_->param("values").split("/"); - if(prop_->param("values_label").simplified().isEmpty()) - lst=lstData; - - assert(lst.count() == lstData.count()); - for(int i=0; i < lst.count(); i++) - { - cb_->addItem(lst[i],lstData[i]); - } -} - -QWidget* ComboMultiPropertyLine::item() -{ - return cb_; -} - -QWidget* ComboMultiPropertyLine::button() -{ - return NULL; -} - -void ComboMultiPropertyLine::slotReset(QVariant v) -{ - QStringList vals=v.toString().split("/"); - - cb_->setSelectionByData(vals); - - PropertyLine::checkState(); - valueChanged(); -} - -bool ComboMultiPropertyLine::applyChange() -{ - PropertyLine::applyMaster(); - - QString currentVal=cb_->selectionData().join("/"); - - if(oriVal_.toString() != currentVal) - { - prop_->setValue(currentVal); - oriVal_=prop_->value(); - return true; - } - - return false; -} - -QVariant ComboMultiPropertyLine::currentValue() -{ - QStringList lst=cb_->selection(); - - return lst.join("/"); -} - -void ComboMultiPropertyLine::slotCurrentChanged(int) -{ - PropertyLine::checkState(); - valueChanged(); -} - -void ComboMultiPropertyLine::setEnabledEditable(bool b) -{ - cb_->setEnabled(b); -} - - -//========================================================================= -// -// SoundComboPropertyLine -// -//========================================================================= - -SoundComboPropertyLine::SoundComboPropertyLine(VProperty* guiProp,bool addLabel,QWidget * parent) : - ComboPropertyLine(guiProp,addLabel,parent), - playTb_(NULL) -{ - playTb_=new QToolButton(parent); - playTb_->setObjectName(prop_->name()); - - playTb_->setText("play"); - playTb_->setToolTip(tr("Play sound")); - - connect(playTb_,SIGNAL(clicked(bool)), - this,SLOT(slotPlay(bool))); -} - -QWidget* SoundComboPropertyLine::item() -{ - return cb_; -} - -QWidget* SoundComboPropertyLine::button() -{ - return playTb_; -} - -void SoundComboPropertyLine::setEnabledEditable(bool b) -{ - cb_->setEnabled(b); - playTb_->setEnabled(b); -} - -void SoundComboPropertyLine::slotPlay(bool) -{ - int loopCount=1; - if(PropertyLine* line=helpers_.value("sound_loop",NULL)) - loopCount=line->currentValue().toInt(); - - Sound::instance()->playSystem(currentValue().toString().toStdString(),loopCount); -} - - -static PropertyLineMaker makerStr(VProperty::StringGui); -static PropertyLineMaker makerCol(VProperty::ColourGui); -static PropertyLineMaker makerFont(VProperty::FontGui); -static PropertyLineMaker makerInt(VProperty::IntGui); -static PropertyLineMaker makerBool(VProperty::BoolGui); -static PropertyLineMaker makerCombo(VProperty::StringComboGui); -static PropertyLineMaker makerComboMulti(VProperty::MultiStringComboGui); -static PropertyLineMaker makerSoundCombo(VProperty::SoundComboGui); diff -Nru ecflow-4.9.0/Viewer/src/PropertyLine.hpp ecflow-4.11.1/Viewer/src/PropertyLine.hpp --- ecflow-4.9.0/Viewer/src/PropertyLine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyLine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,363 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef PROPERTYLINE_INC_ -#define PROPERTYLINE_INC_ - -#include - -#include -#include -#include -#include - -class QComboBox; -class QCheckBox; -class QLabel; -class QLineEdit; -class QSpinBox; -class QToolButton; -class QWidget; -class ComboMulti; - -#include "VProperty.hpp" - -class PropertyLine; - -class FontSizeSpin : public QSpinBox -{ -public: - FontSizeSpin(QWidget* parent=0); - void setFamily(QString); - -protected: - QString textFromValue(int value) const; - - QList vals_; - -}; - - - -//------------------------------------- -// Factory -//------------------------------------ - -class PropertyLineFactory -{ -public: - explicit PropertyLineFactory(VProperty::GuiType); - virtual ~PropertyLineFactory(); - - virtual PropertyLine* make(VProperty* p,bool,QWidget* w) = 0; - static PropertyLine* create(VProperty* p,bool,QWidget* w); - -private: - explicit PropertyLineFactory(const PropertyLineFactory&); - PropertyLineFactory& operator=(const PropertyLineFactory&); - -}; - -template -class PropertyLineMaker : public PropertyLineFactory -{ - PropertyLine* make(VProperty* p,bool addLabel,QWidget* w) { return new T(p,addLabel,w); } -public: - explicit PropertyLineMaker(VProperty::GuiType t) : PropertyLineFactory(t) {} -}; - - -//------------------------------------- -// Abstract property line editor -//------------------------------------ - -class PropertyLine: public QObject -{ - Q_OBJECT - -public: - PropertyLine(VProperty*,bool addLabel,QWidget* parent=0); - virtual ~PropertyLine(); - - QLabel* label() {return label_;} - QLabel* suffixLabel() {return suffixLabel_;} - virtual QWidget* item()=0; - virtual QWidget* button()=0; - QToolButton* defaultTb() {return defaultTb_;} - QToolButton* masterTb() {return masterTb_;} - VProperty* property() const {return prop_;} - VProperty* guiProperty() const {return guiProp_;} - VProperty* ruleProperty(); - void addRuleLine(PropertyLine*); - virtual bool canExpand() const {return false;} - - void addHelper(PropertyLine*); - - void init(); - virtual bool applyChange()=0; - virtual QVariant currentValue()=0; - -public Q_SLOTS: - virtual void slotReset(QVariant)=0; - virtual void slotReset(VProperty*,QVariant); - virtual void slotEnabled(QVariant); - -protected Q_SLOTS: - void slotResetToDefault(bool); - void slotMaster(bool b); - void checkState(); - void slotRule(); - -Q_SIGNALS: - void changed(QVariant); - void masterChanged(bool); - void changed(); - -protected: - virtual void setEnabledEditable(bool)=0; - bool applyMaster(); - void valueChanged(); - - VProperty* prop_; - VProperty* guiProp_; - QLabel* label_; - QLabel* suffixLabel_; - QToolButton* defaultTb_; - QToolButton* masterTb_; - bool enabled_; - QVariant oriVal_; - bool doNotEmitChange_; - QMap helpers_; - PropertyLine* ruleLine_; - QString ruleValue_; -}; - -//------------------------------------- -// String editor -//------------------------------------ - -class StringPropertyLine : public PropertyLine -{ - Q_OBJECT - -public: - StringPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - bool canExpand() const {return true;} - -public Q_SLOTS: - void slotEdited(QString); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -private: - QLineEdit* le_; -}; - -//------------------------------------- -// Colour editor -//------------------------------------ - -class ColourPropertyLine : public PropertyLine -{ -Q_OBJECT - -public: - ColourPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - -private Q_SLOTS: - void slotEdit(bool); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -private: - QToolButton* cb_; - QColor currentCol_; - QString styleSheet_; -}; - -//------------------------------------- -// Font editor -//------------------------------------ - -class FontPropertyLine : public PropertyLine -{ -Q_OBJECT - -public: - FontPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - -private Q_SLOTS: - void slotEdit(bool); - void slotReset(QVariant); - void slotFamilyChanged(int); - void slotSizeChanged(int); - -protected: - void setEnabledEditable(bool); - -private: - QWidget* holderW_; - QComboBox* familyCb_; - QSpinBox* sizeSpin_; - QLabel* lName_; - QToolButton *tbEdit_; - QFont font_; -}; - -//------------------------------------- -// Int editor -//------------------------------------ - -class IntPropertyLine : public PropertyLine -{ - Q_OBJECT - -public: - IntPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - -public Q_SLOTS: - void slotEdited(QString); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -private: - QLineEdit* le_; -}; - -//------------------------------------- -// Boolean editor -//------------------------------------ - -class BoolPropertyLine : public PropertyLine -{ - Q_OBJECT - -public: - BoolPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - -public Q_SLOTS: - void slotStateChanged(int); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -private: - QCheckBox* cb_; -}; - -//------------------------------------- -// Combo box editor -//------------------------------------ - -class ComboPropertyLine : public PropertyLine -{ - Q_OBJECT - -public: - ComboPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - -public Q_SLOTS: - void slotCurrentChanged(int); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -protected: - QComboBox* cb_; -}; - - -//------------------------------------- -// Combo box editor -//------------------------------------ - -class ComboMultiPropertyLine : public PropertyLine -{ - Q_OBJECT - -public: - ComboMultiPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - bool applyChange(); - QVariant currentValue(); - bool canExpand() const {return true;} - -public Q_SLOTS: - void slotCurrentChanged(int); - void slotReset(QVariant); - -protected: - void setEnabledEditable(bool); - -protected: - ComboMulti* cb_; -}; - -//------------------------------------- -// Combo box editor -//------------------------------------ - -class SoundComboPropertyLine : public ComboPropertyLine -{ - Q_OBJECT - -public: - SoundComboPropertyLine(VProperty* vProp,bool addLabel,QWidget * parent=0); - QWidget* item(); - QWidget* button(); - -public Q_SLOTS: - void slotPlay(bool); - -protected: - void setEnabledEditable(bool); - -private: - QToolButton* playTb_; - -}; - - - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/PropertyMapper.cpp ecflow-4.11.1/Viewer/src/PropertyMapper.cpp --- ecflow-4.9.0/Viewer/src/PropertyMapper.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyMapper.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,57 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "PropertyMapper.hpp" - -#include "UIDebug.hpp" -#include "VConfig.hpp" - -PropertyMapper::PropertyMapper(const std::vector& names,VPropertyObserver* obs) : obs_(obs) -{ - for(std::vector::const_iterator it=names.begin(); it != names.end(); ++it) - { - if(VProperty* p=VConfig::instance()->find(*it)) - { - p->addObserver(obs); - props_.push_back(p); - } - } -} - -PropertyMapper::~PropertyMapper() -{ - for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) - { - (*it)->removeObserver(obs_); - } -} - -VProperty* PropertyMapper::find(const std::string& path,bool failOnError) const -{ - for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) - { - if((*it)->path() == path) - return *it; - } - - if(failOnError) - UI_ASSERT(0,"Could not find property=" + path); - - return 0; -} - -void PropertyMapper::initObserver(VPropertyObserver *obs) const -{ - for(std::vector::const_iterator it=props_.begin(); it != props_.end(); ++it) - { - obs->notifyChange(*it); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/PropertyMapper.hpp ecflow-4.11.1/Viewer/src/PropertyMapper.hpp --- ecflow-4.9.0/Viewer/src/PropertyMapper.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/PropertyMapper.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef PROPERTYMAPPER_INC_ -#define PROPERTYMAPPER_INC_ - -#include "VProperty.hpp" - -class PropertyMapper -{ -public: - PropertyMapper(const std::vector&,VPropertyObserver* obs); - ~PropertyMapper(); - VProperty* find(const std::string& path,bool failOnError=false) const; - void initObserver(VPropertyObserver *obs) const; - -private: - VPropertyObserver* obs_; - std::vector props_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/RectMetrics.cpp ecflow-4.11.1/Viewer/src/RectMetrics.cpp --- ecflow-4.9.0/Viewer/src/RectMetrics.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RectMetrics.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "RectMetrics.hpp" - -#include -#include - -RectMetrics::RectMetrics(int penWidth) : - topOffset_(0), - bottomOffset_(0) -{ - if(penWidth >=0 && penWidth <=4) - compute(penWidth); -} - -void RectMetrics::compute(int penWidth) -{ - QImage img(24,24,QImage::Format_ARGB32_Premultiplied); - img.fill(Qt::white); - QPainter p(&img); - p.setPen(QPen(Qt::black,penWidth)); - - int top=6; - int bottom=img.height()-top; - int left=6; - int right=img.width()-left; - p.drawRect(QRect(left,top,right-left,bottom-top)); - - int j=12; - - //top - for(int i=0; i < img.height(); i++) - { - QRgb c=img.pixel(j,i); - if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) - { - topOffset_=i-top; - break; - } - } - - //bottom - for(int i=img.height()-1; i >=0; i--) - { - QRgb c=img.pixel(j,i); - if(qRed(c) != 255 || qGreen(c) != 255 || qBlue(c) != 255) - { - bottomOffset_=i-bottom; - break; - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/RectMetrics.hpp ecflow-4.11.1/Viewer/src/RectMetrics.hpp --- ecflow-4.9.0/Viewer/src/RectMetrics.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RectMetrics.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef RECTMETRICS_HPP -#define RECTMETRICS_HPP - -class RectMetrics -{ -public: - RectMetrics(int penWidth); - int topOffset() const {return topOffset_;} - int bottomOffset() const {return bottomOffset_;} - -protected: - int topOffset_; - int bottomOffset_; - -private: - void compute(int); -}; - -#endif // RECTMETRICS_HPP diff -Nru ecflow-4.9.0/Viewer/src/RepeatEditor.cpp ecflow-4.11.1/Viewer/src/RepeatEditor.cpp --- ecflow-4.9.0/Viewer/src/RepeatEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RepeatEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,429 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "RepeatEditor.hpp" - -#include -#include -#include -#include -#include - -#include "Node.hpp" -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "SessionHandler.hpp" -#include "UiLog.hpp" -#include "VInfo.hpp" -#include "VNode.hpp" -#include "VRepeatAttr.hpp" - -RepeatEditorWidget::RepeatEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); -} - -void RepeatEditorWidget::hideRow(QWidget* w) -{ - w->hide(); - QWidget* item=formLayout_->labelForField(w); - Q_ASSERT(item); - item->hide(); -} - -//================================================================ -// -// RepeatEditor -// -//================================================================ - -RepeatEditor::RepeatEditor(VInfo_ptr info,QWidget* parent) : - AttributeEditor(info,"repeat",parent), - model_(0) -{ - w_=new RepeatEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "repeat"); - - VRepeatAttr *rep=static_cast(a); - -#if 0 - if(a->data().count() < 7) - return; - - VNode *vnode=info_->node(); - Q_ASSERT(vnode); - node_ptr node=vnode->node(); - Q_ASSERT(node); - const Repeat& r=node->repeat(); - repeat_=VRepeat::make(r); -#endif - - - oriVal_=a->data().at(3); - - w_->nameLabel_->setText(a->data().at(2)); - //w_->valueLe_->setText(a->data().at(3)); - w_->startLabel_->setText(a->data().at(4)); - w_->endLabel_->setText(a->data().at(5)); - w_->stepLabel_->setText(a->data().at(6)); - - //Value will be initailised in the subclasses - oriVal_=a->data().at(3); - - buildList(rep); - -#if 0 - QIntValidator *validator=new QIntValidator(this); - if(!a->data().at(3).isEmpty() && !a->data().at(4).isEmpty()) - { - validator->setRange(a->data().at(3).toInt(), - a->data().at(4).toInt()); - } - valueLe_->setValidator(validator); -#endif - - header_->setInfo(QString::fromStdString(info_->nodePath()),"Repeat " + QString::fromStdString(rep->subType())); - - readSettings(); -} - -RepeatEditor::~RepeatEditor() -{ - writeSettings(); -} - -void RepeatEditor::buildList(VRepeatAttr *rep) -{ - int start=rep->startIndex(); - int end=rep->endIndex(); - int step=rep->step(); - int current=rep->currentIndex(); - - if(step<=0 || end <= start) - { - return; - } - - modelData_.clear(); - int cnt=end-start; - if(cnt >1) - { - for(int i=start; i <= end; i++) - modelData_ << QString::fromStdString(rep->value(i)); - - model_=new QStringListModel(this); - model_->setStringList(modelData_); - w_->valueView_->setModel(model_); - w_->valueView_->setCurrentIndex(model_->index(current,0)); - - connect(w_->valueView_->selectionModel(), - SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this, SLOT(slotSelectedInView(QModelIndex,QModelIndex))); - - w_->valueView_->setFocus(Qt::MouseFocusReason); - } - else - { - w_->valueView_->hide(); - } -} - -void RepeatEditor::slotSelectedInView(const QModelIndex ¤t, const QModelIndex &previous) -{ - setValue(current.data().toString()); - checkButtonStatus(); -} - -bool RepeatEditor::isListMode() const -{ - return (model_)?true:false; -} - -void RepeatEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("RepeatEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void RepeatEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("RepeatEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(310,340)); - } - - settings.endGroup(); -} - -//================================================================ -// -// RepeatIntEditor -// -//================================================================ - -RepeatIntEditor::RepeatIntEditor(VInfo_ptr info,QWidget* parent) : - RepeatEditor(info,parent) -{ - //if(!repeat_) - // return; - - w_->hideRow(w_->valueLe_); - - initSpinner(); - - connect(w_->valueSpin_,SIGNAL(valueChanged(int)), - this,SLOT(slotValueChanged(int))); - - checkButtonStatus(); -} - -void RepeatIntEditor::initSpinner() -{ - w_->valueSpin_->setValue(oriVal_.toInt()); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "repeat"); - - VRepeatAttr *rep=static_cast(a); - - int startIndex=rep->startIndex(); - int endIndex=rep->endIndex(); - int step=rep->step(); - - if(step<=0 || endIndex <= startIndex) - { - return; - } - - int minVal=QString::fromStdString(rep->value(startIndex)).toInt(); - int maxVal=QString::fromStdString(rep->value(endIndex)).toInt(); - -#if 0 - UiLog().dbg() << "min=" << minVal << " max=" << maxVal << " step=" << step; -#endif - w_->valueSpin_->setMinimum(minVal); - w_->valueSpin_->setMaximum(maxVal); - w_->valueSpin_->setSingleStep(step); -} - -void RepeatIntEditor::setValue(QString val) -{ - w_->valueSpin_->setValue(val.toInt()); -} - -void RepeatIntEditor::slotValueChanged(int val) -{ - if(isListMode()) - { - QString txt=QString::number(val); - int row=modelData_.indexOf(txt); - if(row != -1) - { - w_->valueView_->setCurrentIndex(model_->index(row,0)); - } - else - { - w_->valueView_->clearSelection(); - w_->valueView_->setCurrentIndex(QModelIndex()); - } - } - checkButtonStatus(); -} - -void RepeatIntEditor::resetValue() -{ - w_->valueSpin_->setValue(oriVal_.toInt()); - slotValueChanged(oriVal_.toInt()); - checkButtonStatus(); -} - -bool RepeatIntEditor::isValueChanged() -{ - return(oriVal_.toInt() != w_->valueSpin_->value()); -} - -void RepeatIntEditor::apply() -{ - std::string val=QString::number(w_->valueSpin_->value()).toStdString(); - //std::string name=w_->nameLabel_->text().toStdString(); - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","repeat",val); - CommandHandler::run(info_,cmd); -} - -//================================================================ -// -// RepeatStringEditor -// -//================================================================ - -RepeatStringEditor::RepeatStringEditor(VInfo_ptr info,QWidget* parent) : - RepeatEditor(info,parent) -{ - //if(!repeat_) - // return; - - w_->hideRow(w_->valueSpin_); - w_->hideRow(w_->stepLabel_); - w_->valueLe_->setText(oriVal_); - - connect(w_->valueLe_,SIGNAL(textEdited(QString)), - this,SLOT(slotValueEdited(QString))); - - checkButtonStatus(); -} - -void RepeatStringEditor::setValue(QString val) -{ - w_->valueLe_->setText(val); -} - -void RepeatStringEditor::slotValueEdited(QString txt) -{ - if(isListMode()) - { - int row=modelData_.indexOf(txt); - if(row != -1) - { - w_->valueView_->setCurrentIndex(model_->index(row,0)); - } - else - { - w_->valueView_->clearSelection(); - w_->valueView_->setCurrentIndex(QModelIndex()); - } - } - checkButtonStatus(); -} - -void RepeatStringEditor::resetValue() -{ - w_->valueLe_->setText(oriVal_); - slotValueEdited(oriVal_); - checkButtonStatus(); -} - -bool RepeatStringEditor::isValueChanged() -{ - return(oriVal_ != w_->valueLe_->text()); -} - -void RepeatStringEditor::apply() -{ - std::string val=w_->valueLe_->text().toStdString(); - //std::string name=w_->nameLabel_->text().toStdString(); - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","repeat",val); - CommandHandler::run(info_,cmd); -} - -//================================================================ -// -// RepeatDateEditor -// -//================================================================ - -RepeatDateEditor::RepeatDateEditor(VInfo_ptr info,QWidget* parent) : - RepeatEditor(info,parent) -{ - //if(!repeat_) - // return; - - w_->hideRow(w_->valueSpin_); - w_->valueLe_->setText(oriVal_); - - connect(w_->valueLe_,SIGNAL(textEdited(QString)), - this,SLOT(slotValueEdited(QString))); - - checkButtonStatus(); -} - -void RepeatDateEditor::setValue(QString val) -{ - w_->valueLe_->setText(val); -} - -void RepeatDateEditor::slotValueEdited(QString txt) -{ - if(isListMode()) - { - int row=modelData_.indexOf(txt); - if(row != -1) - { - w_->valueView_->setCurrentIndex(model_->index(row,0)); - } - else - { - w_->valueView_->clearSelection(); - w_->valueView_->setCurrentIndex(QModelIndex()); - } - } - checkButtonStatus(); -} - -void RepeatDateEditor::resetValue() -{ - w_->valueLe_->setText(oriVal_); - slotValueEdited(oriVal_); - checkButtonStatus(); -} - -bool RepeatDateEditor::isValueChanged() -{ - return(oriVal_ != w_->valueLe_->text()); -} - -void RepeatDateEditor::apply() -{ - std::string val=w_->valueLe_->text().toStdString(); - //std::string name=w_->nameLabel_->text().toStdString(); - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","repeat",val); - CommandHandler::run(info_,cmd); -} - - -static AttributeEditorMaker makerStr1("repeat_integer"); -static AttributeEditorMaker makerStr2("repeat_string"); -static AttributeEditorMaker makerStr3("repeat_enumerated"); -static AttributeEditorMaker makerStr4("repeat_date"); diff -Nru ecflow-4.9.0/Viewer/src/RepeatEditor.hpp ecflow-4.11.1/Viewer/src/RepeatEditor.hpp --- ecflow-4.9.0/Viewer/src/RepeatEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RepeatEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef REPEATEDITOR_HPP -#define REPEATEDITOR_HPP - -#include "ui_RepeatEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -class QModelIndex; -class QStringList; -class QStringListModel; -class VRepeatAttr; -class RepeatEditor; - -class RepeatEditorWidget : public QWidget, protected Ui::RepeatEditorWidget -{ -friend class RepeatEditor; -friend class RepeatIntEditor; -friend class RepeatStringEditor; -friend class RepeatDateEditor; -public: - RepeatEditorWidget(QWidget *parent=0); -protected: - void hideRow(QWidget* w); -}; - -class RepeatEditor : public AttributeEditor -{ -Q_OBJECT - -public: - RepeatEditor(VInfo_ptr,QWidget* parent=0); - ~RepeatEditor(); - -protected Q_SLOTS: - void slotSelectedInView(const QModelIndex&,const QModelIndex&); - -protected: - void buildList(VRepeatAttr *rep); - bool isListMode() const; - virtual void setValue(QString)=0; - void readSettings(); - void writeSettings(); - - RepeatEditorWidget* w_; - //VRepeat* repeat_; - QStringListModel* model_; - QStringList modelData_; - QString oriVal_; -}; - -class RepeatIntEditor : public RepeatEditor -{ -Q_OBJECT -public: - RepeatIntEditor(VInfo_ptr,QWidget* parent=0); - -protected Q_SLOTS: - void slotValueChanged(int); - -protected: - void initSpinner(); - void apply(); - void setValue(QString val); - void resetValue(); - bool isValueChanged(); -}; - -class RepeatStringEditor : public RepeatEditor -{ -Q_OBJECT -public: - RepeatStringEditor(VInfo_ptr,QWidget* parent=0); - -protected Q_SLOTS: - void slotValueEdited(QString); - -protected: - void apply(); - void setValue(QString val); - void resetValue(); - bool isValueChanged(); -}; - -class RepeatDateEditor : public RepeatEditor -{ -Q_OBJECT -public: - RepeatDateEditor(VInfo_ptr,QWidget* parent=0); - -protected Q_SLOTS: - void slotValueEdited(QString); - -protected: - void apply(); - void setValue(QString val); - void resetValue(); - bool isValueChanged(); -}; - -#endif // REPEATEDITOR_HPP - - diff -Nru ecflow-4.9.0/Viewer/src/RepeatEditorWidget.ui ecflow-4.11.1/Viewer/src/RepeatEditorWidget.ui --- ecflow-4.9.0/Viewer/src/RepeatEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RepeatEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,122 +0,0 @@ - - - RepeatEditorWidget - - - - 0 - 0 - 467 - 390 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Name: - - - - - - - TextLabel - - - - - - - Start: - - - - - - - TextLabel - - - - - - - End: - - - - - - - TextLabel - - - - - - - Step: - - - - - - - TextLabel - - - - - - - Value: - - - - - - - - - - - 0 - 1 - - - - QListView::Fixed - - - - - - - Value: - - - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/RichTextEdit.cpp ecflow-4.11.1/Viewer/src/RichTextEdit.cpp --- ecflow-4.9.0/Viewer/src/RichTextEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "RichTextEdit.hpp" - -#include -#include -#include -#include -#include -#include - -#include "VConfig.hpp" -#include "UiLog.hpp" - -RichTextEdit::RichTextEdit(QWidget * parent) : - QTextBrowser(parent), - fontProp_(0) -{ - QFont f("Courier"); - //QFont f("Monospace"); - //f.setStyleHint(QFont::TypeWriter); - f.setFixedPitch(true); - f.setPointSize(10); - //f.setStyleStrategy(QFont::PreferAntialias); - setFont(f); -} - -RichTextEdit::~RichTextEdit() -{ - if(fontProp_) - fontProp_->removeObserver(this); -} - -//--------------------------------------------- -// Fontsize management -//--------------------------------------------- - -void RichTextEdit::setFontProperty(VProperty* p) -{ - fontProp_=p; - fontProp_->addObserver(this); - updateFont(); -} - -void RichTextEdit::slotZoomIn() -{ - zoomIn(); - fontSizeChangedByZoom(); -} - -void RichTextEdit::slotZoomOut() -{ - int oriSize=font().pointSize(); - zoomOut(); - - if(font().pointSize() != oriSize) - fontSizeChangedByZoom(); -} - -void RichTextEdit::fontSizeChangedByZoom() -{ - if(fontProp_) - fontProp_->setValue(font()); -} - -void RichTextEdit::updateFont() -{ - if(fontProp_) - { - QFont f=fontProp_->value().value(); - if(font() != f) - setFont(f); - } -} - -void RichTextEdit::notifyChange(VProperty* p) -{ - if(fontProp_ ==p) - { - setFont(p->value().value()); - } -} diff -Nru ecflow-4.9.0/Viewer/src/RichTextEdit.hpp ecflow-4.11.1/Viewer/src/RichTextEdit.hpp --- ecflow-4.9.0/Viewer/src/RichTextEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef RICHTEXTEDIT_HPP -#define RICHTEXTEDIT_HPP - -#include - -#include "VProperty.hpp" - -class RichTextEdit : public QTextBrowser, public VPropertyObserver -{ -Q_OBJECT - -public: - explicit RichTextEdit(QWidget* parent = 0); - ~RichTextEdit(); - - void setFontProperty(VProperty* p); - void updateFont(); - void notifyChange(VProperty* p); - -public Q_SLOTS: - void slotZoomIn(); - void slotZoomOut(); - -Q_SIGNALS: - void fontSizeChangedByWheel(); - -private: - void fontSizeChangedByZoom(); - - VProperty *fontProp_; -}; - - -#endif // RICHTEXTEDIT_HPP diff -Nru ecflow-4.9.0/Viewer/src/RichTextSearchInterface.cpp ecflow-4.11.1/Viewer/src/RichTextSearchInterface.cpp --- ecflow-4.9.0/Viewer/src/RichTextSearchInterface.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,231 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "RichTextSearchInterface.hpp" - -#include - -RichTextSearchInterface::RichTextSearchInterface() : editor_(NULL) -{ -} - - -bool RichTextSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, - QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode) -{ - if(!editor_) - return false; - - if(editor_->document()->isEmpty()) - return false; - - QTextCursor cursor(editor_->textCursor()); - - if (highlightAll) // if highlighting all matches, start from the start of the document - cursor.movePosition(QTextCursor::Start); - - else // move the cursor? - cursor.movePosition(move); - - - QList extraSelections; - bool found = false; - bool keepGoing = true; - int numMatches = 0; - - Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; - - while (keepGoing) - { - switch (matchMode) - { - case StringMatchMode::ContainsMatch: - { - cursor = editor_->document()->find(str, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - case StringMatchMode::WildcardMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - regexp.setPatternSyntax(QRegExp::Wildcard); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - case StringMatchMode::RegexpMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - - cursor = editor_->document()->find(regexp, cursor, flags); // perform the search - found = (!cursor.isNull()); - break; - } - - default: - { - break; - } - } - - - if (found) - { - if (highlightAll) - { - QTextEdit::ExtraSelection highlight; - highlight.cursor = cursor; - highlight.format.setBackground(highlightColour_); - extraSelections << highlight; - numMatches++; - } - else - { - editor_->setTextCursor(cursor); // mark the selection of the match - } - } - - - if (found && !highlightAll) // found a match and we only want one - stop here and select it - keepGoing = false; - - else if (!found && !highlightAll && (iteration != 0)) // didn't find a match, only want one, we HAVE wrapped around - keepGoing = false; - - if (!found && highlightAll) // want to highlight all, but no more matches found - keepGoing = false; - - - - // not found, and we only want one match, then we need to wrap around and keep going - if (keepGoing) - { - if (!highlightAll) - { - cursor=editor_->textCursor(); - if (flags & QTextDocument::FindBackward) - cursor.movePosition(QTextCursor::End); - else - cursor.movePosition(QTextCursor::Start); - iteration = 1; // iteration=1 to avoid infinite wraparound! - } - } - } - - - if (highlightAll) - { - //char num[64]; - //sprintf(num, "%d", numMatches); - //UserMessage::message(UserMessage::DBG, false," highlighting : " + std::string(num)); - - editor_->setExtraSelections( extraSelections ); - } - - return (found); -} - -void RichTextSearchInterface::automaticSearchForKeywords(bool userClickedReload) -{ - if(editor_->document()->isEmpty()) - return; - - bool performSearch = vpPerformAutomaticSearch_->value().toBool(); - - if (performSearch) - { - // search direction - QTextDocument::FindFlags findFlags; - QTextCursor cursor(editor_->textCursor()); - std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); - QTextCursor::MoveOperation move; - if (searchFrom == "bottom") - { - findFlags = QTextDocument::FindBackward; - move = QTextCursor::End; - } - else - { - move = QTextCursor::Start; - } - - // case sensitivity - bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); - if (caseSensitive) - findFlags = findFlags | QTextDocument::FindCaseSensitively; - - // string match mode - std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); - StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); - - // the term to be searched for - std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); - QString searchTerm = QString::fromStdString(searchTerm_s); - - // perform the search - bool found = findString (searchTerm, false, findFlags, move, 1, mode); - - if(!found) - { - if(userClickedReload) - { - // move the cursor to the start of the last line - gotoLastLine(); - } - } - } - else - { - // move the cursor to the start of the last line - gotoLastLine(); - } -} - -void RichTextSearchInterface::refreshSearch() -{ - if(!editor_) - return; - - QTextCursor cursor(editor_->textCursor()); - if (cursor.hasSelection()) - { - cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::MoveAnchor); - editor_->setTextCursor(cursor); - } -} - - -void RichTextSearchInterface::clearHighlights() -{ - if(!editor_) - return; - - QList empty; - editor_->setExtraSelections(empty); -} - -void RichTextSearchInterface::disableHighlights() -{ - clearHighlights(); -} - - -void RichTextSearchInterface::gotoLastLine() -{ - // move the cursor to the start of the last line - QTextCursor cursor = editor_->textCursor(); - cursor.movePosition(QTextCursor::End); - cursor.movePosition(QTextCursor::StartOfLine); - editor_->setTextCursor(cursor); - editor_->ensureCursorVisible(); -} diff -Nru ecflow-4.9.0/Viewer/src/RichTextSearchInterface.hpp ecflow-4.11.1/Viewer/src/RichTextSearchInterface.hpp --- ecflow-4.9.0/Viewer/src/RichTextSearchInterface.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef RichTextSearchInterface_HPP -#define RichTextSearchInterface_HPP - -#include "AbstractTextEditSearchInterface.hpp" - -class QTextBrowser; - -class RichTextSearchInterface : public AbstractTextEditSearchInterface -{ -public: - RichTextSearchInterface(); - void setEditor(QTextBrowser* e) {editor_=e;} - - bool findString(QString str, bool highlightAll, QTextDocument::FindFlags findFlags, - QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); - - void automaticSearchForKeywords(bool); - void refreshSearch(); - void clearHighlights(); - void disableHighlights(); - void enableHighlights() {} - bool highlightsNeedSearch() {return true;} - void gotoLastLine(); - -protected: - - QTextBrowser *editor_; -}; - -#endif // RichTextSearchInterface_HPP diff -Nru ecflow-4.9.0/Viewer/src/RichTextSearchLine.cpp ecflow-4.11.1/Viewer/src/RichTextSearchLine.cpp --- ecflow-4.9.0/Viewer/src/RichTextSearchLine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "RichTextSearchLine.hpp" -#include "RichTextSearchInterface.hpp" - -#include - -RichTextSearchLine::RichTextSearchLine(QWidget *parent) : - TextEditSearchLine(parent) -{ - interface_=new RichTextSearchInterface; - TextEditSearchLine::setSearchInterface(interface_); -} - -RichTextSearchLine::~RichTextSearchLine() -{ - delete interface_; -} - -void RichTextSearchLine::setEditor(QTextBrowser *e) -{ - RichTextSearchInterface *pti=static_cast(interface_); - assert(pti); - pti->setEditor(e); -} diff -Nru ecflow-4.9.0/Viewer/src/RichTextSearchLine.hpp ecflow-4.11.1/Viewer/src/RichTextSearchLine.hpp --- ecflow-4.9.0/Viewer/src/RichTextSearchLine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/RichTextSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef RICHTEXTSEARCHLINE_HPP -#define RICHTEXTSEARCHLINE_HPP - -#include - -#include "TextEditSearchLine.hpp" - -class AbstractTextSearchInterface; - -class RichTextSearchLine : public TextEditSearchLine -{ -public: - explicit RichTextSearchLine(QWidget *parent=0); - ~RichTextSearchLine(); - void setEditor(QTextBrowser*); - -private: - //The interface is set internally - void setSearchInterface(AbstractTextSearchInterface*) {} - -}; - -#endif // RICHTEXTSEARCHLINE_HPP diff -Nru ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.cpp ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.cpp --- ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - - -#include "SaveSessionAsDialog.hpp" -#include "ui_SaveSessionAsDialog.h" - -#include "DirectoryHandler.hpp" - - -SaveSessionAsDialog::SaveSessionAsDialog(QWidget *parent) : QDialog(parent) -{ - //ui->setupUi(this); - setupUi(this); - - refreshListOfSavedSessions(); - - // ensure the correct state of the Save button - on_sessionNameEdit__textChanged(); -} - -SaveSessionAsDialog::~SaveSessionAsDialog() -{ - //delete ui; -} - -void SaveSessionAsDialog::refreshListOfSavedSessions() -{ - //sessionsTable_->clearContents(); - savedSessionsList_->clear(); - - // get the list of existing sessions - int numSessions = SessionHandler::instance()->numSessions(); - for (int i = 0; i < numSessions; i++) - { - SessionItem *s = SessionHandler::instance()->sessionFromIndex(i); - addSessionToTable(s); - } -} - -void SaveSessionAsDialog::addSessionToTable(SessionItem *s) -{ - - savedSessionsList_->addItem(QString::fromStdString(s->name())); -/* - int lastRow = sessionsTable_->rowCount()-1; - sessionsTable_->insertRow(lastRow+1); - - QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(s->name())); - sessionsTable_->setItem(lastRow+1, 0, nameItem); -*/ -} - -bool SaveSessionAsDialog::validSaveName(const std::string &name) -{ - QString boxName(QObject::tr("Save session")); - // name empty? - if (name.empty()) - { - QMessageBox::critical(0,boxName, tr("Please enter a name for the session")); - return false; - } - - - // is there already a session with this name? - bool sessionWithThisName = (SessionHandler::instance()->find(name) != NULL); - - if (sessionWithThisName) - { - QMessageBox::critical(0,boxName, tr("A session with that name already exists - please choose another name")); - return false; - } - else - { - return true; - } -} - -void SaveSessionAsDialog::on_sessionNameEdit__textChanged() -{ - // only allow to save a non-empty session name - saveButton_->setEnabled(!sessionNameEdit_->text().isEmpty()); -} - - -void SaveSessionAsDialog::on_saveButton__clicked() -{ - std::string name = sessionNameEdit_->text().toStdString(); - - if (validSaveName(name)) - { - SessionItem* newSession = SessionHandler::instance()->copySession(SessionHandler::instance()->current(), name); - if (newSession) - { - //SessionHandler::instance()->add(name); - refreshListOfSavedSessions(); - SessionHandler::instance()->current(newSession); - QMessageBox::information(0,tr("Session"), tr("Session saved")); - } - else - { - QMessageBox::critical(0,tr("Session"), tr("Failed to save session")); - } - close(); - } -} - -// called when the user clicks the Save button -//void SaveSessionAsDialog::accept() -//{ -// -//} diff -Nru ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.hpp ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.hpp --- ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SAVESESSIONASDIALOG_HPP -#define SAVESESSIONASDIALOG_HPP - -#include -#include "ui_SaveSessionAsDialog.h" - -#include "SessionHandler.hpp" - -namespace Ui { -class SaveSessionAsDialog; -} - -class SaveSessionAsDialog : public QDialog, protected Ui::SaveSessionAsDialog -{ - Q_OBJECT - -public: - explicit SaveSessionAsDialog(QWidget *parent = 0); - ~SaveSessionAsDialog(); - -public Q_SLOTS: - void on_saveButton__clicked(); - void on_sessionNameEdit__textChanged(); - -private: - //Ui::SaveSessionAsDialog *ui; - void addSessionToTable(SessionItem *s); - bool validSaveName(const std::string &name); - void refreshListOfSavedSessions(); -}; - -#endif // SAVESESSIONASDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.ui ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.ui --- ecflow-4.9.0/Viewer/src/SaveSessionAsDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SaveSessionAsDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ - - - SaveSessionAsDialog - - - - 0 - 0 - 411 - 292 - - - - Save session - - - - - - Existing sessions - - - - - - false - - - QAbstractItemView::NoSelection - - - - - - - New session: - - - - - - - - - Name: - - - - - - - - - - &Save - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - - buttonBox_ - accepted() - SaveSessionAsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox_ - rejected() - SaveSessionAsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ScriptItemWidget.cpp ecflow-4.11.1/Viewer/src/ScriptItemWidget.cpp --- ecflow-4.9.0/Viewer/src/ScriptItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ScriptItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ScriptItemWidget.hpp" - -#include "Highlighter.hpp" -#include "InfoProvider.hpp" -#include "MessageLabel.hpp" -#include "VConfig.hpp" -#include "VNode.hpp" -#include "VReply.hpp" - -//======================================================== -// -// ScriptItemWidget -// -//======================================================== - -ScriptItemWidget::ScriptItemWidget(QWidget *parent) : CodeItemWidget(parent) -{ - messageLabel_->setShowTypeTitle(false); - messageLabel_->hide(); - - //Remove the first spacer item!! - removeSpacer(); - - //The document becomes the owner of the highlighter - new Highlighter(textEdit_->document(),"script"); - - infoProvider_=new ScriptProvider(this); - - //Editor font - textEdit_->setFontProperty(VConfig::instance()->find("panel.script.font")); -} - -ScriptItemWidget::~ScriptItemWidget() -{ -} - -QWidget* ScriptItemWidget::realWidget() -{ - return this; -} - -void ScriptItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - info_=info; - messageLabel_->hide(); - - //Info must be a node - if(info_ && info_->isNode() && info_->node()) - { - reloadTb_->setEnabled(false); - infoProvider_->info(info_); - } -} - -void ScriptItemWidget::clearContents() -{ - InfoPanelItem::clear(); - fileLabel_->clear(); - textEdit_->clear(); - messageLabel_->hide(); - reloadTb_->setEnabled(true); - clearCurrentFileName(); -} - -void ScriptItemWidget::infoReady(VReply* reply) -{ - Q_ASSERT(reply); - - messageLabel_->hide(); - - QString s=QString::fromStdString(reply->text()); - textEdit_->setPlainText(s); - - if(reply->hasWarning()) - { - messageLabel_->showWarning(QString::fromStdString(reply->warningText())); - } - else if(reply->hasInfo()) - { - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); - } - - fileLabel_->update(reply); - reloadTb_->setEnabled(true); - setCurrentFileName(reply->fileName()); -} - -void ScriptItemWidget::infoProgress(VReply* reply) -{ - QString s=QString::fromStdString(reply->text()); - messageLabel_->showInfo(QString::fromStdString(reply->infoText())); -} - -void ScriptItemWidget::infoFailed(VReply* reply) -{ - QString s=QString::fromStdString(reply->errorText()); - //textEdit_->setPlainText(s); - messageLabel_->showError(s); - reloadTb_->setEnabled(true); -} - -void ScriptItemWidget::reloadRequested() -{ - reload(info_); -} - -void ScriptItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - //Suspend - if(suspended_) - { - reloadTb_->setEnabled(false); - } - //Resume - else - { - if(info_ && info_->node()) - { - reloadTb_->setEnabled(true); - } - else - { - clearContents(); - } - } - } -} - -static InfoPanelItemMaker maker1("script"); diff -Nru ecflow-4.9.0/Viewer/src/ScriptItemWidget.hpp ecflow-4.11.1/Viewer/src/ScriptItemWidget.hpp --- ecflow-4.9.0/Viewer/src/ScriptItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ScriptItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SCRIPTITEMWIDGET_HPP_ -#define SCRIPTITEMWIDGET_HPP_ - -#include "InfoPanelItem.hpp" -#include "CodeItemWidget.hpp" - -class ScriptItemWidget : public CodeItemWidget, public InfoPanelItem -{ -public: - explicit ScriptItemWidget(QWidget *parent=0); - ~ScriptItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - //From VInfoPresenter - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected: - void updateState(const ChangeFlags&); - void reloadRequested(); -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/SearchLineWidget.ui ecflow-4.11.1/Viewer/src/SearchLineWidget.ui --- ecflow-4.9.0/Viewer/src/SearchLineWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SearchLineWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ - - - SearchLineWidget - - - - 0 - 0 - 744 - 266 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - &Find: - - - searchLine_ - - - - - - - - - - - - - - - - - 0 - 0 - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - - - Qt::ToolButtonIconOnly - - - true - - - - - - - Search options - - - Options - - - - :/viewer/images/configure.svg:/viewer/images/configure.svg - - - QToolButton::InstantPopup - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Close search bar - - - ... - - - true - - - - - - - true - - - Case sensitive - - - Case sensitive search - - - Alt+C - - - - - true - - - Whole words - - - Search for whole words only - - - Alt+O - - - - - - :/viewer/images/arrow_down.svg:/viewer/images/arrow_down.svg - - - Find next - - - Jump to next match <code>F3</code> - - - F3 - - - - - - :/viewer/images/arrow_up.svg:/viewer/images/arrow_up.svg - - - Find prev - - - Jump to previous match <code>Shift+F3</code> - - - Shift+F3 - - - - - true - - - Highlight All - - - Highlight all occurrences of the text - - - Alt+A - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      - - StringMatchCombo - QComboBox -
      StringMatchCombo.hpp
      -
      -
      - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/ServerAddDialog.ui ecflow-4.11.1/Viewer/src/ServerAddDialog.ui --- ecflow-4.9.0/Viewer/src/ServerAddDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,120 +0,0 @@ - - - ServerAddDialog - - - - 0 - 0 - 294 - 172 - - - - Add new server - - - true - - - - - - - - &Name: - - - label - - - - - - - - - - - - - &Host: - - - hostEdit - - - - - - - &Port: - - - portEdit - - - - - - - - - - Add server to current &view - - - true - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - ServerAddDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox - rejected() - ServerAddDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ServerComInfoWidget.cpp ecflow-4.11.1/Viewer/src/ServerComInfoWidget.cpp --- ecflow-4.9.0/Viewer/src/ServerComInfoWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComInfoWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1232 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ServerComInfoWidget.hpp" - -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include "FontMetrics.hpp" -#include "PropertyMapper.hpp" -#include "ServerHandler.hpp" -#include "TextFormat.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VNode.hpp" - -QIcon* ServerRefreshInfoWidget::icon_=0; -QBrush ServerRefreshInfoWidget::buttonBgBrush_(QColor(229,228,227)); -QBrush ServerRefreshInfoWidget::serverBgBrush_(QColor(241,242,243)); -QPen ServerRefreshInfoWidget::borderPen_(QColor(197,197,197)); -QPen ServerRefreshInfoWidget::buttonBorderPen_(QColor(167,167,167)); -QPen ServerRefreshInfoWidget::disabledBorderPen_(QColor(182,182,182)); -QBrush ServerRefreshInfoWidget::buttonBgHoverBrush_(QColor(249,248,248)); -QPen ServerRefreshInfoWidget::buttonHoverPen_(QColor(160,160,160)); -QBrush ServerRefreshInfoWidget::buttonBgRefreshBrush_(QColor(214,227,213)); -QBrush ServerRefreshInfoWidget::periodBgBrush_(QColor(241,242,243)); -QBrush ServerRefreshInfoWidget::progBrush_(QColor(117,165,230)); -QBrush ServerRefreshInfoWidget::progBgBrush_(QColor(255,255,255)); -QBrush ServerRefreshInfoWidget::lastBgBrush_(QColor(238,238,238)); -QPen ServerRefreshInfoWidget::buttonRefreshPen_(QColor(79,179,100),2); -QPen ServerRefreshInfoWidget::serverTextPen_(QColor(80,80,80)); -QPen ServerRefreshInfoWidget::periodTextPen_(QColor(45,45,45)); -QPen ServerRefreshInfoWidget::driftTextPen_(QColor(120,120,120)); -QPen ServerRefreshInfoWidget::lastTextPen_(QColor(45,45,45)); -QPen ServerRefreshInfoWidget::disabledTextPen_(QColor(180,180,180)); - -//#define _UI_SERVERCOMINFOWIDGET_DEBUG - -#if 0 -ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent) : - QWidget(parent), - refreshAction_(refreshAction) -{ - Q_ASSERT(refreshAction_); - - QHBoxLayout *hb=new QHBoxLayout(this); - hb->setContentsMargins(0,0,0,0); - hb->setSpacing(0); - - //QToolButton* refreshTb=new QToolButton(this); - //refreshTb->setAutoRaise(true); - //refreshTb->setDefaultAction(refreshAction_); - //hb->addWidget(refreshTb); - - infoW_=new ServerComLineDisplay(this); - hb->addWidget(infoW_); - - IconProvider::add(":/viewer/reload_one.svg","reload_one"); - - -} -#endif - -ServerRefreshInfoWidget::ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent) : - QWidget(parent), - refreshAction_(refreshAction), - server_(0), - fontServer_(QFont()), - fontPeriod_(QFont()), - fontLast_(QFont()), - fmServer_(QFont()), - fmServerReal_(QFont()), - fmPeriod_(QFont()), - fmLast_(QFont()), - periodTextWidth_(0), - periodTextWidthMin_(0), - periodDummyText_(" D=300s "), - periodDummyFullText_(" D=300s d=99s"), - currentComponent_(NoComponent), - progRectHeight_(2), - serverRectHeight_(10), - serverYPadding_(2), - prop_(0), - mode_(NoMode), - noBlinkLimit_(15), - hasInfo_(false), - inRefresh_(true), - userInitiatedRefresh_(false), - showLastAutoRefresh_(true), - total_(-1), - period_(-1), - toNext_(-1), - drift_(-1), - needBorder_(true) -{ - Q_ASSERT(refreshAction_); - - //The icon for the round refresh button - if(!icon_) - icon_=new QIcon(QPixmap(":/viewer/reload_green.svg")); - - //Init fonts - fontServer_=QFont(); - fontServer_.setPointSize(fontServer_.pointSize()-1); - fontServer_.setBold(true); - fmServer_=QFontMetrics(fontServer_); - fmServerReal_=FontMetrics(fontServer_); - - fontPeriod_=QFont(); - fontPeriod_.setPointSize(fontPeriod_.pointSize()-2); - fmPeriod_=QFontMetrics(fontPeriod_); - - fontLast_=QFont(); - fontLast_.setPointSize(fontLast_.pointSize()-2); - fmLast_=QFontMetrics(fontLast_); - - int w=200; - serverRectHeight_=fmServerReal_.realHeight()+2*serverYPadding_; - int h=serverRectHeight_+2*4; - - //timer - timer_=new QTimer(this); - - connect(timer_,SIGNAL(timeout()), - this,SLOT(slotTimeOut())); - - //set size - setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); - setMinimumSize(w,h); - - buttonRect_=QRect(1,1,h-2,h-2); - buttonRadius2_=pow(buttonRect_.width()/2,2); - - adjustGeometry(false); - - //we need this for the mousemove event - setMouseTracking(true); - - //properties to use - std::vector propVec; - propVec.push_back("server.update.blinkUpdateButtonLimit"); - propVec.push_back("server.update.showLastRefreshTimeInAutoMode"); - prop_=new PropertyMapper(propVec,this); - updateSettings(); -} - -ServerRefreshInfoWidget::~ServerRefreshInfoWidget() -{ - //Detach from the server - if(server_) - { - server_->removeServerObserver(this); - server_->removeServerComObserver(this); - } - delete prop_; -} - -bool ServerRefreshInfoWidget::isActionEnabled() const -{ - return refreshAction_->isEnabled(); -} - -void ServerRefreshInfoWidget::notifyChange(VProperty* p) -{ - updateSettings(); -} - -void ServerRefreshInfoWidget::updateSettings() -{ - bool fetched=false; - bool changed=false; - if(VProperty* p=prop_->find("server.update.showLastRefreshTimeInAutoMode")) - { - bool v=p->value().toBool(); - if(showLastAutoRefresh_!= v) - { - showLastAutoRefresh_=v; - adjustGeometry(true); - fetched=true; - changed=true; - } - } - - if(VProperty* p=prop_->find("server.update.blinkUpdateButtonLimit")) - { - int v=p->value().toInt(); - if(noBlinkLimit_ != v) - { - noBlinkLimit_=v; - changed=true; - } - } - - if(changed) - { - if(!fetched) - { - fetchInfo(); - } - update(); - } -} - -void ServerRefreshInfoWidget::setServer(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - - if(server_ != server && server_) - { - server_->removeServerObserver(this); - server_->removeServerComObserver(this); - } - - server_=server; - - if(server_) - { - server_->addServerObserver(this); - server_->addServerComObserver(this); - } - else - { - hasInfo_=0; - } - - refreshAction_->setEnabled(true); - inRefresh_=false; - userInitiatedRefresh_=false; - mode_=NoMode; - - //Cache some data - serverName_.clear(); - serverText_.clear(); - if(server_) - { - serverName_=QString::fromStdString(server_->name()); - serverText_=" " + serverName_ + " "; - } - - periodText_.clear(); - driftText_.clear(); - periodTextWidthMin_=0; - periodTextWidth_=0; - lastTextWidth_=0; - - //Adjust width + get info - adjustGeometry(true); - - //rerender - update(); -} - -//------------------------------- -// Server observer notifications -//------------------------------- - -void ServerRefreshInfoWidget::notifyServerDelete(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - - Q_ASSERT(server_ == server); - if(server_ == server) - { - server_->removeServerObserver(this); - server_->removeServerComObserver(this); - server_=0; - serverName_.clear(); - serverText_.clear(); - hasInfo_=false; - mode_=NoMode; - inRefresh_=false; - userInitiatedRefresh_=false; - periodText_.clear(); - driftText_.clear(); - periodTextWidthMin_=0; - periodTextWidth_=0; - lastTextWidth_=0; - - refreshAction_->setEnabled(false); - - //get info and rerender - reloadAll(); - - adjustGeometry(false); - } -} - -//While the server is being reloaded the refresh button is disabled. It must be followed by a -//notifyEndServerScan() call! -void ServerRefreshInfoWidget::notifyBeginServerClear(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - timer_->stop(); - refreshAction_->setEnabled(false); - hasInfo_=false; - inRefresh_=false; - userInitiatedRefresh_=false; - mode_=NoMode; - periodText_.clear(); - driftText_.clear(); - periodTextWidthMin_=0; - periodTextWidth_=0; - lastTextWidth_=0; - - update(); -} - -//The server has been reloaded. We must get the current state. -void ServerRefreshInfoWidget::notifyEndServerScan(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - refreshAction_->setEnabled(true); - reloadAll(); -} - -//virtual void notifyServerConnectState(ServerHandler* server) {} -void ServerRefreshInfoWidget::notifyServerActivityChanged(ServerHandler* /*server*/) -{ - -} - -//----------------------------------- -// Server com observer notifications -//----------------------------------- - -void ServerRefreshInfoWidget::notifyRefreshTimerStarted(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - reloadAll(); //get info and rerender -} - -void ServerRefreshInfoWidget::notifyRefreshTimerStopped(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - reloadAll(); //get info and rerender -} - -void ServerRefreshInfoWidget::notifyRefreshTimerChanged(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG - printStatus(); -#endif - Q_ASSERT(server_ == server); - - //if we are in refresh we do not want to fetch any new information unless we are in - //NoMode - if(!inRefresh_ || mode_==NoMode) - { - reloadAll(); //get info and rerender - } -} - -//While the refresh is being executed the the refresh button -void ServerRefreshInfoWidget::notifyRefreshScheduled(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - inRefresh_=true; - inRefreshElapsed_.restart(); - if(mode_ == NormalMode) - { - timer_->stop(); - -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - printStatus(); -#endif - //redraw - update(); - } -} - -void ServerRefreshInfoWidget::notifyRefreshFinished(ServerHandler* server) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(server_ == server); - int elapsed=inRefreshElapsed_.elapsed(); - - if((mode_ == NormalMode) && elapsed < 450) - { - Q_ASSERT(500-elapsed > 0); - //We keep the button in inRefresh state for 0.5 sec (the timer is stopped now!!) - QTimer::singleShot(500-elapsed,this,SLOT(slotTimeOutRefreshFinished())); -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - printStatus(); -#endif - } - else - { - inRefresh_=false; - userInitiatedRefresh_=false; - } -} - -void ServerRefreshInfoWidget::reloadAll() -{ - fetchInfo(); //get info - update(); //renrender -} - -void ServerRefreshInfoWidget::slotTimeOut() -{ - reloadAll(); -} - -void ServerRefreshInfoWidget::slotTimeOutRefreshFinished() -{ - inRefresh_=false; - userInitiatedRefresh_=false; - reloadAll(); -} - -void ServerRefreshInfoWidget::fetchInfo() -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG - printStatus(); -#endif - - if(!server_) - { - mode_=NoMode; - hasInfo_=false; - timer_->stop(); - } - else - { - QDateTime currentTime=QDateTime::currentDateTime(); - bool v=server_->updateInfo(period_,total_,drift_,toNext_); - - bool geoUpdateNeeded=(v != hasInfo_); - hasInfo_=v; - -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UiLog().dbg() << " period=" << period_ << " total=" << total_ << - " toNext=" << toNext_; -#endif - - //the server has an automatic update - if(hasInfo_) - { - lastRefresh_=server_->lastRefresh().time().toString(); //currentTime.addSecs(-total_+toNext_).time().toString(); - nextRefresh_=currentTime.addSecs(toNext_).time().toString(); - - mode_=NormalMode; - if(!inRefresh_) - refreshAction_->setEnabled(true); - } - //the server's automatic refresh is switched off - else - { - lastRefresh_=server_->lastRefresh().time().toString(); - mode_=ManualMode; - if(!inRefresh_) - refreshAction_->setEnabled(true); - } - - //Determine period text - determinePeriodText(); - - if(geoUpdateNeeded || periodTextWidthAboutToChange()) - adjustGeometry(false); - - //if(currentComponent_ == TextComponent) - adjustToolTip(); - - adjustTimer(toNext_); - } - -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - printStatus(); -#endif -} - -//Adjust timer interval -void ServerRefreshInfoWidget::adjustTimer(int toNext) -{ - //we stop the timer when: - // -the action is disabled - // -we are in fast mode - // -the countdown is not shown - if(!refreshAction_->isEnabled() || mode_ == NoMode || mode_ == ManualMode) - { - timer_->stop(); - - } - else if(hasInfo_) - { - if(total_ <= 1) - { - timer_->stop(); - return; - } - else if(total_==2) - timer_->setInterval(750); - else - { - Q_ASSERT(total_ > 0); - int progWidth=periodTextWidth_; - Q_ASSERT(progWidth > 0); - float secPerPix=static_cast(total_)/static_cast(progWidth); - float r=ceil(secPerPix); - Q_ASSERT(r >= 1.); - - if(toNext > 135) - { - if(r > 30) - r=60; - else if(r > 15) - r=30; - else - r=15; - } - else if(toNext > 60) - { - if(r < 10) - r=10; - } - else if(toNext > 30) - { - if(r < 5) - r=5; - } - else if(toNext > 5) - { - if(r < 2.5) - r=2.5; - } - else - { - r=1; - } - - timer_->setInterval(static_cast(r*1000.)); - } - - if(!timer_->isActive()) - timer_->start(); - } - else - { - timer_->stop(); - } -} - -//Check if a point is inside the (round) button -bool ServerRefreshInfoWidget::isInButton(const QPoint& pos) const -{ - QPoint d=pos-buttonRect_.center(); - return d.x()*d.x()+d.y()*d.y() <= buttonRadius2_; -} - -bool ServerRefreshInfoWidget::isInText(const QPoint& pos) const -{ - return (pos.x() > buttonRect_.right()); -} - -void ServerRefreshInfoWidget::resizeEvent(QResizeEvent* event) -{ - buttonRect_=QRect(1,1,height()-2,height()-2); - buttonRadius2_=pow(buttonRect_.width()/2,2); -} - -void ServerRefreshInfoWidget::mouseDoubleClickEvent(QMouseEvent* event) -{ - if(server_ && !isInButton(event->pos())) - { - Q_EMIT serverSettingsEditRequested(server_); - } -} - -void ServerRefreshInfoWidget::mousePressEvent(QMouseEvent* event) -{ - if(!refreshAction_->isEnabled()) - return; - - //We are in the button - if(isInButton(event->pos())) - { -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG -// UiLog().dbg() << "pressed"; -#endif - if(currentComponent_ != ButtonComponent) - { - currentComponent_ = ButtonComponent; - } - userInitiatedRefresh_=true; - refreshAction_->trigger(); - } -} - -void ServerRefreshInfoWidget::mouseMoveEvent(QMouseEvent* event) -{ - //We are in the button - if(isInButton(event->pos())) - { -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG -// UiLog().dbg() << "inButton"; -#endif - //we just entered the button - if(currentComponent_ != ButtonComponent) - { - currentComponent_=ButtonComponent; - adjustToolTip(); - if(refreshAction_->isEnabled()) - { - update(); //rerender - } - } - } - //We are in the progress part - else if(isInText(event->pos())) - { -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG -// UiLog().dbg() << "inText"; -#endif - //we came from the button - if(currentComponent_ == ButtonComponent) - { - currentComponent_=TextComponent; - adjustToolTip(); - if(refreshAction_->isEnabled()) - { - update(); //rerender - } - } - //we came from outside - else if(currentComponent_ != TextComponent) - { - currentComponent_=TextComponent; - adjustToolTip(); - } - } -} - -void ServerRefreshInfoWidget::leaveEvent(QEvent*) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - //UI_FUNCTION_LOG -#endif - currentComponent_=NoComponent; - update(); //rerender -} - - -void ServerRefreshInfoWidget::paintEvent(QPaintEvent*) -{ - QPainter painter(this); - drawProgress(&painter); - drawButton(&painter); -} - - -QString ServerRefreshInfoWidget::formatPeriodTime(int timeInSec) const -{ - int h=timeInSec/3600; - int r=timeInSec%3600; - int m=r/60; - int s=r%60; - - QTime t(h,m,s); - if(h > 0) - return QString::number(h) + QString(":%1h").arg(m, 2, 10, QChar('0')); - else if(m >= 5) - return QString::number(m) + QString(":%1s").arg(s, 2, 10, QChar('0')); - else if(m> 0) - return QString::number(m*60+s) + "s"; - else - return QString::number(s) + "s"; - - return QString(); -} - -void ServerRefreshInfoWidget::determinePeriodText() -{ - periodText_.clear(); - driftText_.clear(); - if(hasInfo_) - { - //Unicode 916=Greek capital delta - periodText_=QString(" ") + QChar(916) + QString("=") + formatPeriodTime(total_) + " "; - if(drift_ > 0) - { - driftText_="d=" + formatPeriodTime(total_-period_) + " "; - } - } -} - -QString ServerRefreshInfoWidget::fullPeriodText() const -{ - return periodText_+driftText_; -} - -int ServerRefreshInfoWidget::determinePeriodTextWidthMin() const -{ - QString s=periodDummyText_; - if(hasInfo_ && drift_ > 0) - { - s=periodDummyFullText_; - } - return fmPeriod_.width(s); -} - -//Indicate if the full period text's size will change in such a way that the -//geometry needs to be adjusted -bool ServerRefreshInfoWidget::periodTextWidthAboutToChange() const -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - - int mval=determinePeriodTextWidthMin(); - - QString pt=fullPeriodText(); -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UiLog().dbg() << " full=" << pt << " pt.size=" << pt.size() << - " mval=" << mval << " periodTextWidthMin=" << periodTextWidthMin_ << - " periodTextSize " << periodTextWidth_; -#endif - bool changed=false; - if(periodTextWidthMin_ == mval) - { - int w=fmPeriod_.width(pt); - if(w > periodTextWidth_) - { - changed=w > periodTextWidthMin_; - } - else if(w < periodTextWidth_) - { - changed=periodTextWidth_ >= periodTextWidthMin_; - } - } - else - { - changed=true; - } -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UiLog().dbg() << " changed=" << changed; -#endif - return changed; -} - -void ServerRefreshInfoWidget::adjustGeometry(bool doFetchInfo) -{ -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - - if(server_) - { - //timeTextLen_=qMax(fmTime_.width(" <9:59m"),fmUpdate_.width("updating")); - - //Define server rect - int h=serverRectHeight_; //fmServer_.height()+progRectHeight_; //;2*yPadding; - int currentRight=0; - - serverRect_=QRect(buttonRect_.center().x()+4,(height()-h)/2, - buttonRect_.width()/2-4+4+fmServer_.width(serverText_), - h); - currentRight=serverRect_.x()+serverRect_.width(); - - if(doFetchInfo) - fetchInfo(); - - //Determine the minimum size for the period text - periodTextWidthMin_=determinePeriodTextWidthMin(); - - //Compute physical width of the period text - QString pt=fullPeriodText(); - int w=fmPeriod_.width(pt); - if(w <= periodTextWidthMin_) - { - periodTextWidth_=periodTextWidthMin_; - } - else - { -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UiLog().dbg() << " width changed before=" << periodTextWidth_; -#endif - periodTextWidth_=w; -#ifdef _UI_SERVERCOMINFOWIDGET_DEBUG - UiLog().dbg() << " width changed after=" << periodTextWidth_; -#endif - - } - periodRect_=QRect(); - progRect_=QRect(); - lastRect_=QRect(); - - if(hasInfo_) - { - periodRect_ = serverRect_; - periodRect_.setX(serverRect_.x()+serverRect_.width()); - periodRect_.setWidth(periodTextWidth_); - //periodRect_.setHeight(fmPeriod_.height()-2); - periodRect_.setHeight(serverRect_.height()-progRectHeight_); - - currentRight+=periodRect_.width(); - - progRect_ = periodRect_; - progRect_.setY(periodRect_.y()+periodRect_.height()); - progRect_.setHeight(serverRect_.height()-periodRect_.height()); - } - - lastTextWidth_=0; - if((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) - { - //Compute physical width of the last refresh text - lastTextWidth_=fmLast_.width(" last: 22:22:22 "); - - lastRect_ = QRect(currentRight,serverRect_.y(),lastTextWidth_,h); - currentRight+=lastRect_.width()+6; - } - else - currentRight+=6; - - setFixedWidth(currentRight); - } - else - { - periodTextWidthMin_=0; - periodTextWidth_=0; - lastTextWidth_=0; - setFixedWidth(buttonRect_.x()+buttonRect_.width()+4); - } -} - - -void ServerRefreshInfoWidget::drawButton(QPainter* painter) -{ - painter->setRenderHint(QPainter::Antialiasing,true); - - if(server_) - { - //blink - if(inRefresh_ && - ((mode_ == NormalMode && period_ >= noBlinkLimit_) || - userInitiatedRefresh_ || mode_ == ManualMode )) - { - painter->setBrush(buttonBgRefreshBrush_); - painter->setPen(buttonRefreshPen_); - } - else - { - painter->setBrush((currentComponent_ == ButtonComponent)?buttonBgHoverBrush_:buttonBgBrush_); - - if(!refreshAction_->isEnabled()) - painter->setPen(disabledBorderPen_); - else - painter->setPen((currentComponent_ == ButtonComponent)?buttonHoverPen_:buttonBorderPen_); - } - } - else - { - painter->setBrush(buttonBgBrush_); - painter->setPen(buttonBorderPen_); - } - - //The filled circle - painter->drawEllipse(buttonRect_); - - //The reload icon - QRect r1=buttonRect_.adjusted(2,2,-2,-2); - QRect r2=r1.adjusted(3,3,-3,-3); - QPixmap pix=icon_->pixmap(QSize(r2.width(),r2.width()), - refreshAction_->isEnabled()? QIcon::Normal: QIcon::Disabled); - painter->drawPixmap(r2,pix); -} - -void ServerRefreshInfoWidget::drawProgress(QPainter* painter) -{ - if(!server_) - return; - - //server bg - painter->setBrush(serverBgBrush_); - painter->drawRect(serverRect_); - - if(needBorder_) - { - painter->setBrush(Qt::NoBrush); - painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); - painter->drawRect(serverRect_); - } - - //Server text - QRect serverTextRect=serverRect_.adjusted(buttonRect_.width()/2-4+4,0,0,0); - painter->setFont(fontServer_); - painter->setPen((refreshAction_->isEnabled())?serverTextPen_:disabledTextPen_); - painter->drawText(serverTextRect,Qt::AlignHCenter | Qt::AlignVCenter,serverText_); - - //The time rects and texts - if(hasInfo_) - { - //backgrounds - painter->setPen(Qt::NoPen); - painter->setBrush(periodBgBrush_); - painter->drawRect(periodRect_); - painter->setBrush(progBgBrush_); - painter->drawRect(progRect_); - - //border - if(needBorder_) - { - painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); - painter->setBrush(Qt::NoBrush); - painter->drawRect(periodRect_.adjusted(0,0,0,progRect_.height())); - } - - painter->setFont(fontPeriod_); - - //period + drift text - if(drift_ > 0) - { - int tw=fmPeriod_.width(fullPeriodText()); - int w=periodRect_.width(); - QRect rr=periodRect_.adjusted((w-tw)/2,0,-(w-tw)/2,0); - - painter->setPen(periodTextPen_); - painter->drawText(rr,Qt::AlignLeft | Qt::AlignVCenter,periodText_); - painter->setPen(driftTextPen_); - painter->drawText(rr,Qt::AlignRight| Qt::AlignVCenter,driftText_); - } - else - { - painter->setPen(periodTextPen_); - painter->drawText(periodRect_,Qt::AlignHCenter | Qt::AlignVCenter,periodText_); - } - - //Progress - float progress; - QRect actProgRect=progRect_; - - if(total_ < 2 || inRefresh_) - progress=1; - else - { - UI_ASSERT(total_ != 0, "total_=" << total_); - progress=(static_cast(total_-toNext_)/static_cast(total_)); - UI_ASSERT(progress >= 0. && progress <= 1.0001, "progress=" << progress); - if(progress >= 1.) progress=1; - - int progressW=static_cast(static_cast(actProgRect.width())*progress); - if(progressW <0) progressW=0; - else if(progressW > progRect_.width()) progressW=progRect_.width(); - actProgRect.setWidth(progressW); - } - - painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); - painter->drawLine(progRect_.topLeft(),progRect_.topRight()); - - painter->fillRect(actProgRect,progBrush_); - } - - //last refresh time - if((hasInfo_ && showLastAutoRefresh_) || !hasInfo_) - { - painter->setBrush(lastBgBrush_); - painter->setPen((refreshAction_->isEnabled())?borderPen_:disabledBorderPen_); - painter->drawRect(lastRect_); - - painter->setFont(fontLast_); - painter->setPen(lastTextPen_); - painter->drawText(lastRect_,Qt::AlignHCenter | Qt::AlignVCenter,"last: " + lastRefresh_); - } -} - -void ServerRefreshInfoWidget::adjustToolTip() -{ - QString txt; - - /* - if(mode_ == ContMode) - { - Q_ASSERT(server_); - Q_ASSERT(hasInfo_); - txt=tr("Refresh period is too short! Manual refreshing is disabled for server ") + - serverName_ + - "
      --------------------------------------------"+ - tr("
      Refresh period: ") + QString::number(total_) + "s" + - " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_-period_) +"s)"; - - } - */ - if(1) - { - if(currentComponent_ == ButtonComponent) - { - if(!server_) - { - txt=tr("Refresh selected server "); - } - else - { - txt=tr("Refresh server ") + serverName_ +" "; - } - - txt+=Viewer::formatShortCut(refreshAction_); - } - - if(hasInfo_) - { - Q_ASSERT(server_); - if(!txt.isEmpty()) - txt+="
      --------------------------------------------"; - else - txt+="Server: " + serverName_; - - txt+="
      Last refresh: " + lastRefresh_ + - "
      Next refresh: " + nextRefresh_ + - "
      Total refresh period: " + QString::number(total_) + "s" + - " (base=" + QString::number(period_) + "s" + ",drifted=" + QString::number(total_-period_) +"s)"; - - /*if(drift_ > 0) - { - txt+="
      --------------------------------------------
      "; - txt+="When drift is enabled the server refresh period is increased at every automatic refresh until \ - the maximum period is reached. The drift is reset to zero when the user interacts with the server."; - - }*/ - - - } - else if(server_) - { - if(!txt.isEmpty()) - txt+="
      --------------------------------------------"; - else - txt+="Server: " + serverName_; - - txt+="
      Last refresh: " + lastRefresh_ + "
      " + - "Automatic refresh is disabled!"; - - } - - if(currentComponent_ != ButtonComponent && server_) - { - txt+="
      " + Viewer::formatShortCut("Double click to change refresh settings for server"); - } - } - - setToolTip(txt); -} - -#if 0 -QString ServerRefreshInfoWidget::formatTime(int timeInSec) const -{ - int h=timeInSec/3600; - int r=timeInSec%3600; - int m=r/60; - int s=r%60; - - QTime t(h,m,s); - if(h > 0) - return QString::number(h) + "h"; - else if(m > 2) - { - if(s >= 30) - return QString::number(m+1) + "m"; - else - return QString::number(m) + "m"; - } - else if(m >= 1) - { - if(s >= 45) - return "1:45m"; - else if(s >= 30) - return "1:30m"; - else if(s >= 15) - return "1:15m"; - else - return "1m"; - } - else if(s > 50) - return "<1m"; - else if(s > 45) - return "<50s"; - else if(s > 40) - return "<45s"; - else if(s > 35) - return "<40s"; - else if(s > 30) - return "<35s"; - else if(s > 25) - return "<30s"; - else if(s > 20) - return "<25s"; - else if(s > 15) - return "<20s"; - else if(s > 10) - return "<15s"; - else if(s > 5) - return "<10s"; - else - return "<5s"; //return QString("%1s").arg(s, 2, 10, QChar('0')); - - return QString(); -} -#endif - - - -void ServerRefreshInfoWidget::printStatus() const -{ - UiLog().dbg() << " server=" << server_ << " action=" << refreshAction_->isEnabled() << " hasInfo=" << hasInfo_ << - " mode=" << mode_ << " inRefresh=" << inRefresh_ << " timer=" << timer_->isActive() << - " timeout=" << timer_->interval()/1000. << "s"; -} - -ServerComActivityLine::ServerComActivityLine(QWidget *parent) : - QWidget(parent), - font_(QFont()), - fm_(font_), - server_(0) -{ - font_.setPointSize(font_.pointSize()-1); - fm_=QFontMetrics(font_); - - int width_=200; - int height_=fm_.height()+4+6; - - timer_=new QTimer(this); - - connect(timer_,SIGNAL(timeout()), - this,SLOT(update())); - - timer_->setInterval(1000); - timer_->start(); - - setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Minimum); - setMinimumSize(width_,height_); -} - -void ServerComActivityLine::setServer(ServerHandler* server) -{ - server_=server; -} - -void ServerComActivityLine::paintEvent(QPaintEvent*) -{ -#if 0 - if(!server_) - return; - - int currentRight=0; - int offset=4; - int yPadding=2; - - int period,total,drift,toNext; - bool hasUpdate=server_->updateInfo(period,total,drift,toNext); - bool hasDrift=(hasUpdate && drift > 0); - int h=height()-2*yPadding; - int progHeight=h; - int driftHeight=0; - - - if(hasDrift) - { - progHeight=fm_.height()+2; - driftHeight=h-progHeight; - } - - //progress rect - with the server name in it - QRect progRect=QRect(offset,yPadding, - fm_.width("ABCDEABDCEABCD"), - progHeight); - - QString progText=fm_.elidedText(QString::fromStdString(server_->name()), - Qt::ElideRight,progRect.width()); - - //drif rect - QRect driftRect; - if(hasDrift) - { - driftRect=progRect; - driftRect.setY(yPadding+progHeight); - driftRect.setHeight(driftHeight); - } - - //toNext rect - currentRight=progRect.x()+progRect.width()+offset; - - QString toNextText=QString::number(total) + "s"; - if(hasUpdate) - toNextText=formatTime(toNext); - - QRect toNextRect=progRect; - toNextRect.setX(currentRight); - toNextRect.setWidth(fm_.wi - -#endif - - -} - - - diff -Nru ecflow-4.9.0/Viewer/src/ServerComInfoWidget.hpp ecflow-4.11.1/Viewer/src/ServerComInfoWidget.hpp --- ecflow-4.9.0/Viewer/src/ServerComInfoWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComInfoWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,205 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SERVERCOMLINE_HPP -#define SERVERCOMLINE_HPP - -#include -#include -#include -#include -#include -#include - -#include "FontMetrics.hpp" -#include "ServerObserver.hpp" -#include "ServerComObserver.hpp" -#include "VProperty.hpp" - -class QAction; -class QPainter; -class QTimer; - -class PropertyMapper; -class ServerHandler; -class ServerUpdateData; - - -class ServerRefreshInfoWidget : public QWidget, public ServerObserver, public ServerComObserver, - public VPropertyObserver -{ -Q_OBJECT - -public: - explicit ServerRefreshInfoWidget(QAction* refreshAction,QWidget* parent=0); - ~ServerRefreshInfoWidget(); - - void setServer(ServerHandler* server); - - void notifyChange(VProperty* p); - - void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler*); - void notifyEndServerScan(ServerHandler*); - void notifyServerActivityChanged(ServerHandler*); - - void notifyRefreshTimerStarted(ServerHandler* server); - void notifyRefreshTimerStopped(ServerHandler* server); - void notifyRefreshTimerChanged(ServerHandler* server); - void notifyRefreshScheduled(ServerHandler* server); - void notifyRefreshFinished(ServerHandler* server); - -protected Q_SLOTS: - void slotTimeOut(); - void slotTimeOutRefreshFinished(); - -Q_SIGNALS: - void serverSettingsEditRequested(ServerHandler*); - -protected: - void resizeEvent(QResizeEvent* event); - void mouseDoubleClickEvent(QMouseEvent* event); - void mousePressEvent(QMouseEvent* event); - void mouseMoveEvent(QMouseEvent* e); - void leaveEvent(QEvent*); - void paintEvent(QPaintEvent*); - - void updateSettings(); - void reloadAll(); - void fetchInfo(); - void drawButton(QPainter*); - void drawProgress(QPainter*); - - QString formatPeriodTime(int timeInSec) const; - void determinePeriodText(); - QString fullPeriodText() const; - int determinePeriodTextWidthMin() const; - bool periodTextWidthAboutToChange() const; - void adjustGeometry(bool); - void adjustTimer(int toNext); - void adjustToolTip(); - - QString formatTime(int timeInSec) const; - bool isInButton(const QPoint& pos) const; - bool isInText(const QPoint& pos) const; - void printStatus() const; - bool isActionEnabled() const; - - enum Component {ButtonComponent,TextComponent,ConfigComponent,NoComponent}; - enum Mode {NormalMode,ManualMode,NoMode}; - - QAction* refreshAction_; - ServerHandler* server_; - QString serverName_; - QString serverText_; - QTimer *timer_; - QTime inRefreshElapsed_; - - QFont fontServer_; - QFont fontPeriod_; - QFont fontLast_; - QFontMetrics fmServer_; - FontMetrics fmServerReal_; - QFontMetrics fmPeriod_; - QFontMetrics fmLast_; - - static QIcon *icon_; - static QPen borderPen_; - static QPen buttonBorderPen_; - static QPen disabledBorderPen_; - static QBrush serverBgBrush_; - static QBrush buttonBgBrush_; - static QBrush buttonBgHoverBrush_; - static QPen buttonHoverPen_; - static QBrush buttonBgRefreshBrush_; - static QPen buttonRefreshPen_; - static QBrush periodBgBrush_; - static QBrush progBrush_; - static QBrush progBgBrush_; - static QBrush lastBgBrush_; - static QPen serverTextPen_; - static QPen periodTextPen_; - static QPen driftTextPen_; - static QPen lastTextPen_; - static QPen disabledTextPen_; - - QRect buttonRect_; - int buttonRadius2_; - QString periodText_; - QString driftText_; - int periodTextWidth_; - int periodTextWidthMin_; - QString periodDummyText_; - QString periodDummyFullText_; - int lastTextWidth_; - QRect serverRect_; - QRect periodRect_; - QRect progRect_; - QRect lastRect_; - Component currentComponent_; - int progRectHeight_; - int serverRectHeight_; - int serverYPadding_; - - PropertyMapper* prop_; - - Mode mode_; - int noBlinkLimit_; - bool hasInfo_; - bool inRefresh_; - bool userInitiatedRefresh_; - bool showLastAutoRefresh_; - QString lastRefresh_; - QString nextRefresh_; - int total_; - int period_; - int toNext_; - int drift_; - bool needBorder_; - -}; - -class ServerComActivityLine : public QWidget - -{ -//Q_OBJECT - -public: - explicit ServerComActivityLine(QWidget* parent=0); - - void setServer(ServerHandler* server); - -protected: - void paintEvent(QPaintEvent*); - - QFont font_; - QFontMetrics fm_; - ServerHandler* server_; - QPixmap pix_; - QTimer *timer_; -}; - -#if 0 -class ServerRefreshInfoWidget : public QWidget -{ -public: - explicit ServerRefreshInfoWidget(QAction* refreshAction,QWidget *parent=0); - - void setServer(ServerHandler* server); - -protected: - QAction* refreshAction_; - ServerComLineDisplay* infoW_; -}; -#endif - - -#endif // SERVERCOMLINE_HPP diff -Nru ecflow-4.9.0/Viewer/src/ServerComObserver.hpp ecflow-4.11.1/Viewer/src/ServerComObserver.hpp --- ecflow-4.9.0/Viewer/src/ServerComObserver.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComObserver.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERCOMOBSERVER_HPP -#define SERVERCOMOBSERVER_HPP - -class ServerHandler; - -class ServerComObserver -{ -public: - ServerComObserver() {} - virtual ~ServerComObserver() {} - - virtual void notifyRefreshTimerStarted(ServerHandler* server) {} - virtual void notifyRefreshTimerStopped(ServerHandler* server) {} - virtual void notifyRefreshTimerChanged(ServerHandler* server) {} - virtual void notifyRefreshScheduled(ServerHandler* server) {} - virtual void notifyRefreshFinished(ServerHandler* server) {} -}; - -#endif // SERVERCOMOBSERVER_HPP - diff -Nru ecflow-4.9.0/Viewer/src/ServerComQueue.cpp ecflow-4.11.1/Viewer/src/ServerComQueue.cpp --- ecflow-4.9.0/Viewer/src/ServerComQueue.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComQueue.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,622 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerComQueue.hpp" - -#include "ClientInvoker.hpp" -#include "ServerComThread.hpp" -#include "ServerHandler.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" - -#include "Log.hpp" - -#define _UI_SERVERCOMQUEUE_DEBUG - -// This class manages the tasks to be sent to the ServerComThread, which controls -// the communication with the ClientInvoker. The ClientInvoker is hidden from the -// ServerHandler. The ServerHandler defines a task and sends it to -// ServerComQueue, which then passes it on to the ClientInvoker. When a task is finished -// ServerComQueue notifies the ServerHandler about it. - -ServerComQueue::ServerComQueue(ServerHandler *server,ClientInvoker *client) : - QObject(server), - server_(server), - client_(client), - comThread_(0), - timeout_(5), - ctStartTimeout_(1000), - ctStartWaitTimeout_(500), //wait() is a blocking call, so it should be short - startTimeoutTryCnt_(0), - ctMaxStartTimeoutTryCnt_(4), - state_(NoState), //the queue is enabled but not running - taskStarted_(false), - taskIsBeingFinished_(false), - taskIsBeingFailed_(false) -{ - timer_=new QTimer(this); - timer_->setInterval(timeout_); - - connect(timer_,SIGNAL(timeout()), - this,SLOT(slotRun())); - - - createThread(); -} - -ServerComQueue::~ServerComQueue() -{ - state_=DisabledState; - - //Stop the timer - timer_->stop(); - - //Empty the tasks - tasks_.clear(); - - //Disconnects all the signals from the thread - comThread_->disconnect(0,this); - - //If the comthread is running we need to wait - //until it finishes its task. - comThread_->wait(); - - //Send a logout task - VTask_ptr task=VTask::create(VTask::LogOutTask); - comThread_->task(task); - - //Wait until the logout finishes - comThread_->wait(); - - delete comThread_; -} - -void ServerComQueue::createThread() -{ - if(comThread_) - delete comThread_; - - //We create a ServerComThread here. At this point the thread is not doing anything. - comThread_=new ServerComThread(server_,client_); - - //When the ServerComThread starts it emits a signal that - //is connected to the queue. - connect(comThread_, SIGNAL(started()), - this, SLOT(slotTaskStarted())); - - //When the ServerComThread finishes it emits a signal that - //is connected to the queue. - connect(comThread_, SIGNAL(finished()), - this, SLOT(slotTaskFinished())); - - //When there is an error in ServerComThread it emits the - //failed() signal that is connected to the queue. - connect(comThread_, SIGNAL(failed(std::string)), - this, SLOT(slotTaskFailed(std::string))); - - //The ServerComThread is observing the actual server and its nodes. When there is a change it - //emits a signal to notify the ServerHandler about it. - connect(comThread_,SIGNAL(nodeChanged(const Node*, std::vector)), - server_,SLOT(slotNodeChanged(const Node*, std::vector))); - - connect(comThread_,SIGNAL(defsChanged(std::vector)), - server_,SLOT(slotDefsChanged(std::vector))); - - connect(comThread_,SIGNAL(rescanNeed()), - server_,SLOT(slotRescanNeed())); -} - - -void ServerComQueue::enable() -{ - state_=NoState; - start(); -} - -void ServerComQueue::disable() -{ - if(state_ == DisabledState) - return; - - UiLog(server_).dbg() << "ComQueue::disable -->"; - - state_=DisabledState; - - //Remove all tasks - tasks_.clear(); - - //Stop the timer - stopTimer(); - - //If the comthread is running we need to wait - //until it finishes its task. - comThread_->wait(); - - UiLog(server_).dbg() << " queue is disabled"; - - //Clear the current task - if(current_) - current_.reset(); - - taskStarted_=false; -} - - -//This is a special mode to reload the whole ClientInvoker -bool ServerComQueue::prepareReset() -{ - if(state_ == DisabledState || state_ == ResetState || state_ == SuspendedState) - return false; - - //Stop the timer - stopTimer(); - - //Remove all tasks - tasks_.clear(); - - taskStarted_=false; - - state_=ResetState; - - //If the comthread is running we need to wait - //until it finishes its task. - comThread_->wait(); - - //The thread cannot be running - assert(comThread_->isRunning() == false); - //assert(taskIsBeingFinished_==false); - //assert(taskIsBeingFailed_==false); - //assert(!current_); - - return true; -} - -//This is a special mode to reload the whole ClientInvoker. Must be called after prepareReset returned true; -void ServerComQueue::reset() -{ - assert(state_ == ResetState); - - //The thread cannot be running - assert(comThread_->isRunning() == false); - - //We send a Reset command to the thread!! This is the only task that is allowed - //during the reset!! - VTask_ptr task=VTask::create(VTask::ResetTask); - tasks_.push_back(task); - - //TODO: why do we not run it directly - //We start the timer with a shorter interval - timer_->start(100); -} - -void ServerComQueue::endReset() -{ - if(state_ == ResetState) - { - state_=SuspendedState; - start(); - } -} - - -//When the queue is started: -// -it is ready to accept tasks -// -its timer is running -void ServerComQueue::start() -{ - if(state_ != DisabledState && state_ != ResetState) - { - UiLog(server_).dbg() << "ComQueue::start -->"; - - //assert(taskIsBeingFinished_==false); - //assert(taskIsBeingFailed_==false); - //assert(!current_); - - startTimeoutTryCnt_=0; - taskStarted_=false; - - //If the comthread is running we need to wait - //until it finishes its task. - comThread_->wait(); - - state_=RunningState; - - UiLog(server_).dbg() << " thread finished"; - - //Starts the timer - timer_->start(timeout_); - - UiLog(server_).dbg() << " timer started"; - UiLog(server_).dbg() << "<-- ComQueue::start"; - } -} - -//The queue contents remains the same but the timer is stopped. Until start() is -//called nothing will be submitted to the queue. -void ServerComQueue::suspend(bool wait) -{ - if(state_ != DisabledState && state_ != ResetState && - state_ != SuspendedState) - { - state_=SuspendedState; - stopTimer(); - if(wait) - { - comThread_->wait(); - } - - //assert(taskIsBeingFinished_==false); - //assert(taskIsBeingFailed_==false); - //assert(!current_); - } -} - -void ServerComQueue::stopTimer() -{ - timer_->stop(); - startTimeoutTryCnt_=0; -} - -bool ServerComQueue::hasTask(VTask::Type t) const -{ - for(std::deque::const_iterator it=tasks_.begin(); it != tasks_.end(); ++it) - { - if(*it && (*it)->type() == t && (*it)->status() != VTask::CANCELLED && - (*it)->status() != VTask::ABORTED ) - return true; - - } - return false; -} - -bool ServerComQueue::isNextTask(VTask::Type t) const -{ - return (!tasks_.empty() && tasks_.back()->type() == t); -} - - -void ServerComQueue::addTask(VTask_ptr task) -{ - if(!task) - return; - - if(isNextTask(VTask::ZombieListTask) && tasks_.back()->type() == task->type()) - return; - - if(state_ == DisabledState || state_ == ResetState || - (task && task->type() ==VTask::ResetTask) ) - return; - - tasks_.push_back(task); - if(state_ != SuspendedState) - { - if(!timer_->isActive()) - { - //we immediately execute the "current" task - slotRun(); - //and only start the timer after it - timer_->start(timeout_); - } - } -} - -void ServerComQueue::addNewsTask() -{ - if(state_ == DisabledState || state_ == ResetState) - return; - - if(isNextTask(VTask::NewsTask)) - return; - - VTask_ptr task=VTask::create(VTask::NewsTask); - addTask(task); - server_->refreshScheduled(); -} - -void ServerComQueue::addSyncTask() -{ - if(state_ == DisabledState || state_ == ResetState) - return; - - if(isNextTask(VTask::SyncTask)) - return; - - VTask_ptr task=VTask::create(VTask::SyncTask); - addTask(task); -} - -void ServerComQueue::addSuiteListTask() -{ - if(state_ == DisabledState || state_ == ResetState) - return; - - if(isNextTask(VTask::SuiteListTask)) - return; - - VTask_ptr task=VTask::create(VTask::SuiteListTask); - addTask(task); -} - -void ServerComQueue::addSuiteAutoRegisterTask() -{ - if(state_ == DisabledState || state_ == ResetState) - return; - - if(isNextTask(VTask::SuiteAutoRegisterTask)) - return; - - VTask_ptr task=VTask::create(VTask::SuiteAutoRegisterTask); - addTask(task); -} - -void ServerComQueue::startCurrentTask() -{ - taskStarted_=false; - ctStartTime_.start(); - comThread_->task(current_); -} - -void ServerComQueue::slotRun() -{ - if(state_ == DisabledState ||state_ == SuspendedState ) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UI_FUNCTION_LOG_S(server_); - UiLog(server_).dbg() << " queue is either disabled or suspended"; -#endif - return; - } - - if(taskIsBeingFinished_ || taskIsBeingFailed_) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UI_FUNCTION_LOG_S(server_); - UiLog(server_).dbg() << " task is either being finished or failed"; -#endif - return; - } - - if(tasks_.empty() && !current_) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UI_FUNCTION_LOG_S(server_); - UiLog(server_).dbg() << " there are no tasks! Stop timer!"; -#endif - timer_->stop(); - return; - } - -#if 0 -#ifdef _UI_SERVERCOMQUEUE_DEBUG - if(tasks_.size() > 0) - { - UI_FUNCTION_LOG_S(server_); - UiLog(server_).dbg() << " number of tasks: " << tasks_.size(); - for(std::deque::const_iterator it=tasks_.begin(); it != tasks_.end(); it++) - { - UiLog(server_).dbg() << " task: " << (*it)->typeString(); - } - } -#endif -#endif - - //If a task was sent to the thread but the queue did not get the - //notification about the thread's start there is a PROBLEM! - //Note: when the thread starts it emits a signal to the queue - //and the queue sets taskStarted_ to true. Since this is a queued communication - //there can be a time lag between the two events. We set a timeout for it! - //If we pass the timeout we stop the thread and try to resend the task! - if(current_ && !taskStarted_ && ctStartTime_.elapsed() > ctStartTimeout_) - { - UI_FUNCTION_LOG_S(server_); - if(startTimeoutTryCnt_ < ctMaxStartTimeoutTryCnt_) - { - UiLog(server_).warn() << " ServerCom thread does not seem to have started within the allocated timeout. \ - startTimeoutTryCnt_=" << startTimeoutTryCnt_; - - startTimeoutTryCnt_++; - } - else - { - startTimeoutTryCnt_=0; - - //So if we are here: - //-the thread did not emit a notification about its start - //-the elapsed time since we sent the task to the thread past the timeout. - //-since the timeout was passed slotRun() has been was called at least startTimeoutTryCnt_ times - - bool running=comThread_->isRunning(); - - //Problem 1: - //-the thread is running - if(running) - { - UiLog(server_).warn() << " It seems that the ServerCom thread started but it is in a bad state. Try to run task again."; - } - - //Problem 2: - //-the thread is not running - else - { - UiLog(server_).warn() << " It seems that the ServerCom thread could not start. Try to run task again."; - } - - if(comThread_->wait(ctStartWaitTimeout_) == false) - { - UiLog(server_).warn() << " Calling wait() on the ServerCom thread failed."; - - //The thread started to run in the meantime. We check its status again at the next slotRun call. - if(!running && comThread_->isRunning()) - { - UiLog(server_).warn() << " It seems that in the meantime the thread started to run.\ - We will check its state again"; - return; - } - - //Otherwise there is nothing to do!! - //We exit here because when we tried to call terminate() it just hung!! - UI_ASSERT(0,"Cannot stop ServerCom thread, which is in a bad state"); - exit(1); -#if 0 - comThread_->terminate(); - if(comThread_->wait(taskTimeout_) == false) - { - UiLog(server_).err() << " Calling wait() after terminate() on the ServerCom thread failed"; - UiLog(server_).dbg() << "Delete the current ComThread and create a new one."; - createThread(); - UI_ASSERT(0,"Cannot stop ServerCom thread that is in a bad state"); - - } - else - { - UiLog(server_).dbg() << " Terminating ServerCom thread succeeded."; - } -#endif - } - - UiLog(server_).warn() << " Calling wait() on the ServerCom thread succeeded."; - - Q_ASSERT(comThread_->isRunning() == false); - - if(current_->status() != VTask::CANCELLED && - current_->status() != VTask::ABORTED ) - { - startCurrentTask(); - return; - } - else - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UiLog(server_).dbg() << " current_ aborted or cancelled. Reset current_ !"; -#endif - current_.reset(); - } - } - } - - if(current_) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - //UiLog(server_).dbg() << " still processing reply from previous task"; -#endif - return; - } - - if(comThread_->isRunning()) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - //UiLog(server_).dbg() << " thread is active"; -#endif - return; - } - - //We search for the first non-cancelled/aborted task - while(!tasks_.empty()) - { - current_=tasks_.front(); - tasks_.pop_front(); - if(current_->status() != VTask::CANCELLED && - current_->status() != VTask::ABORTED ) - { - break; - } - current_.reset(); - } - - if(!current_) - { - timer_->stop(); - return; - } - -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UiLog(server_).dbg() << " run task: " << current_->typeString(); -#endif - - //Send it to the thread - startCurrentTask(); -} - -//This slot is called when ComThread finishes its task. At this point the -//thread is not running so it is safe to access the ClientInvoker! -void ServerComQueue::slotTaskStarted() -{ - taskStarted_=true; -} - -//This slot is called when ComThread finishes its task. At this point the -//thread is not running so it is safe to access the ClientInvoker! -void ServerComQueue::slotTaskFinished() -{ - taskStarted_=false; - taskIsBeingFinished_=true; - startTimeoutTryCnt_=0; - - UiLog(server_).dbg() << "ComQueue::slotTaskFinished -->"; - - //We need to leave the load mode - endReset(); - - //If the current task is empty there must have been an error that was - //handled by the sloTaskFailed slot. - if(current_) - { -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UiLog(server_).dbg() << " reset current_"; -#endif - - VTask_ptr task=current_; - current_.reset(); - - //We notify the server that the task has finished and the results can be accessed. - server_->clientTaskFinished(task,client_->server_reply()); - } - - taskIsBeingFinished_=false; -} - -//This slot is called when the task failed in the ComThread. Right after this signal is emitted -//the thread will finish and and emits the finished() signal that is connected -//to the slotTaskFinished slot. -void ServerComQueue::slotTaskFailed(std::string msg) -{ - taskStarted_=false; - taskIsBeingFailed_=true; - startTimeoutTryCnt_=0; - - UiLog(server_).dbg() << "ComQueue::slotTaskFailed -->"; -#ifdef _UI_SERVERCOMQUEUE_DEBUG - if(current_) - UiLog(server_).dbg() << " current_ exists"; - else - UiLog(server_).dbg() << " current_ is null"; -#endif - - //We need to leave the load mode - endReset(); - -#ifdef _UI_SERVERCOMQUEUE_DEBUG - if(current_) - UiLog(server_).dbg() << " current_ exists"; - else - UiLog(server_).dbg() << " current_ is null"; -#endif - -#ifdef _UI_SERVERCOMQUEUE_DEBUG - UiLog(server_).dbg() << " reset current_"; -#endif - assert(current_); - VTask_ptr task=current_; - current_.reset(); - - //We notify the server that the task has failed - server_->clientTaskFailed(task,msg); - - taskIsBeingFailed_=false; -} diff -Nru ecflow-4.9.0/Viewer/src/ServerComQueue.hpp ecflow-4.11.1/Viewer/src/ServerComQueue.hpp --- ecflow-4.9.0/Viewer/src/ServerComQueue.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComQueue.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERCOMQUEUE_HPP_ -#define SERVERCOMQUEUE_HPP_ - -#include -#include - -#include "VTask.hpp" - -#include -#include -#include - -class ClientInvoker; -class NodeObserver; -class ServerHandler; -class ServerComThread; - -// -------------------------------------------------------------- -// ServerComQueue - a class to provide a queueing system for -// sending tasks to the ClientIvoker via the ServerComThread. -// -------------------------------------------------------------- - -class ServerComQueue : public QObject -{ -Q_OBJECT - -public: - ServerComQueue(ServerHandler *server,ClientInvoker* client); - ~ServerComQueue(); - - enum State {NoState,RunningState,SuspendedState,ResetState,DisabledState}; - State state() const {return state_;} - - void addTask(VTask_ptr); - void addNewsTask(); - void addSyncTask(); - void addSuiteListTask(); - void addSuiteAutoRegisterTask(); - - void enable(); - void disable(); - void start(); - void suspend(bool); - bool prepareReset(); - void reset(); - bool isSuspended() const {return state_==SuspendedState;} - -protected Q_SLOTS: - void slotRun(); - void slotTaskStarted(); - void slotTaskFinished(); - void slotTaskFailed(std::string); - -protected: - void createThread(); - void stopTimer(); - void startCurrentTask(); - void endReset(); - bool hasTask(VTask::Type t) const; - bool isNextTask(VTask::Type t) const; - - ServerHandler *server_; - ClientInvoker* client_; - ServerComThread *comThread_; - QTimer* timer_; - int timeout_; - QTime ctStartTime_; - int ctStartTimeout_; - int ctStartWaitTimeout_; - int startTimeoutTryCnt_; - int ctMaxStartTimeoutTryCnt_; - std::deque tasks_; - VTask_ptr current_; - State state_; - bool taskStarted_; - bool taskIsBeingFinished_; - bool taskIsBeingFailed_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerComThread.cpp ecflow-4.11.1/Viewer/src/ServerComThread.cpp --- ecflow-4.9.0/Viewer/src/ServerComThread.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComThread.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,568 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerComThread.hpp" - -#include "Defs.hpp" -#include "ClientInvoker.hpp" -#include "ArgvCreator.hpp" - -#include "ServerDefsAccess.hpp" -#include "ServerComQueue.hpp" -#include "ServerHandler.hpp" -#include "SuiteFilter.hpp" -#include "UiLog.hpp" - -#include - -#define _UI_SERVERCOMTHREAD_DEBUG - -ServerComThread::ServerComThread(ServerHandler *server, ClientInvoker *ci) : - server_(server), - ci_(ci), - taskType_(VTask::NoTask), - rescanNeed_(false), - hasSuiteFilter_(false), - autoAddNewSuites_(false), - maxLineNum_(-1) -{ - assert(server_); -} - -ServerComThread::~ServerComThread() -{ - detach(); -} - -void ServerComThread::task(VTask_ptr task) -{ - //do not execute thread if already running - - if(isRunning()) - { - UiLog(serverName_).err() << "ComThread::task - thread already running, will not execute command"; - } - else - { - //if(!server_ && server) - // initObserver(server); - - //We set the parameters needed to run the task. These members are not protected by - //a mutex, because apart from this function only run() can access them!! - serverName_=server_->longName(); - command_=task->command(); - params_=task->params(); - contents_=task->contents(); - vars_=task->vars(); - nodePath_.clear(); - taskType_=task->type(); - nodePath_=task->targetPath(); - zombie_=task->zombie(); - - //Suite filter - hasSuiteFilter_=server_->suiteFilter()->isEnabled(); - autoAddNewSuites_=server_->suiteFilter()->autoAddNewSuites(); - if(hasSuiteFilter_) - filteredSuites_=server_->suiteFilter()->filter(); - else - filteredSuites_.clear(); - - maxLineNum_=server_->conf()->intValue(VServerSettings::MaxOutputFileLines); - - //Start the thread execution - start(); - } -} - -void ServerComThread::run() -{ - UiLog(serverName_).dbg() << "ComThread::run --> path=" << nodePath_; - - //Init flags - rescanNeed_=false; - bool isMessage = false; - std::string errorString; - - try - { - switch (taskType_) - { - case VTask::CommandTask: - { - // call the client invoker with the saved command - UiLog(serverName_).dbg() << " COMMAND"; - ArgvCreator argvCreator(command_); -#ifdef _UI_SERVERCOMTHREAD_DEBUG - UiLog(serverName_).dbg() << " args=" << argvCreator.toString(); -#endif - ci_->invoke(argvCreator.argc(), argvCreator.argv()); - - /*ci_->news_local(); - switch (ci_->server_reply().get_news()) - { - case ServerReply::NO_NEWS: - case ServerReply::NEWS: - ci_->sync_local(); - break; - case ServerReply::DO_FULL_SYNC: - - break; - }*/ - break; - } - - case VTask::NewsTask: - { - UiLog(serverName_).dbg() << " NEWS"; - ci_->news_local(); // call the server - break; - } - - case VTask::SyncTask: - { - UiLog(serverName_).dbg() << " SYNC"; - sync_local(); - break; - } - - //This is called during reset - case VTask::ResetTask: - { - UiLog(serverName_).dbg() << " SYNC"; - reset(); - break; - } - - case VTask::JobTask: - case VTask::ManualTask: - case VTask::ScriptTask: - case VTask::OutputTask: - { - UiLog(serverName_).dbg() << " FILE" << " " << params_["clientPar"]; - if(maxLineNum_ < 0) - ci_->file(nodePath_,params_["clientPar"]); - else - ci_->file(nodePath_,params_["clientPar"],boost::lexical_cast(maxLineNum_)); - - break; - } - - case VTask::MessageTask: - { - UiLog(serverName_).dbg() << " EDIT HISTORY"; - ci_->edit_history(nodePath_); - break; - } - - case VTask::StatsTask: - { - UiLog(serverName_).dbg() << " STATS"; - ci_->stats(); - break; - } - - case VTask::HistoryTask: - { - UiLog(serverName_).dbg() << " SERVER LOG"; - ci_->getLog(100); - break; - } - - case VTask::ScriptPreprocTask: - UiLog(serverName_).dbg() << " SCRIP PREPROCESS"; - ci_->edit_script_preprocess(nodePath_); - break; - - case VTask::ScriptEditTask: - UiLog(serverName_).dbg() << " SCRIP EDIT"; - ci_->edit_script_edit(nodePath_); - break; - - case VTask::ScriptSubmitTask: - UiLog(serverName_).dbg() << " SCRIP SUBMIT"; - ci_->edit_script_submit(nodePath_, vars_, contents_, - (params_["alias"]=="1")?true:false, - (params_["run"] == "1")?true:false); - break; - - case VTask::SuiteListTask: - UiLog(serverName_).dbg() << " SUITES"; - ci_->suites(); - break; - - case VTask::SuiteAutoRegisterTask: - UiLog(serverName_).dbg() << " SUITE AUTO REGISTER"; - if(hasSuiteFilter_) - { - ci_->ch1_auto_add(autoAddNewSuites_); - } - break; - - case VTask::ZombieCommandTask: - { - std::string cmd=params_["command"]; - UiLog(serverName_).dbg() << " ZOMBIE COMMAND " << cmd << " path=" << zombie_.path_to_task(); - if(cmd == "zombie_fob") - ci_->zombieFob(zombie_); - else if(cmd == "zombie_fail") - ci_->zombieFail(zombie_); - else if(cmd == "zombie_adopt") - ci_->zombieAdopt(zombie_); - else if(cmd == "zombie_block") - ci_->zombieBlock(zombie_); - else if(cmd == "zombie_remove") - ci_->zombieRemove(zombie_); - else if(cmd == "zombie_kill") - ci_->zombieKill(zombie_); - - break; - } - - case VTask::ZombieListTask: - UiLog(serverName_).dbg() << " ZOMBIES"; - ci_->zombieGet(); - break; - - case VTask::LogOutTask: - UiLog(serverName_).dbg() << " LOGOUT"; - detach(); - if(ci_->client_handle() > 0) - { - ci_->ch1_drop(); - } - break; - default: - break; - } - } - catch(std::exception& e) - { - isMessage = true; - errorString = e.what(); - } - - // we can get an error string in one of two ways - either an exception is raised, or - // the get_string() of the server reply is non-empty. - if (!isMessage && (taskType_ == VTask::CommandTask) && !(ci_->server_reply().get_string().empty())) - { - isMessage = true; - errorString = ci_->server_reply().get_string(); - } - - if (isMessage) - { - // note that we need to emit a signal rather than directly call a message function - // because we can't call Qt widgets from a worker thread - - UiLog(serverName_).dbg() << " thread failed: " << errorString; - Q_EMIT failed(errorString); - - //Reset flags - rescanNeed_=false; - - //This will stop the thread. - return; - } - - //Reset flags - rescanNeed_=false; - - //Can we use it? We are in the thread!!! - //UserMessage::message(UserMessage::DBG, false, std::string(" ServerComThread::run finished")); -} - - -void ServerComThread::sync_local() -{ - //For this part we need to lock the mutex on defs - { - ServerDefsAccess defsAccess(server_); - - UiLog(serverName_).dbg() << "ComThread::sync_local --> sync begin"; - ci_->sync_local(); - UiLog(serverName_).dbg() << " sync end"; - - //If a rescan or fullscan is needed we have either added/remove nodes or deleted the defs. - //So there were significant changes. - - //We detach the nodes currently available in defs, then we attach them again. We can still have nodes - //that were removed from the defs but are still attached. These will be detached when in ServerHandler we - //clear the tree. This tree contains shared pointers to the nodes, so when the tree is cleared - //the shared pointer are reset, the node descturctor is called and finally update_delete is called and - //we can detach the node. - - if(rescanNeed_ || ci_->server_reply().full_sync()) - { - UiLog(serverName_).dbg() << " rescan needed!"; - detach(defsAccess.defs()); - attach(defsAccess.defs()); - } - } -} - -void ServerComThread::reset() -{ - UiLog(serverName_).dbg() << "ComThread::reset -->"; - - //Lock the mutex on defs - ServerDefsAccess defsAccess(server_); - - //Detach the defs and the nodes from the observer - detach(defsAccess.defs()); - - //We drop all the handles belonging to the current user to have a proper clean-up! - //Other running instances of ecflow_ui under the same user will properly react - //to these changes and reset their handles! So it is a safe operation!!! - try - { - ci_->ch_drop_user(); - } - catch (std::exception &e) - { - UiLog(serverName_).warn() << " cannot drop handle for current user: " << e.what(); - } - - if(hasSuiteFilter_) - { - //reset client handle + defs - ci_->reset(); - - if(!filteredSuites_.empty()) - { - UiLog(serverName_).dbg() << " register suites"; - - //This will add a new handle to the client - ci_->ch_register(autoAddNewSuites_, filteredSuites_); - } - //If the suite filter is empty - else - { - //Registering with empty set would lead to retrieve all server content, - //opposite of expected result. So we just register a dummy suite - //to achive the our goal: for an empty suite filter no suites are retrieved. - UiLog(serverName_).dbg() << " register empty suite list"; - - std::vector fsl; - fsl.push_back(SuiteFilter::dummySuite()); - ci_->ch_register(autoAddNewSuites_, fsl); - } - } - else - { - // reset client handle + defs - ci_->reset(); - } - - UiLog(serverName_).dbg() << " sync begin"; - ci_->sync_local(); - UiLog(serverName_).dbg() << " sync end"; - - //Attach the nodes to the observer - attach(defsAccess.defs()); - - UiLog(serverName_).dbg() << "<-- ComThread::reset"; -} - -//This is an observer notification method!! -void ServerComThread::update(const Node* node, const std::vector& types) -{ - //This function can only be called during a SYNC_LOCAl task!!!! - assert(taskType_ == VTask::SyncTask); - - std::vector typesCopy=types; - - UiLog(serverName_).dbg() << "ComThread::update --> node: " << node->name(); - std::stringstream ss; - aspectToStr(ss,types); - UiLog(serverName_).dbg() << " aspects: " << ss.str(); - - //If a node was already requested to be added/deleted in the thread we do not go further. At the end of the sync - //we will regenerate everything (the tree as well in ServerHandle). - if(rescanNeed_) - { - UiLog(serverName_).dbg() << " rescanNeed already set"; - return; - } - - //This is a radical change - if((std::find(types.begin(),types.end(),ecf::Aspect::ADD_REMOVE_NODE) != types.end()) || - (std::find(types.begin(),types.end(),ecf::Aspect::ORDER) != types.end())) - { - UiLog(serverName_).dbg() << " emit rescanNeed()"; - rescanNeed_=true; - - //We notify ServerHandler about the radical changes. When ServerHandler receives this signal - //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are - //reset update_delete() might be called, so it should not write any shared variables! - Q_EMIT rescanNeed(); - - return; - } - - //This will notify SeverHandler - UiLog(serverName_).dbg() << " emit nodeChanged()"; - Q_EMIT nodeChanged(node,types); -} - - -void ServerComThread::update(const Defs* dc, const std::vector& types) -{ - std::vector typesCopy=types; - - UiLog(serverName_).dbg() << "ComThread::update --> defs"; - std::stringstream ss; - aspectToStr(ss,types); - UiLog(serverName_).dbg() << " aspects: " << ss.str(); - - //If anything was requested to be deleted in the thread we do not go further - //because it will trigger a full rescan in ServerHandler! - if(rescanNeed_) - { - UiLog(serverName_).dbg() << " rescanNeed already set"; - return; - } - - //This is a radical change - if(std::find(types.begin(),types.end(),ecf::Aspect::ORDER) != types.end()) - { - UiLog(serverName_).dbg() << " emit rescanNeed()"; - rescanNeed_=true; - - //We notify ServerHandler about the radical changes. When ServerHandler receives this signal - //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are - //reset update_delete() might be called, so it should not write any shared variables! - Q_EMIT rescanNeed(); - - return; - } - - //This will notify SeverHandler - UiLog(serverName_).dbg() << " emit defsChanged()"; - Q_EMIT defsChanged(typesCopy); -} - -void ServerComThread::update_delete(const Node* nc) -{ - Node *n=const_cast(nc); - n->detach(this); -} - -//This only be called when ComThread is running or from the ComThread desctructor. So it is safe to set -//rescanNeed in it. -void ServerComThread::update_delete(const Defs* dc) -{ - UiLog(serverName_).dbg() << "ServerComThread::update_delete -->"; - - Defs *d=const_cast(dc); - d->detach(this); - - //If we are in a SYNC_LOCAl task!!!! - if(taskType_ == VTask::SyncTask) - { - //We notify ServerHandler about the radical changes. When ServerHandler receives this signal - //it will clear its tree, which stores shared pointers to the nodes (node_ptr). If these pointers are - //reset update_delete() might be called, so it should not write any shared variables! - Q_EMIT rescanNeed(); - - rescanNeed_=true; - } -} - -//Attach each node and the defs to the observer -void ServerComThread::attach() -{ - ServerDefsAccess defsAccess(server_); // will reliquish its resources on destruction - defs_ptr d = defsAccess.defs(); - if(d == NULL) - return; - - attach(d); -} - -//Attach each node and the defs to the observer. The access to -//the defs is safe so we do not need to set a mutex on it. -void ServerComThread::attach(defs_ptr d) -{ - if(d == NULL) - return; - - d->attach(this); - - const std::vector &suites = d->suiteVec(); - for(unsigned int i=0; i < suites.size();i++) - { - attach(suites.at(i).get()); - } -} - -//Attach a node to the observer -void ServerComThread::attach(Node *node) -{ - std::vector nodes; - node->immediateChildren(nodes); - - node->attach(this); - - for(std::vector::const_iterator it=nodes.begin(); it != nodes.end(); ++it) - { - attach((*it).get()); - } -} - -//Detach each node and the defs from the observer -void ServerComThread::detach() -{ - ServerDefsAccess defsAccess(server_); // will reliquish its resources on destruction - defs_ptr d = defsAccess.defs(); - if(d == NULL) - return; - - detach(d); -} - - -//Detach each node and the defs from the observer. The access to -//the defs is safe so we do not need to set a mutex on it. -void ServerComThread::detach(defs_ptr d) -{ - if(d == NULL) - return; - - d->detach(this); - - const std::vector &suites = d->suiteVec(); - for(unsigned int i=0; i < suites.size();i++) - { - detach(suites.at(i).get()); - } -} - -//Detach a node from the observer -void ServerComThread::detach(Node *node) -{ - std::vector nodes; - node->immediateChildren(nodes); - - node->detach(this); - - for(std::vector::const_iterator it=nodes.begin(); it != nodes.end(); ++it) - { - detach((*it).get()); - } -} - -void ServerComThread::aspectToStr(std::stringstream& ss,const std::vector& t) const -{ - for(std::vector::const_iterator it=t.begin(); it != t.end(); ++it) - { - if(!ss.str().empty()) - ss << ","; - ss << *it; - } -} diff -Nru ecflow-4.9.0/Viewer/src/ServerComThread.hpp ecflow-4.11.1/Viewer/src/ServerComThread.hpp --- ecflow-4.9.0/Viewer/src/ServerComThread.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerComThread.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERCOMTHREAD_HPP_ -#define SERVERCOMTHREAD_HPP_ - -#include -#include -#include -#include -#include - -#include "Defs.hpp" -#include "AbstractObserver.hpp" - -#include "VTask.hpp" - -#include - -class ChangeMgrSingleton; -class ClientInvoker; -class ServerComQueue; -class ServerHandler; - -// ------------------------------------------------------- -// ServerComThread - a class to handler communication with -// an ecflow server. -// ------------------------------------------------------- - -class ServerComThread : public QThread, public AbstractObserver -{ - Q_OBJECT - -public: - ServerComThread(ServerHandler *server, ClientInvoker *ci); - ~ServerComThread(); - - void task(VTask_ptr); - - //From AbstractObserver - virtual void update_start(const Node*, const std::vector&) {} - virtual void update_start(const Defs*, const std::vector&) {} - void update(const Node*, const std::vector&); - void update(const Defs*, const std::vector&); - void update_delete(const Node*); - void update_delete(const Defs*); - -Q_SIGNALS: - void nodeChanged(const Node*, std::vector); - void defsChanged(std::vector); - void rescanNeed(); - void failed(std::string message); - void suiteListChanged(const std::vector&,const std::vector&); - -protected: - void run(); - void reset(); - void sync_local(); - -private: - void attach(); - void attach(defs_ptr d); - void attach(Node *node); - void detach(); - void detach(defs_ptr d); - void detach(Node *node); - void aspectToStr(std::stringstream& s,const std::vector& t) const; - - ServerHandler *server_; - std::string serverName_; - ClientInvoker *ci_; - VTask::Type taskType_; - std::vector command_; - std::map params_; - std::vector contents_; - NameValueVec vars_; - Zombie zombie_; - std::string nodePath_; - bool rescanNeed_; - bool hasSuiteFilter_; - std::vector filteredSuites_; - bool autoAddNewSuites_; - int maxLineNum_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerDefsAccess.cpp ecflow-4.11.1/Viewer/src/ServerDefsAccess.cpp --- ecflow-4.9.0/Viewer/src/ServerDefsAccess.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerDefsAccess.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerDefsAccess.hpp" - -#include "ServerHandler.hpp" - -ServerDefsAccess::ServerDefsAccess(ServerHandler *server) : - server_(server) -{ - server_->defsMutex_.lock(); // lock the resource on construction -} - - -ServerDefsAccess::~ServerDefsAccess() -{ - server_->defsMutex_.unlock(); // unlock the resource on destruction -} - -defs_ptr ServerDefsAccess::defs() -{ - return server_->defs(); // the resource will always be locked when we use it -} diff -Nru ecflow-4.9.0/Viewer/src/ServerDefsAccess.hpp ecflow-4.11.1/Viewer/src/ServerDefsAccess.hpp --- ecflow-4.9.0/Viewer/src/ServerDefsAccess.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerDefsAccess.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERDEFSACCESS_HPP_ -#define SERVERDEFSACCESS_HPP_ - -#include "Defs.hpp" - -class ServerHandler; - -// ------------------------------------------------------------------------- -// ServerDefsAccess - a class to manage access to the server definition tree -// - required for multi-threaded access -// ------------------------------------------------------------------------- - -class ServerDefsAccess -{ -public: - explicit ServerDefsAccess(ServerHandler *server); - ~ServerDefsAccess(); - - defs_ptr defs(); - -private: - ServerHandler *server_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerEditDialog.ui ecflow-4.11.1/Viewer/src/ServerEditDialog.ui --- ecflow-4.9.0/Viewer/src/ServerEditDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerEditDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ - - - ServerEditDialog - - - - 0 - 0 - 284 - 228 - - - - Edit server properties - - - true - - - - - - QFormLayout::ExpandingFieldsGrow - - - - - &Name: - - - label - - - - - - - - - - &Host: - - - hostEdit - - - - - - - - - - &Port: - - - portEdit - - - - - - - - - - &Favourite: - - - favCh - - - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - ServerEditDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox - rejected() - ServerEditDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ServerFilter.cpp ecflow-4.11.1/Viewer/src/ServerFilter.cpp --- ecflow-4.9.0/Viewer/src/ServerFilter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerFilter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,216 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerFilter.hpp" - -#include "ServerHandler.hpp" -#include "ServerItem.hpp" -#include "ServerList.hpp" -#include "VSettings.hpp" -#include "SessionHandler.hpp" - -//============================================== -// -// ServerFilter -// -//============================================== - -ServerFilter::ServerFilter() -{ -} - -ServerFilter::~ServerFilter() -{ - std::vector obsCopy=observers_; - for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) - { - (*it)->notifyServerFilterDelete(); - } - - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - (*it)->removeObserver(this); - } -} - -void ServerFilter::serverNames(std::vector& vec) const -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - vec.push_back((*it)->name()); - } -} - -void ServerFilter::addServer(ServerItem *item,bool broadcast) -{ - if(item && ServerList::instance()->find(item->name()) == item) - { - //ServerFilterItem* s=new ServerFilterItem(item->name(),item->host(),item->port()); - //ServerItem* s=new ServerFilterItem(item->name()); - - items_.push_back(item); - - item->addObserver(this); - - if(broadcast) - broadcastAdd(item); - } -} - -void ServerFilter::removeServer(ServerItem *server) -{ - if(!server) return; - - std::vector::iterator it=std::find(items_.begin(),items_.end(),server); - if(it != items_.end()) - { - //Remove the item from the filter. This should come - //first because the observers update themselves according to the - //contents of items_!!!! - items_.erase(it); - - //Notifies the view about the changes - broadcastRemove(server); - - //Remove the filter from the observers - server->removeObserver(this); - } -} - -void ServerFilter::notifyServerItemChanged(ServerItem *server) -{ - if(isFiltered(server)) - broadcastChange(server); -} - -//Do not remove the observer in this method!! -void ServerFilter::notifyServerItemDeletion(ServerItem *server) -{ - if(!server) return; - - std::vector::iterator it=std::find(items_.begin(),items_.end(),server); - if(it != items_.end()) - { - items_.erase(it); - - //Notifies the view about the changes - broadcastRemove(server); - } -} - -bool ServerFilter::isFiltered(ServerItem* item) const -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it) == item) - return true; - } - return false; -} - -bool ServerFilter::isFiltered(ServerHandler* server) const -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->serverHandler() == server) - return true; - } - return false; -} - -void ServerFilter::writeSettings(VSettings* vs) const -{ - std::vector array; - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - array.push_back((*it)->name()); - } - - vs->put("server",array); -} - -void ServerFilter::readSettings(VSettings* vs) -{ - items_.clear(); - - std::vector array; - vs->get("server",array); - - for(std::vector::const_iterator it = array.begin(); it != array.end(); ++it) - { - std::string name=*it; - if(ServerItem* s=ServerList::instance()->find(name)) - { - addServer(s,true); - } - // special case - if we're starting a temporary session for looking at one - // particular server, then we need to replace the placeholder server alias - // with the one we actually want to look at - else if (name == "SERVER_ALIAS_PLACEHOLDER") - { - SessionItem *session = SessionHandler::instance()->current(); - if (session->temporary()) - { - std::string alias = session->temporaryServerAlias(); - if (ServerItem* s=ServerList::instance()->find(alias)) - { - addServer(s,true); - } - } - } - } -} - -//=========================================================== -// Observers -//=========================================================== - -void ServerFilter::broadcastAdd(ServerItem *server) -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - { - (*it)->notifyServerFilterAdded(server); - } -} - -void ServerFilter::broadcastRemove(ServerItem *server) -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - { - (*it)->notifyServerFilterRemoved(server); - } -} - -void ServerFilter::broadcastChange(ServerItem *server) -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - { - (*it)->notifyServerFilterChanged(server); - } -} - -void ServerFilter::addObserver(ServerFilterObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - { - observers_.push_back(o); - } -} - -void ServerFilter::removeObserver(ServerFilterObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - { - observers_.erase(it); - } -} - - diff -Nru ecflow-4.9.0/Viewer/src/ServerFilter.hpp ecflow-4.11.1/Viewer/src/ServerFilter.hpp --- ecflow-4.9.0/Viewer/src/ServerFilter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerFilter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERFILTER_HPP_ -#define SERVERFILTER_HPP_ - -#include - -#include "Node.hpp" -#include "ServerItem.hpp" - -class VSettings; - -#include - -class ServerFilterObserver -{ -public: - virtual ~ServerFilterObserver() {} - virtual void notifyServerFilterAdded(ServerItem*)=0; - virtual void notifyServerFilterRemoved(ServerItem*)=0; - virtual void notifyServerFilterChanged(ServerItem*)=0; - virtual void notifyServerFilterDelete()=0; -}; - -class ServerFilter : public ServerItemObserver -{ -public: - ServerFilter(); - ~ServerFilter(); - - enum ChangeAspect {Reset,Added,Removed}; - - const std::vector& items() const {return items_;} - int itemCount() const {return static_cast(items_.size());} - void serverNames(std::vector&) const; - - void addServer(ServerItem*,bool broadcast=true); - void removeServer(ServerItem*); - bool isFiltered(ServerItem*) const; - bool isFiltered(ServerHandler*) const; - - void writeSettings(VSettings*) const; - void readSettings(VSettings*); - - void addObserver(ServerFilterObserver*); - void removeObserver(ServerFilterObserver*); - - //From ServerItemObserver - void notifyServerItemChanged(ServerItem*); - void notifyServerItemDeletion(ServerItem*); - -protected: - void broadcastAdd(ServerItem*); - void broadcastRemove(ServerItem*); - void broadcastChange(ServerItem*); - -private: - std::vector items_; - std::vector observers_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerHandler.cpp ecflow-4.11.1/Viewer/src/ServerHandler.cpp --- ecflow-4.9.0/Viewer/src/ServerHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1576 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerHandler.hpp" - -#include "Defs.hpp" -#include "ClientInvoker.hpp" -#include "File.hpp" -#include "NodeFwd.hpp" -#include "ArgvCreator.hpp" -#include "Str.hpp" - -#include "ChangeNotify.hpp" -#include "ConnectState.hpp" -#include "DirectoryHandler.hpp" -#include "MainWindow.hpp" -#include "NodeObserver.hpp" -#include "SessionHandler.hpp" -#include "ServerComQueue.hpp" -#include "ServerDefsAccess.hpp" -#include "ServerObserver.hpp" -#include "ServerComObserver.hpp" -#include "ShellCommand.hpp" -#include "SuiteFilter.hpp" -#include "UiLog.hpp" -#include "UIDebug.hpp" -#include "UpdateTimer.hpp" -#include "UserMessage.hpp" -#include "VNode.hpp" -#include "VSettings.hpp" -#include "VTaskObserver.hpp" - -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -std::vector ServerHandler::servers_; -std::string ServerHandler::localHostName_; - -//#define __UI_SERVEROBSERVER_DEBUG -//#define __UI_SERVERCOMOBSERVER_DEBUG -#define __UI_SERVERUPDATE_DEBUG - -ServerHandler::ServerHandler(const std::string& name,const std::string& host, const std::string& port) : - name_(name), - host_(host), - port_(port), - client_(0), - updating_(false), - communicating_(false), - suiteFilter_(new SuiteFilter()), - comQueue_(0), - activity_(NoActivity), - connectState_(new ConnectState()), - prevServerState_(SState::RUNNING), - conf_(0) -{ - if(localHostName_.empty()) - { - localHostName_=boost::asio::ip::host_name(); - } - - //Create longname - longName_=host_ + "@" + port_; - - conf_=new VServerSettings(this); - - //Create the client invoker. At this point it is empty. - try - { - client_=new ClientInvoker(host,port); - } - catch(std::exception& e) - { - UiLog().err() << "Could not create ClientInvoker for host=" << host << - " port= " << port << ". " << e.what(); - client_=0; - } - - client_->set_retry_connection_period(1); - client_->set_throw_on_error(true); - - //Create the vnode root. This will represent the node tree in the viewer, but - //at this point it is empty. - vRoot_=new VServer(this); - - //Connect up the timer for refreshing the server info. The timer has not - //started yet. - - refreshTimer_=new UpdateTimer(this); - connect(refreshTimer_, SIGNAL(timeout()), - this, SLOT(refreshServerInfo())); - - - //We will need to pass various non-Qt types via signals and slots for error messages. - //So we need to register these types. - if(servers_.empty()) - { - qRegisterMetaType("std::string"); - qRegisterMetaType >("QList"); - qRegisterMetaType >("std::vector"); - } - - //Add this instance to the servers_ list. - servers_.push_back(this); - - //NOTE: we may not always want to create a thread here because of resource - // issues; another strategy would be to create threads on demand, only - // when server communication is about to start. - - //Create the queue for the tasks to be sent to the client (via the ServerComThread)! It will - //create and take ownership of the ServerComThread. At this point the queue has not started yet. - comQueue_=new ServerComQueue (this,client_); - - //Load settings - loadConf(); - - //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - // At this point nothing is running or active!!!! - - //Indicate that we start an init (initial load) - //activity_=LoadActivity; - - //Try to connect to the server and load the defs etc. This might fail! - reset(); -} - -ServerHandler::~ServerHandler() -{ - //Save setings - saveConf(); - - //Notify the observers - broadcast(&ServerObserver::notifyServerDelete); - - //The queue must be deleted before the client, since the thread might - //be running a job on the client!! - if (comQueue_) - delete comQueue_; - - //Remove itself from the server vector - std::vector::iterator it=std::find(servers_.begin(),servers_.end(),this); - if(it != servers_.end()) - servers_.erase(it); - - delete vRoot_; - delete connectState_; - delete suiteFilter_; - - //The safest is to delete the client in the end - if(client_) - delete client_; - - delete conf_; -} - -//----------------------------------------------- -// Refresh/update -//----------------------------------------------- - -bool ServerHandler::updateInfo(int& basePeriod,int& currentPeriod,int &drift,int& toNext) -{ - if(!refreshTimer_->isActive()) - return false; - - toNext=secsTillNextRefresh(); - basePeriod=conf_->intValue(VServerSettings::UpdateRate); - currentPeriod=refreshTimer_->interval()/1000; - drift=-1; - if(conf_->boolValue(VServerSettings::AdaptiveUpdate)) - { - drift=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement); - } - - return true; -} - -int ServerHandler::secsSinceLastRefresh() const -{ - return static_cast(lastRefresh_.secsTo(QDateTime::currentDateTime())); -} - -int ServerHandler::secsTillNextRefresh() const -{ - if(refreshTimer_->isActive()) -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - return refreshTimer_->remainingTime()/1000; -#else - return 0; -#endif - return -1; -} - -void ServerHandler::stopRefreshTimer() -{ - refreshTimer_->stop(); -#ifdef __UI_SERVERUPDATE_DEBUG - UiLog(this).dbg() << "ServerHandler::stopRefreshTimer -->"; -#endif - broadcast(&ServerComObserver::notifyRefreshTimerStopped); -} - -void ServerHandler::startRefreshTimer() -{ - UiLog(this).dbg() << "ServerHandler::startRefreshTimer -->"; - - if(!conf_->boolValue(VServerSettings::AutoUpdate)) - { - return; - } - - //If we are not connected to the server the - //timer should not run. - if(connectState_->state() == ConnectState::Disconnected) - return; - - if(!refreshTimer_->isActive()) - { - int rate=conf_->intValue(VServerSettings::UpdateRate); - if(rate <=0) rate=1; - refreshTimer_->setInterval(rate*1000); - refreshTimer_->start(); - broadcast(&ServerComObserver::notifyRefreshTimerStarted); - } - -#ifdef __UI_SERVERUPDATE_DEBUG - UiLog(this).dbg() << " refreshTimer interval: " << refreshTimer_->interval(); -#endif -} - -void ServerHandler::updateRefreshTimer() -{ - UiLog(this).dbg() << "ServerHandler::updateRefreshTimer -->"; - - if(!conf_->boolValue(VServerSettings::AutoUpdate)) - { - stopRefreshTimer(); - return; - } - - //If we are not connected to the server the - //timer should not run. - if(connectState_->state() == ConnectState::Disconnected) - return; - - int rate=conf_->intValue(VServerSettings::UpdateRate); - if(rate <=0) rate=1; - - if(refreshTimer_->isActive()) - { - refreshTimer_->stop(); - } - - refreshTimer_->setInterval(rate*1000); - refreshTimer_->start(); - broadcast(&ServerComObserver::notifyRefreshTimerChanged); - -#ifdef __UI_SERVERUPDATE_DEBUG - UiLog(this).dbg() << " refreshTimer interval: " << refreshTimer_->interval(); -#endif - -} - -void ServerHandler::driftRefreshTimer() -{ - if(!conf_->boolValue(VServerSettings::AutoUpdate)) - { - return; - } - - //We increase the update frequency - if(activity_ != LoadActivity && - conf_->boolValue(VServerSettings::AdaptiveUpdate)) - { -#ifdef __UI_SERVERUPDATE_DEBUG - UiLog(this).dbg() << "driftRefreshTimer -->"; -#endif - - int rate=conf_->intValue(VServerSettings::UpdateRate); - int baseDelta=conf_->intValue(VServerSettings::AdaptiveUpdateIncrement); - int delta=baseDelta; //linear mode - - //doubling mode - if(conf_->stringValue(VServerSettings::AdaptiveUpdateMode) == "doubling") - { - delta=refreshTimer_->interval()/1000-rate; - if(delta==0) - delta=baseDelta; - } - //x modes - else - { - float f=conf_->stringValue(VServerSettings::AdaptiveUpdateMode).toFloat(); - if(f >= 1. && f <=5.) - { - delta=(refreshTimer_->interval()/1000-rate)*(f-1); - if(delta==0) - delta=1; - } - } - - refreshTimer_->drift(delta,conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate)); - - broadcast(&ServerComObserver::notifyRefreshTimerChanged); - } - -#ifdef __UI_SERVERUPDATE_DEBUG - UiLog(this).dbg() << "driftRefreshTimer interval: " << refreshTimer_->interval(); -#endif - -} - -//returns true if the current total (drifted) period is within the maximum allowed -bool ServerHandler::checkRefreshTimerDrift() const -{ - if(!conf_->boolValue(VServerSettings::AutoUpdate)) - { - return true; - } - - if(activity_ != LoadActivity && - conf_->boolValue(VServerSettings::AdaptiveUpdate)) - { - return (refreshTimer_->interval()*1000 < - conf_->intValue(VServerSettings::MaxAdaptiveUpdateRate)*60); - } - return true; -} - -//mark that a refresh request was sent to the queue -void ServerHandler::refreshScheduled() -{ - lastRefresh_=QDateTime::currentDateTime(); - broadcast(&ServerComObserver::notifyRefreshScheduled); -} - -//mark that a refresh request was sent to the queue -void ServerHandler::refreshFinished() -{ - broadcast(&ServerComObserver::notifyRefreshFinished); -} - -void ServerHandler::setActivity(Activity ac) -{ - activity_=ac; - broadcast(&ServerObserver::notifyServerActivityChanged); -} - -ServerHandler* ServerHandler::addServer(const std::string& name,const std::string& host, const std::string& port) -{ - ServerHandler* sh=new ServerHandler(name,host,port); - //Without the clinetinvoker we cannot use the serverhandler - if(!sh->client_) - { - delete sh; - sh=0; - } - return sh; -} - -void ServerHandler::removeServer(ServerHandler* server) -{ - std::vector::iterator it=std::find(servers_.begin(), servers_.end(),server); - if(it != servers_.end()) - { - ServerHandler *s=*it; - servers_.erase(it); - delete s; - } -} - -ServerHandler* ServerHandler::findServer(const std::string &alias) -{ - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - { - ServerHandler *s=*it; - - if (s->name() == alias) - { - return s; - } - } - return NULL; // did not find it -} - - - -//This function can be called many times so we need to avoid locking the mutex. -SState::State ServerHandler::serverState() -{ - if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity) - { - prevServerState_= SState::RUNNING; - - } - //If we are here we can be sure that the defs pointer is not being deleted!! We - //access it without locking the mutex!!! - else - { - //If get the defs can be 100% sure that it is not being deleted!! So we can - //access it without locking the mutex!!! - defs_ptr d=safelyAccessSimpleDefsMembers(); - if(d && d.get()) - { - prevServerState_= d->set_server().get_state(); - return prevServerState_; - } - } - - return prevServerState_; -} - -//This function can be called many times so we need to avoid locking the mutex. -NState::State ServerHandler::state(bool& suspended) -{ - if(connectState_->state() != ConnectState::Normal || activity() == LoadActivity) - return NState::UNKNOWN; - - suspended=false; - - defs_ptr d=safelyAccessSimpleDefsMembers(); - if(d && d.get()) - { - suspended=d->isSuspended(); - return d->state(); - } - - return NState::UNKNOWN; -} - -defs_ptr ServerHandler::defs() -{ - defs_ptr null; - - if(client_) - { - return client_->defs(); - } - else - { - return null; - } -} - -defs_ptr ServerHandler::safelyAccessSimpleDefsMembers() -{ - defs_ptr null; - - //The defs might be deleted during reset so it cannot be accessed. - if(activity_ == LoadActivity) - { - return null; - } - //Otherwise it is safe to access certain non-vector members - else if(client_) - { - return client_->defs(); - } - else - { - return null; - } -} - -//------------------------------------------------------------- -// Run client tasks. -// -// The preferred way to run client tasks is to define and add a task to the queue. The -// queue will manage the task and will send it to the ClientInvoker. When the task -// finishes the ServerHandler::clientTaskFinished method is called where the -// result/reply can be processed. -//-------------------------------------------------------------- - -//It is protected! Practically it means we -//we can only run it through CommandHandler!!! -void ServerHandler::runCommand(const std::vector& cmd) -{ - //Shell command - we should not reach this point - if(cmd.size() > 0 && cmd[0]=="sh") - { - UI_ASSERT(0,"cmd=" << cmd[0]); - return; - } - - //ecflow_client command - if(connectState_->state() == ConnectState::Disconnected) - return; - - VTask_ptr task=VTask::create(VTask::CommandTask); - task->command(cmd); - comQueue_->addTask(task); -} - -void ServerHandler::run(VTask_ptr task) -{ - if(connectState_->state() == ConnectState::Disconnected) - return; - - switch(task->type()) - { - case VTask::ScriptTask: - return script(task); - break; - case VTask::JobTask: - return job(task); - break; - case VTask::OutputTask: - return jobout(task); - break; - case VTask::ManualTask: - return manual(task); - break; - case VTask::HistoryTask: - case VTask::MessageTask: - case VTask::StatsTask: - case VTask::ScriptPreprocTask: - case VTask::ScriptEditTask: - case VTask::ScriptSubmitTask: - case VTask::SuiteListTask: - case VTask::ZombieListTask: - case VTask::ZombieCommandTask: - comQueue_->addTask(task); - break; - default: - //If we are here we have an unhandled task type. - task->status(VTask::REJECTED); - break; - } -} - -void ServerHandler::script(VTask_ptr task) -{ - /*static std::string errText="no script!\n" - "check ECF_FILES or ECF_HOME directories, for read access\n" - "check for file presence and read access below files directory\n" - "or this may be a 'dummy' task.\n";*/ - - task->param("clientPar","script"); - comQueue_->addTask(task); -} - -void ServerHandler::job(VTask_ptr task) -{ - /*static std::string errText="no script!\n" - "check ECF_FILES or ECF_HOME directories, for read access\n" - "check for file presence and read access below files directory\n" - "or this may be a 'dummy' task.\n";*/ - - task->param("clientPar","job"); - comQueue_->addTask(task); -} - -void ServerHandler::jobout(VTask_ptr task) -{ - //static std::string errText="no job output..."; - - task->param("clientPar","jobout"); - comQueue_->addTask(task); -} - -void ServerHandler::manual(VTask_ptr task) -{ - //std::string errText="no manual ..."; - task->param("clientPar","manual"); - comQueue_->addTask(task); -} - -//The core refresh function. That should be the only one directly accessing the queue. -void ServerHandler::refreshInternal() -{ - //We add and refresh task to the queue. On startup this function can be called - //before the comQueue_ was created so we need to check if it exists. - if(comQueue_) - { - comQueue_->addNewsTask(); - } -} - -//The user initiated a refresh - using the toolbar/menu buttons or -//called after a user command was issued -void ServerHandler::refresh() -{ - refreshInternal(); - - //Reset the timer to its original value (i.e. remove the drift) - updateRefreshTimer(); -} - - -//This slot is called by the timer regularly to get news from the server. -void ServerHandler::refreshServerInfo() -{ - UiLog(this).dbg() << "Auto refreshing server"; - refreshInternal(); - - //We reduce the update frequency - driftRefreshTimer(); -} - -//====================================================================================== -// Manages node changes. -//====================================================================================== - -//This slot is called when a node changes. -void ServerHandler::slotNodeChanged(const Node* nc,std::vector aspect) -{ - UiLog(this).dbg() << "ServerHandler::slotNodeChanged - node: " + nc->name(); - - //for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - //UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast(*it)); - - //This can happen if we initiated a reset while we sync in the thread - if(vRoot_->isEmpty()) - { - UiLog(this).dbg() << " tree is empty - no change applied!"; - return; - } - - VNode* vn=vRoot_->toVNode(nc); - - //We must have this VNode - assert(vn != NULL); - - //Begin update for the VNode - VNodeChange change; - vRoot_->beginUpdate(vn,aspect,change); - - //TODO: what about the infopanel or breadcrumbs?????? - if(change.ignore_) - { - UiLog(this).dbg() << " update ignored"; - return; - } - else - { - //Notify the observers - broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change); - - //End update for the VNode - vRoot_->endUpdate(vn,aspect,change); - - //Notify the observers - broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change); - - UiLog(this).dbg() << " update applied"; - } -} - -void ServerHandler::addNodeObserver(NodeObserver *obs) -{ - std::vector::iterator it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs); - if(it == nodeObservers_.end()) - { - nodeObservers_.push_back(obs); - } -} - -void ServerHandler::removeNodeObserver(NodeObserver *obs) -{ - std::vector::iterator it=std::find(nodeObservers_.begin(),nodeObservers_.end(),obs); - if(it != nodeObservers_.end()) - { - nodeObservers_.erase(it); - } -} - -void ServerHandler::broadcast(NoMethod proc,const VNode* node) -{ - for(std::vector::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it) - ((*it)->*proc)(node); -} - -void ServerHandler::broadcast(NoMethodV1 proc,const VNode* node,const std::vector& aspect,const VNodeChange& change) -{ - //When the observers are being notified (in a loop) they might - //want to remove themselves from the observer list. This will cause a crash. To avoid - //this we create a copy of the observers and use it in the notification loop. - std::vector nObsCopy=nodeObservers_; - - for(std::vector::const_iterator it=nObsCopy.begin(); it != nObsCopy.end(); ++it) - { - ((*it)->*proc)(node,aspect,change); - } - -#if 0 - for(std::vector::const_iterator it=nodeObservers_.begin(); it != nodeObservers_.end(); ++it) - ((*it)->*proc)(node,aspect,change); -#endif -} - -//--------------------------------------------------------------------------- -// Manages Defs changes and desf observers. Defs observers are notified when -// there is a change. -//--------------------------------------------------------------------------- - -//This slot is called when the Defs change. -void ServerHandler::slotDefsChanged(std::vector aspect) -{ - UiLog().dbg() << "ServerHandler::slotDefsChanged -->"; - //for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - // UserMessage::message(UserMessage::DBG, false, std::string(" aspect: ") + boost::lexical_cast(*it)); - - //Begin update for the VNode - //VNodeChange change; - vRoot_->beginUpdate(aspect); - - //Notify the observers - //broadcast(&NodeObserver::notifyBeginNodeChange,vn,aspect,change); - - //End update for the VNode - //vRoot_->endUpdate(vn,aspect,change); - - //Notify the observers - //broadcast(&NodeObserver::notifyEndNodeChange,vn,aspect,change); - - //UserMessage::message(UserMessage::DBG, false," --> Update applied"); - - for(std::vector::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it) - (*it)->notifyDefsChanged(this,aspect); -} - -void ServerHandler::addServerObserver(ServerObserver *obs) -{ - std::vector::iterator it=std::find(serverObservers_.begin(),serverObservers_.end(),obs); - if(it == serverObservers_.end()) - { - serverObservers_.push_back(obs); -#ifdef __UI_SERVEROBSERVER_DEBUG - UiLog(this).dbg() << "ServerHandler::addServerObserver --> " << obs; -#endif - } -} - -void ServerHandler::removeServerObserver(ServerObserver *obs) -{ -#ifdef __UI_SERVEROBSERVER_DEBUG - UI_FUNCTION_LOG_S(this) -#endif - std::vector::iterator it=std::find(serverObservers_.begin(),serverObservers_.end(),obs); - if(it != serverObservers_.end()) - { - serverObservers_.erase(it); -#ifdef __UI_SERVEROBSERVER_DEBUG - UiLog(this).dbg() << " remove: " << obs; -#endif - } -} - -void ServerHandler::broadcast(SoMethod proc) -{ -#ifdef __UI_SERVEROBSERVER_DEBUG - UI_FUNCTION_LOG_S(this) -#endif - - bool checkExistence=true; - - //When the observers are being notified (in a loop) they might - //want to remove themselves from the observer list. This will cause a crash. To avoid - //this we create a copy of the observers and use it in the notification loop. - std::vector sObsCopy=serverObservers_; - - for(std::vector::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it) - { - //We need to check if the given observer is still in the original list. When we delete the server, due to - //dependencies it is possible that the observer is already deleted at this point. - if(!checkExistence || std::find(serverObservers_.begin(),serverObservers_.end(),*it) != serverObservers_.end()) - { - ((*it)->*proc)(this); - } - } -} - - -void ServerHandler::broadcast(SoMethodV1 proc,const VServerChange& ch) -{ - for(std::vector::const_iterator it=serverObservers_.begin(); it != serverObservers_.end(); ++it) - ((*it)->*proc)(this,ch); -} - -//------------------------------------------------ -// ServerComObserver -//------------------------------------------------ - -void ServerHandler::addServerComObserver(ServerComObserver *obs) -{ - std::vector::iterator it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs); - if(it == serverComObservers_.end()) - { - serverComObservers_.push_back(obs); -#ifdef __UI_SERVERCOMOBSERVER_DEBUG - UiLog(this).dbg() << "ServerHandler::addServerComObserver --> " << obs; -#endif - } -} - -void ServerHandler::removeServerComObserver(ServerComObserver *obs) -{ - std::vector::iterator it=std::find(serverComObservers_.begin(),serverComObservers_.end(),obs); - if(it != serverComObservers_.end()) - { - serverComObservers_.erase(it); - #ifdef __UI_SERVERCOMOBSERVER_DEBUG - UiLog(this).dbg() << "ServerHandler::removeServerComObserver --> " << obs; - #endif - } -} - -void ServerHandler::broadcast(SocMethod proc) -{ - bool checkExistence=true; - - //When the observers are being notified (in a loop) they might - //want to remove themselves from the observer list. This will cause a crash. To avoid - //this we create a copy of the observers and use it in the notification loop. - std::vector sObsCopy=serverComObservers_; - - for(std::vector::const_iterator it=sObsCopy.begin(); it != sObsCopy.end(); ++it) - { - //We need to check if the given observer is still in the original list. When we delete the server, due to - //dependencies it is possible that the observer is already deleted at this point. - if(!checkExistence || std::find(serverComObservers_.begin(),serverComObservers_.end(),*it) != serverComObservers_.end()) - ((*it)->*proc)(this); - } -} - - -//------------------------------------------------------------------- -// This slot is called when the comThread finished the given task!! -//------------------------------------------------------------------- - -//There was a drastic change during the SYNC! As a safety measure we need to clear -//the tree. We will rebuild it when the SYNC finishes. -void ServerHandler::slotRescanNeed() -{ - clearTree(); -} - -void ServerHandler::clientTaskFinished(VTask_ptr task,const ServerReply& serverReply) -{ - UiLog(this).dbg() << "ServerHandler::clientTaskFinished -->"; - - //The status of the tasks sent from the info panel must be properly set to - //FINISHED!! Only that will notify the observers about the change!! - - //See which type of task finished. What we do now will depend on that. - switch(task->type()) - { - case VTask::CommandTask: - { - // a command was sent - we should now check whether there have been - // any updates on the server (there should have been, because we - // just did something!) - - UiLog(this).dbg() << " command finished - send SYNC command"; - //comQueue_->addNewsTask(); - comQueue_->addSyncTask(); - updateRefreshTimer(); - break; - } - case VTask::NewsTask: - { - // we've just asked the server if anything has changed - has it? - - refreshFinished(); - - switch (serverReply.get_news()) - { - case ServerReply::NO_NEWS: - { - // no news, nothing to do - UiLog(this).dbg() << " NO_NEWS"; - - //If we just regained the connection we need to reset - if(connectionGained()) - { - reset(); - } - break; - } - - case ServerReply::NEWS: - { - // yes, something's changed - synchronise with the server - - //If we just regained the connection we need to reset - UiLog(this).dbg() << " NEWS - send SYNC command"; - if(connectionGained()) - { - reset(); - } - else - { - comQueue_->addSyncTask(); - } - break; - } - - case ServerReply::DO_FULL_SYNC: - { - // yes, a lot of things have changed - we need to reset!!!!!! - UiLog(this).dbg() << " DO_FULL_SYNC - will reset"; - connectionGained(); - reset(); - break; - } - - default: - { - break; - } - } - break; - } - case VTask::SyncTask: - { - UiLog(this).dbg() << " sync finished"; - - //This typically happens when a suite is added/removed - if(serverReply.full_sync() || vRoot_->isEmpty()) - { - UiLog(this).dbg() << " full sync requested - rescanTree"; - - //This will update the suites + restart the timer - rescanTree(); - -#if 0 - { - ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction - defs_ptr defs = defsAccess.defs(); - if(defs) - { - std::cout << defs; - } - } -#endif - - } - else - { - broadcast(&ServerObserver::notifyEndServerSync); - } - break; - } - - case VTask::ResetTask: - { - //If not yet connected but the sync task was successful - resetFinished(); - break; - } - - case VTask::ScriptTask: - case VTask::ManualTask: - case VTask::HistoryTask: - case VTask::JobTask: - { - task->reply()->fileReadMode(VReply::ServerReadMode); - task->reply()->setText(serverReply.get_string()); - task->reply()->setReadTruncatedTo(truncatedLinesFromServer(task->reply()->text())); - task->status(VTask::FINISHED); - break; - } - - case VTask::OutputTask: - { - task->reply()->fileReadMode(VReply::ServerReadMode); - - std::string err; - VFile_ptr f(VFile::create(false)); - f->setFetchMode(VFile::ServerFetchMode); - f->setFetchModeStr("fetched from server " + name()); - f->setSourcePath(task->reply()->fileName()); - f->setTruncatedTo(truncatedLinesFromServer(serverReply.get_string())); - f->write(serverReply.get_string(),err); - task->reply()->tmpFile(f); - - task->status(VTask::FINISHED); - break; - } - - case VTask::MessageTask: - { - task->reply()->setTextVec(serverReply.get_string_vec()); - task->status(VTask::FINISHED); - break; - } - - case VTask::StatsTask: - { - std::stringstream ss; - serverReply.stats().show(ss); - task->reply()->text(ss.str()); - task->status(VTask::FINISHED); - break; - } - - case VTask::ScriptPreprocTask: - case VTask::ScriptEditTask: - { - task->reply()->text(serverReply.get_string()); - task->status(VTask::FINISHED); - break; - } - case VTask::ScriptSubmitTask: - { - UiLog(this).dbg() << " script submit finished - send NEWS command"; - - task->reply()->text(serverReply.get_string()); - task->status(VTask::FINISHED); - - //Submitting the task was successful - we should now get updates from the server - refresh(); - break; - } - - case VTask::SuiteListTask: - { - //Update the suite filter with the list of suites actually loaded onto the server. - //If the suitefilter is enabled this might have only a subset of it in our tree - updateSuiteFilterWithLoaded(serverReply.get_string_vec()); - task->status(VTask::FINISHED); - break; - } - - case VTask::ZombieListTask: - { - task->reply()->zombies(serverReply.zombies()); - task->status(VTask::FINISHED); - break; - } - - default: - task->status(VTask::FINISHED); - break; - - } -} - -//------------------------------------------------------------------- -// This slot is called when the comThread failed the given task!! -//------------------------------------------------------------------- - -void ServerHandler::clientTaskFailed(VTask_ptr task,const std::string& errMsg) -{ - UiLog(this).dbg() << "ServerHandler::clientTaskFailed -->"; - - //TODO: suite filter + ci_ observers - - //See which type of task finished. What we do now will depend on that. - switch(task->type()) - { - case VTask::SyncTask: - connectionLost(errMsg); - break; - - //The initialisation failed - case VTask::ResetTask: - { - resetFailed(errMsg); - break; - } - case VTask::NewsTask: - { - connectionLost(errMsg); - refreshFinished(); - break; - } - case VTask::StatsTask: - { - connectionLost(errMsg); - break; - } - case VTask::CommandTask: - { - task->reply()->setErrorText(errMsg); - task->status(VTask::ABORTED); - UserMessage::message(UserMessage::WARN, true, errMsg); - break; - } - default: - task->reply()->setErrorText(errMsg); - task->status(VTask::ABORTED); - break; - } -} - -void ServerHandler::connectionLost(const std::string& errMsg) -{ - connectState_->state(ConnectState::Lost); - connectState_->errorMessage(errMsg); - broadcast(&ServerObserver::notifyServerConnectState); -} - -bool ServerHandler::connectionGained() -{ - if(connectState_->state() != ConnectState::Normal) - { - connectState_->state(ConnectState::Normal); - broadcast(&ServerObserver::notifyServerConnectState); - return true; - } - return false; - -} - -void ServerHandler::disconnectServer() -{ - if(connectState_->state() != ConnectState::Disconnected) - { - connectState_->state(ConnectState::Disconnected); - broadcast(&ServerObserver::notifyServerConnectState); - - //Stop the queue - comQueue_->disable(); - - //Stop the timer - stopRefreshTimer(); - } -} - -void ServerHandler::connectServer() -{ - if(connectState_->state() == ConnectState::Disconnected) - { - //Start the queue - comQueue_->enable(); - - //Start the timer - startRefreshTimer(); - - //Try to get the news - refreshInternal(); - - //TODO: attach the observers!!!! - } -} - -void ServerHandler::reset() -{ - UiLog(this).dbg() << "ServerHandler::reset -->"; - - //--------------------------------- - // First part of reset: clearing - //--------------------------------- - - if(comQueue_->prepareReset()) - { - //Stop the timer - stopRefreshTimer(); - - // First part of reset: clear the tree - clearTree(); - - // Second part of reset: loading - - //Indicate that we reload the defs - setActivity(LoadActivity); - - //mark the current moment as last refresh - lastRefresh_=QDateTime::currentDateTime(); - - //NOTE: at this point the queue is not running but reset() will start it. - //While the queue is in reset mode it does not accept tasks. - comQueue_->reset(); - } - else - { - UiLog(this).dbg() << " skip reset"; - } -} - -//The reset was successful -void ServerHandler::resetFinished() -{ - setActivity(NoActivity); - - //Set server host and port in defs. It is used to find the server of - //a given node in the viewer. - { - ServerDefsAccess defsAccess(this); // will reliquish its resources on destruction - - defs_ptr defs = defsAccess.defs(); - if(defs != NULL) - { - ServerState& st=defs->set_server(); - st.hostPort(std::make_pair(host_,port_)); - } - } - - //Create an object to inform the observers about the change - VServerChange change; - - //Begin the full scan to get the tree. This call does not actually - //run the scan but counts how many suits will be available. - vRoot_->beginScan(change); - - //Notify the observers that the scan has started - broadcast(&ServerObserver::notifyBeginServerScan,change); - - //Finish full scan - vRoot_->endScan(); - - //assert(change.suiteNum_ == vRoot_->numOfChildren()); - - //Notify the observers that scan has ended - broadcast(&ServerObserver::notifyEndServerScan); - - //The suites might have been changed - updateSuiteFilter(); - - //Restart the timer - startRefreshTimer(); - - //Set the connection state - if(connectState_->state() != ConnectState::Normal) - { - connectState_->state(ConnectState::Normal); - broadcast(&ServerObserver::notifyServerConnectState); - } -} - -//The reset failed and we could not connect to the server, e.g. because the the server -//may be down, or there is a network error, or the authorisation is missing. -void ServerHandler::resetFailed(const std::string& errMsg) -{ - //This status is indicated by the connectStat_. Each object needs to be aware of it - //and do its tasks accordingly. - - //Create an object to inform the observers about the change - VServerChange change; - //Notify the observers that the scan has started - broadcast(&ServerObserver::notifyBeginServerScan,change); - //Notify the observers that scan has ended - broadcast(&ServerObserver::notifyEndServerScan); - - connectState_->state(ConnectState::Lost); - connectState_->errorMessage(errMsg); - setActivity(NoActivity); - - broadcast(&ServerObserver::notifyServerConnectState); - - //Restart the timer - startRefreshTimer(); -} - -//This function must be called during a SYNC!!!!!!!! - -void ServerHandler::clearTree() -{ - UiLog(this).dbg() << "ServerHandler::clearTree -->"; - - if(!vRoot_->isEmpty()) - { - //Notify observers that the clear is about to begin - broadcast(&ServerObserver::notifyBeginServerClear); - - //Clear vnode - vRoot_->clear(); - - //Notify observers that the clear ended - broadcast(&ServerObserver::notifyEndServerClear); - } - - UiLog(this).dbg() << " <-- clearTree"; -} - - -void ServerHandler::rescanTree() -{ - UiLog(this).dbg() << "ServerHandler::rescanTree -->"; - - setActivity(RescanActivity); - - //--------------------------------- - // First part of rescan: clearing - //--------------------------------- - - //Stop the timer - stopRefreshTimer(); - - //Stop the queue as a safety measure: we do not want any changes during the rescan - comQueue_->suspend(false); - - //clear the tree - clearTree(); - - //At this point nothing is running and the tree is empty (it only contains - //the root node) - - //-------------------------------------- - // Second part of rescan: loading - //-------------------------------------- - - //Create an object to inform the observers about the change - VServerChange change; - - //Begin the full scan to get the tree. This call does not actually - //run the scan but counts how many suits will be available. - vRoot_->beginScan(change); - - //Notify the observers that the scan has started - broadcast(&ServerObserver::notifyBeginServerScan,change); - - //Finish full scan - vRoot_->endScan(); - - //Notify the observers that scan has ended - broadcast(&ServerObserver::notifyEndServerScan); - - //Restart the queue - comQueue_->start(); - - //The suites might have been changed - updateSuiteFilter(); - - //Start the timer - startRefreshTimer(); - - setActivity(NoActivity); - - UiLog(this).dbg() << "<-- rescanTree"; -} - -//==================================================== -// Suite filter -//==================================================== - -void ServerHandler::setSuiteFilterWithOne(VNode* n) -{ - if(n) - { - if(VNode* sn=n->suite()) - if(VSuiteNode *suite=sn->isSuite()) - { - if(suiteFilter_->isEnabled() == false) - { - suiteFilter_->setEnabled(true); - suiteFilter_->selectOnlyOne(suite->strName()); - //after reset the loaded suites need to be read again from the server! - suiteFilter_->setLoadedInitialised(false); - reset(); - } - else if(suiteFilter_->isOnlyOneFiltered(suite->strName()) == false) - { - suiteFilter_->selectOnlyOne(suite->strName()); - //after reset the loaded suites need to be read again from the server! - suiteFilter_->setLoadedInitialised(false); - reset(); - } - } - } -} - -void ServerHandler::updateSuiteFilter(SuiteFilter* sf) -{ - if(suiteFilter_->update(sf)) - { - //If only this flag has changed we exec a custom task for it - if(suiteFilter_->changeFlags().sameAs(SuiteFilter::AutoAddChanged)) - { - comQueue_->addSuiteAutoRegisterTask(); - } - //Otherwise we need a full reset - else - { - reset(); - } - - //Save the settings - conf_->saveSettings(); - } -} - -//Update the suite filter with the list of suites actually loaded onto the server. -//If the suitefilter is enabled this might have only a subset of it in our tree. -void ServerHandler::updateSuiteFilterWithLoaded(const std::vector& loadedSuites) -{ - suiteFilter_->setLoaded(loadedSuites); -} - -//Update the suite filter with the list of suites stored in the defs (in the tree). It only -//makes sense if the filter is disabled since in this case the defs stores all the loaded servers. -void ServerHandler::updateSuiteFilterWithDefs() -{ - if(suiteFilter_->isEnabled()) - return; - - std::vector defSuites; - vRoot_->suites(defSuites); - suiteFilter_->setLoaded(defSuites); -} - -//Only called internally after reset or serverscan!! -void ServerHandler::updateSuiteFilter() -{ - //We only fetch the full list of loaded suites from the server - //via the thread when - // -the suiteFilter is not yet initialised - // OR - // -the suiteFilter is observerved and it is enabled! - if(!suiteFilter_->isLoadedInitialised() || - (suiteFilter_->hasObserver() && suiteFilter_->isEnabled())) - { - //This will call updateSuiteFilterWithLoaded() - comQueue_->addSuiteListTask(); - } - else - { - std::vector defSuites; - vRoot_->suites(defSuites); - suiteFilter_->setLoaded(defSuites); - } -} - -bool ServerHandler::readFromDisk() const -{ - return conf_->boolValue(VServerSettings::ReadFromDisk); -} - -QString ServerHandler::nodeMenuMode() const -{ - return conf_->stringValue(VServerSettings::NodeMenuMode); -} - -void ServerHandler::confChanged(VServerSettings::Param par,VProperty* prop) -{ - switch(par) - { - case VServerSettings::AutoUpdate: - case VServerSettings::UpdateRate: - case VServerSettings::AdaptiveUpdate: - case VServerSettings::AdaptiveUpdateMode: - case VServerSettings::AdaptiveUpdateIncrement: - updateRefreshTimer(); - break; - case VServerSettings::MaxAdaptiveUpdateRate: - { - if(!checkRefreshTimerDrift()) - { - updateRefreshTimer(); - } - break; - } - case VServerSettings::NodeMenuMode: - MainWindow::updateMenuMode(this); - break; - - case VServerSettings::NotifyAbortedEnabled: - case VServerSettings::NotifyRestartedEnabled: - case VServerSettings::NotifyLateEnabled: - case VServerSettings::NotifyZombieEnabled: - case VServerSettings::NotifyAliasEnabled: - checkNotificationState(par); - break; - default: - break; - } -} - -void ServerHandler::checkNotificationState(VServerSettings::Param par) -{ - std::string id=VServerSettings::notificationId(par); - if(id.empty()) - return; - - bool enabled=false; - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - { - ServerHandler *s=*it; - if(s->conf()->boolValue(par)) - { - enabled=true; - break; - } - } - - ChangeNotify::updateNotificationStateFromServer(id,enabled); -} - -//Called from changeNotify -bool ServerHandler::checkNotificationState(const std::string& notifierId) -{ - bool enabled=false; - VServerSettings::Param par=VServerSettings::notificationParam(notifierId); - - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - { - ServerHandler *s=*it; - if(s->conf()->boolValue(par)) - { - enabled=true; - break; - } - } - - return enabled; -} - -void ServerHandler::saveSettings() -{ - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - (*it)->saveConf(); -} - -void ServerHandler::saveConf() -{ - conf_->saveSettings(); -} - -void ServerHandler::loadConf() -{ - //This will call confChanged for any non-default settings - conf_->loadSettings(); -} - -//-------------------------------------------- -// Other -//-------------------------------------------- - -void ServerHandler::searchBegan() -{ - UiLog(this).dbg() << "ServerHandler::searchBegan --> suspend queue"; - comQueue_->suspend(true); -} - -void ServerHandler::searchFinished() -{ - UiLog(this).dbg() << "ServerHandler::searchFinished --> start queue"; - comQueue_->start(); - -} - -int ServerHandler::truncatedLinesFromServer(const std::string& txt) const -{ - //if the text is truncated the following line is added to the bottom of it: - //# >>>>>>>> File truncated down to 15. Truncated from the end of file <<<<<<<<< - //We search for this string and if truncation did happen we indicate it in the reply - size_t txtSize=txt.size(); - if(txt.find(">> File truncated down to", - (txtSize > 200)?(txtSize-100):0) != std::string::npos) - { - return conf_->intValue(VServerSettings::MaxOutputFileLines); - } - - return -1; -} - -//-------------------------------------------------------------- -// -// Find the server for a node. -// TODO: this is just a backup method. We might not want to use it -// at all, since it is not safe. -// -//-------------------------------------------------------------- - -ServerHandler* ServerHandler::find(const std::string& name) -{ - for(std::vector::const_iterator it=servers_.begin(); it != servers_.end(); ++it) - if((*it)->name() == name) - return *it; - return NULL; -} diff -Nru ecflow-4.9.0/Viewer/src/ServerHandler.hpp ecflow-4.11.1/Viewer/src/ServerHandler.hpp --- ecflow-4.9.0/Viewer/src/ServerHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,230 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERHANDLER_HPP_ -#define SERVERHANDLER_HPP_ - -#include -#include -#include -#include -#include - -#include "Defs.hpp" - -#include "VReply.hpp" -#include "VTask.hpp" -#include "VInfo.hpp" -#include "VServerSettings.hpp" - -#include - -class ClientInvoker; -class ServerReply; - -class ConnectState; -class NodeObserver; -class ServerHandler; -class ServerComQueue; -class ServerObserver; -class ServerComObserver; -class SuiteFilter; -class UpdateTimer; -class VNodeChange; -class VServer; -class VServerChange; -class VSettings; -class VSuiteNode; - - -class ServerHandler : public QObject -{ - Q_OBJECT // inherits from QObject in order to gain signal/slots - - friend class ServerDefsAccess; - friend class ServerComQueue; - friend class CommandHandler; - -public: - enum Activity {NoActivity,LoadActivity,RescanActivity}; - - const std::string& name() const {return name_;} - const std::string& host() const {return host_;} - const std::string& longName() const {return longName_;} - const std::string& port() const {return port_;} - - Activity activity() const {return activity_;} - ConnectState* connectState() const {return connectState_;} - bool communicating() {return communicating_;} - bool readFromDisk() const; - SuiteFilter* suiteFilter() const {return suiteFilter_;} - QString nodeMenuMode() const; - - void setSuiteFilterWithOne(VNode*); - void updateSuiteFilter(SuiteFilter*); - void updateSuiteFilterWithDefs(); - - void connectServer(); - void disconnectServer(); - void reset(); - - void refresh(); - void setUpdatingStatus(bool newStatus) {updating_ = newStatus;} - - VServer* vRoot() const {return vRoot_;} - SState::State serverState(); - NState::State state(bool& isSuspended); - - void run(VTask_ptr); - - void addNodeObserver(NodeObserver* obs); - void removeNodeObserver(NodeObserver* obs); - - void addServerObserver(ServerObserver* obs); - void removeServerObserver(ServerObserver* obs); - - void addServerComObserver(ServerComObserver* obs); - void removeServerComObserver(ServerComObserver* obs); - - void confChanged(VServerSettings::Param,VProperty*); - VServerSettings* conf() const {return conf_;} - - bool isLocalHost() {return (localHostName_ == host_ || host_ == "localhost");} - - static void saveSettings(); - - static const std::vector& servers() {return servers_;} - static ServerHandler* addServer(const std::string &name,const std::string &host, const std::string &port); - static void removeServer(ServerHandler*); - static ServerHandler* findServer(const std::string &alias); - - void searchBegan(); - void searchFinished(); - bool updateInfo(int& basePeriod,int& currentPeriod,int &drift,int& toNext); - QDateTime lastRefresh() const {return lastRefresh_;} - int secsSinceLastRefresh() const; - int secsTillNextRefresh() const; - - static bool checkNotificationState(const std::string& notifierId); - - static ServerHandler* find(const std::string& name); - -protected: - ServerHandler(const std::string& name,const std::string& host,const std::string& port); - ~ServerHandler(); - - //Only friend classes can access it. Practically it means we - //we can only run it through CommandHandler!!! - void runCommand(const std::vector& cmd); - - void connectToServer(); - void setCommunicatingStatus(bool c) {communicating_ = c;} - void clientTaskFinished(VTask_ptr task,const ServerReply& serverReply); - void clientTaskFailed(VTask_ptr task,const std::string& errMsg); - - static void checkNotificationState(VServerSettings::Param par); - - bool checkRefreshTimerDrift() const; - void refreshScheduled(); - void refreshFinished(); - - std::string name_; - std::string host_; - std::string port_; - ClientInvoker* client_; - std::string longName_; - bool updating_; - bool communicating_; - std::vector nodeObservers_; - std::vector serverObservers_; - std::vector serverComObservers_; - - VServer* vRoot_; - - //The list of suites the server makes accessible - SuiteFilter* suiteFilter_; - - static std::vector servers_; - -private Q_SLOTS: - void refreshServerInfo(); - void slotNodeChanged(const Node* n, std::vector); - void slotDefsChanged(std::vector); - void slotRescanNeed(); - -private: - //Begin and end the initialisation by connecting to the server and syncing. - void refreshInternal(); - void resetFinished(); - void resetFailed(const std::string& errMsg); - void clearTree(); - void rescanTree(); - void connectionLost(const std::string& errMsg); - bool connectionGained(); - - void updateSuiteFilterWithLoaded(const std::vector&); - void updateSuiteFilter(); - - //Handle the refresh timer - void stopRefreshTimer(); - void startRefreshTimer(); - void updateRefreshTimer(); - void driftRefreshTimer(); - - void script(VTask_ptr req); - void job(VTask_ptr req); - void jobout(VTask_ptr req); - void manual(VTask_ptr req); - - defs_ptr defs(); - defs_ptr safelyAccessSimpleDefsMembers(); - - void setActivity(Activity activity); - - typedef void (ServerObserver::*SoMethod)(ServerHandler*); - typedef void (ServerObserver::*SoMethodV1)(ServerHandler*,const VServerChange&); - void broadcast(SoMethod); - void broadcast(SoMethodV1,const VServerChange&); - - typedef void (NodeObserver::*NoMethod)(const VNode*); - typedef void (NodeObserver::*NoMethodV1)(const VNode*,const std::vector&,const VNodeChange&); - typedef void (NodeObserver::*NoMethodV2)(const VNode*,const VNodeChange&); - void broadcast(NoMethod,const VNode*); - void broadcast(NoMethodV1,const VNode*,const std::vector&,const VNodeChange&); - void broadcast(NoMethodV2,const VNode*,const VNodeChange&); - - typedef void (ServerComObserver::*SocMethod)(ServerHandler*); - void broadcast(SocMethod); - - void saveConf(); - void loadConf(); - - int truncatedLinesFromServer(const std::string& txt) const; - - QMutex defsMutex_; - defs_ptr defs_; - - ServerComQueue* comQueue_; - - //std::string targetNodeNames_; // used when building up a command in ServerHandler::command - //std::string targetNodeFullNames_; // used when building up a command in ServerHandler::command - - UpdateTimer* refreshTimer_; - QDateTime lastRefresh_; - - Activity activity_; - ConnectState* connectState_; - SState::State prevServerState_; - - VServerSettings* conf_; - - static std::string localHostName_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerItem.cpp ecflow-4.11.1/Viewer/src/ServerItem.cpp --- ecflow-4.9.0/Viewer/src/ServerItem.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerItem.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,141 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerItem.hpp" -#include "ServerHandler.hpp" - -#include -#include -#include -#include -#include - - -ServerItem::ServerItem(const std::string& name) : - name_(name), - favourite_(false), - system_(false), - useCnt_(0), - handler_(0) -{ -} - -ServerItem::ServerItem(const std::string& name,const std::string& host,const std::string& port, bool favourite) : - name_(name), host_(host), port_(port), - favourite_(favourite), - system_(false), - useCnt_(0), - handler_(0) -{ -} - -ServerItem::~ServerItem() -{ - broadcastDeletion(); - - if(handler_) - ServerHandler::removeServer(handler_); -} - -bool ServerItem::isUsed() const -{ - return (handler_ != NULL); -} - - -void ServerItem::reset(const std::string& name,const std::string& host,const std::string& port) -{ - name_=name; - host_=host; - port_=port; - - broadcastChanged(); -} - -void ServerItem::setFavourite(bool b) -{ - favourite_=b; - broadcastChanged(); -} - -void ServerItem::setSystem(bool b) -{ - system_=b; - //broadcastChanged(); -} - -std::string ServerItem::longName() const -{ - return host_ + "@" + port_; -} - -//=========================================================== -// Register the usage of the server. Create and destroys the -// the ServerHandler. -//=========================================================== - -void ServerItem::registerUsageBegin() -{ - if(!handler_) - { - handler_=ServerHandler::addServer(name_,host_,port_); - } - if(handler_) - useCnt_++; -} - -void ServerItem::registerUsageEnd() -{ - useCnt_--; - if(useCnt_ == 0 && handler_) - { - ServerHandler::removeServer(handler_); - handler_=0; - } -} - -//=========================================================== -// Observers -//=========================================================== - -void ServerItem::addObserver(ServerItemObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - { - registerUsageBegin(); - //We might not be able to create the handle - if(handler_) - observers_.push_back(o); - } -} - -void ServerItem::removeObserver(ServerItemObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - { - observers_.erase(it); - registerUsageEnd(); - } -} - -void ServerItem::broadcastChanged() -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - (*it)->notifyServerItemChanged(this); -} - -void ServerItem::broadcastDeletion() -{ - std::vector obsCopy=observers_; - - for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) - (*it)->notifyServerItemDeletion(this); -} diff -Nru ecflow-4.9.0/Viewer/src/ServerItem.hpp ecflow-4.11.1/Viewer/src/ServerItem.hpp --- ecflow-4.9.0/Viewer/src/ServerItem.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerItem.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERITEM_HPP_ -#define SERVERITEM_HPP_ - -#include -#include -#include - -#include -#include - -class ServerHandler; -class ServerItem; - -class ServerItemObserver -{ -public: - ServerItemObserver() {} - virtual ~ServerItemObserver() {} - virtual void notifyServerItemChanged(ServerItem*)=0; - virtual void notifyServerItemDeletion(ServerItem*)=0; -}; - - -class ServerItem -{ - -friend class ServerList; - -public: - const std::string& name() const {return name_;} - const std::string& host() const {return host_;} - const std::string& port() const {return port_;} - std::string longName() const; - bool isFavourite() const {return favourite_;} - bool isSystem() const {return system_;} - - bool isUsed() const; - int useCnt() const {return useCnt_;} - ServerHandler* serverHandler() const {return handler_;} - - void addObserver(ServerItemObserver*); - void removeObserver(ServerItemObserver*); - -protected: - explicit ServerItem(const std::string&); - ServerItem(const std::string&,const std::string&,const std::string&,bool favourite=false); - ~ServerItem(); - - void name(const std::string& name) {name_=name;} - void host(const std::string& host) {host_=host;} - void port(const std::string& port) {port_=port;} - void reset(const std::string& name,const std::string& host,const std::string& port); - void setFavourite(bool b); - void setSystem(bool b); - - void registerUsageBegin(); - void registerUsageEnd(); - - void broadcastChanged(); - void broadcastDeletion(); - - std::string name_; - std::string host_; - std::string port_; - bool favourite_; - bool system_; - int useCnt_; - ServerHandler* handler_; - - std::vector observers_; -}; - -typedef boost::shared_ptr ServerItem_ptr; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerList.cpp ecflow-4.11.1/Viewer/src/ServerList.cpp --- ecflow-4.9.0/Viewer/src/ServerList.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerList.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,583 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include -#include -#include - -#include "ServerList.hpp" - -#include "DirectoryHandler.hpp" -#include "ServerItem.hpp" -#include "UserMessage.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" - -#include -#include -#include -#include -#include - -#include - -ServerList* ServerList::instance_=0; - -#define _UI_SERVERLIST_DEBUG - -ServerListTmpItem::ServerListTmpItem(ServerItem* item) : - name_(item->name()), - host_(item->host()), - port_(item->port()) -{} - -//Singleton instance method -ServerList* ServerList::instance() -{ - if(!instance_) - instance_=new ServerList(); - return instance_; -} - -//=========================================================== -// Managing server items -//=========================================================== - -ServerItem* ServerList::itemAt(int index) -{ - return (index >=0 && index < static_cast(items_.size()))?items_.at(index):0; -} - -ServerItem* ServerList::find(const std::string& name) -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name) - return *it; - } - return 0; -} - -ServerItem* ServerList::find(const std::string& name, const std::string& host, const std::string& port) -{ - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name && (*it)->host() == host && (*it)->port() == port) - return *it; - } - return 0; -} - -ServerItem* ServerList::add(const std::string& name,const std::string& host, - const std::string& port,bool favourite,bool saveIt) -{ - std::string errStr; - if(!checkItemToAdd(name,host,port,true,errStr)) - { - throw std::runtime_error(errStr); - return 0; - } - - ServerItem* item=new ServerItem(name,host,port,favourite); - - items_.push_back(item); - - if(saveIt) - save(); - - broadcastChanged(); - - return item; -} - -void ServerList::remove(ServerItem *item) -{ - std::vector::iterator it=std::find(items_.begin(),items_.end(),item); - if(it != items_.end()) - { - items_.erase(it); - //item->broadcastDeletion(); - delete item; - - save(); - broadcastChanged(); - } -} - -void ServerList::reset(ServerItem* item,const std::string& name,const std::string& host,const std::string& port) -{ - std::vector::iterator it=std::find(items_.begin(),items_.end(),item); - if(it != items_.end()) - { - //Check if there is an item with the same name. names have to be unique! - if(item->name() != name && find(name)) - return; - - item->reset(name,host,port); - - save(); - broadcastChanged(); - } -} - -void ServerList::setFavourite(ServerItem* item,bool b) -{ - std::vector::iterator it=std::find(items_.begin(),items_.end(),item); - if(it != items_.end()) - { - item->setFavourite(b); - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - (*it)->notifyServerListFavouriteChanged(item); - } -} - -std::string ServerList::uniqueName(const std::string& name) -{ - bool hasIt=false; - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == name) - { - hasIt=true; - break; - } - } - if(!hasIt) - { - return name; - } - - for(int i=1; i < 100; i++) - { - std::ostringstream c; - c << i; - std::string currentName=name+"_"+c.str(); - - hasIt=false; - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it)->name() == currentName) - { - hasIt=true; - break; - } - } - if(!hasIt) - { - return currentName; - } - } - - return name; - -} - - -void ServerList::rescan() -{ - -} - -bool ServerList::checkItemToAdd(const std::string& name,const std::string& host,const std::string& port,bool checkDuplicate,std::string& errStr) -{ - if(name.empty()) - { - errStr="Empty server name"; - return false; - } - else if(host.empty()) - { - errStr="Empty server host"; - return false; - } - else if(port.empty()) - { - errStr="Empty server port"; - return false; - } - - try { boost::lexical_cast(port); } - catch ( boost::bad_lexical_cast& e) - { - errStr="Invalid port number: " + port; - return false; - } - - if(checkDuplicate && find(name)) - { - errStr="Duplicated server name: " + name; - return false; - } - - return true; -} - -//=========================================================== -// Initialisation -//=========================================================== - -void ServerList::init() -{ - localFile_ = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "servers.txt"); - systemFile_=DirectoryHandler::concatenate(DirectoryHandler::shareDir(), "servers"); - - if(load() == false) - { - if(readRcFile()) - save(); - } - - syncSystemFile(); -} - -bool ServerList::load() -{ - UiLog().dbg() << "ServerList::load() -->"; - - std::ifstream in(localFile_.c_str()); - if(!in.good()) - return false; - - std::string errStr; - - std::string line; - int lineCnt=1; - while(getline(in,line)) - { - //We ignore comment lines - std::string buf=boost::trim_left_copy(line); - if(buf.size() > 0 && buf.at(0) == '#') - { - lineCnt++; - continue; - } - - std::vector sv; - boost::split(sv,line,boost::is_any_of(",")); - - bool favourite=false; - if(sv.size() >= 4) - favourite=(sv[3]=="1")?true:false; - - bool sys=false; - if(sv.size() >= 5) - sys=(sv[4]=="1")?true:false; - - if(sv.size() >= 3) - { - std::string name=sv[0], host=sv[1], port=sv[2]; - ServerItem* item=0; - try - { - item=add(name,host,port,favourite,false); - UI_ASSERT(item != 0,"name=" << name << " host=" << host << " port=" << port); - item->setSystem(sys); - } - catch(std::exception& e) - { - std::string err=e.what(); - err+=" [name=" + name + ",host=" + host + ",port=" + port + "]"; - errStr+=err + " (in line " + UiLog::toString(lineCnt) + ")
      "; - UiLog().err() << " " << err << " (in line " << lineCnt << ")"; - } - } - - lineCnt++; - } - - in.close(); - - if(!errStr.empty()) - { - errStr="Could not parse the server list file " + localFile_ + ". The \ - following errors occured:

      " + - errStr + "
      Please correct the errors in the server list file and restart ecFlowUI!"; - UserMessage::setEchoToCout(false); - UserMessage::message(UserMessage::ERROR,true,errStr); - UserMessage::setEchoToCout(true); - exit(1); - } - - if(count() == 0) - return false; - - return true; -} - -void ServerList::save() -{ - std::ofstream out; - out.open(localFile_.c_str()); - if(!out.good()) - return; - - out << "#Name Host Port Favourite System" << std::endl; - - for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) - { - std::string fav=((*it)->isFavourite())?"1":"0"; - std::string sys=((*it)->isSystem())?"1":"0"; - out << (*it)->name() << "," << (*it)->host() << "," << (*it)->port() << "," << fav << "," << sys << std::endl; - } - out.close(); -} - -bool ServerList::readRcFile() -{ - UiLog().dbg() << "ServerList::readRcFile -->"; - std::string path(DirectoryHandler::concatenate(DirectoryHandler::rcDir(), "servers")); - - UiLog().dbg() << " Read servers from ecflowview rcfile: " << path; - std::ifstream in(path.c_str()); - - if(in.good()) - { - std::string line; - while(getline(in,line)) - { - std::string buf=boost::trim_left_copy(line); - if(buf.size() > 0 && buf.at(0) == '#') - continue; - - std::stringstream ssdata(line); - std::vector vec; - - while(ssdata >> buf) - { - vec.push_back(buf); - } - - if(vec.size() >= 3) - { - std::string name=vec[0], host=vec[1], port=vec[2]; - try - { - add(name,host,port,false,false); - } - catch(std::exception& e) - { - std::string err=e.what(); - UiLog().err() << " Failed to read server (name=" << name << ",host=" << host << - ",port=" << port << "). " << err; - } - } - } - } - else - return false; - - in.close(); - - return true; -} - -bool ServerList::hasSystemFile() const -{ - boost::filesystem::path p(systemFile_); - return boost::filesystem::exists(p); -} - -void ServerList::syncSystemFile() -{ - UiLog().dbg() << "ServerList::syncSystemFile -->"; - - std::vector sysVec; - std::ifstream in(systemFile_.c_str()); - - syncDate_=QDateTime::currentDateTime(); - clearSyncChange(); - - if(in.good()) - { - std::string line; - while(getline(in,line)) - { - std::string buf=boost::trim_left_copy(line); - if(buf.size() >0 && buf.at(0) == '#') - continue; - - std::stringstream ssdata(line); - std::vector vec; - - while(ssdata >> buf) - { - vec.push_back(buf); - } - - if(vec.size() >= 3) - { - std::string errStr,name=vec[0], host=vec[1], port=vec[2]; - if(checkItemToAdd(name,host,port,false,errStr)) - { - sysVec.push_back(ServerListTmpItem(vec[0],vec[1],vec[2])); - } - } - } - } - else - return; - - in.close(); - -#ifdef _UI_SERVERLIST_DEBUG - for(unsigned int i=0; i < sysVec.size(); i++) - UiLog().dbg() << sysVec[i].name() << "\t" + sysVec[i].host() << "\t" + sysVec[i].port(); -#endif - - bool changed=false; - bool needBrodcast=false; - - //See what changed or was added - for(unsigned int i=0; i < sysVec.size(); i++) - { -#ifdef _UI_SERVERLIST_DEBUG - UiLog().dbg() << sysVec[i].name() << "\t" + sysVec[i].host() << "\t" + sysVec[i].port(); -#endif - ServerItem *item=0; - - //There is a server with same name, host and port as in the local list. We - //mark it as system - item=find(sysVec[i].name(),sysVec[i].host(),sysVec[i].port()); - if(item) - { - if(!item->isSystem()) - { -#ifdef _UI_SERVERLIST_DEBUG - UiLog().dbg() << " already in list (same name, host, port) -> mark as system"; -#endif - changed=true; - syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],sysVec[i], - ServerListSyncChangeItem::SetSysChange)); - item->setSystem(true); - } - continue; - } - - //There is no server with the same name in the local list - item=find(sysVec[i].name()); - if(!item) - { -#ifdef _UI_SERVERLIST_DEBUG - UiLog().dbg() << " name not in list -> import as system"; -#endif - changed=true; - std::string name=sysVec[i].name(),host=sysVec[i].host(), port=sysVec[i].port(); - try - { - item=add(name,host,port,false,false); - UI_ASSERT(item != 0,"name=" << name << " host=" << host - << " port=" << port); - item->setSystem(true); - syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],sysVec[i], - ServerListSyncChangeItem::AddedChange)); - } - catch(std::exception& e) - { - std::string err=e.what(); - UiLog().err() << " Could not sync server (name=" << name << ",host=" << host << - "port=" << port << "). " << err; - } - continue; - } - //There is a server with the same name but with different host or/and port - else - { -#ifdef _UI_SERVERLIST_DEBUG - UiLog().dbg() << " name in list with different port or/and host"; -#endif - changed=true; - needBrodcast=true; - assert(item->name() == sysVec[i].name()); - - ServerListTmpItem localTmp(item); - syncChange_.push_back(new ServerListSyncChangeItem(sysVec[i],localTmp, - ServerListSyncChangeItem::MatchChange)); - - item->reset(sysVec[i].name(),sysVec[i].host(),sysVec[i].port()); - item->setSystem(true); - broadcastChanged(); - continue; - } - } - - std::vector itemsCopy=items_; - - //See what needs to be removed - for(std::vector::const_iterator it=itemsCopy.begin(); it != itemsCopy.end(); ++it) - { - if((*it)->isSystem()) - { - bool found=false; - for(unsigned int i=0; i < sysVec.size(); i++) - { - if(sysVec[i].name() == (*it)->name()) - { - found=true; - break; - } - } - if(!found) - { - changed=true; - ServerListTmpItem localTmp(*it); - syncChange_.push_back(new ServerListSyncChangeItem(localTmp,localTmp, - ServerListSyncChangeItem::UnsetSysChange)); - //remove item - remove(*it); - } - } - } - - if(changed) - save(); - - if(needBrodcast) - broadcastChanged(); - -#ifdef _UI_SERVERLIST_DEBUG - UiLog().dbg() << "<-- syncSystemFile"; -#endif -} - -void ServerList::clearSyncChange() -{ - for(size_t i=0;i < syncChange_.size(); i++) - delete syncChange_[i]; - - syncChange_.clear(); -} - - -//=========================================================== -// Observers -//=========================================================== - -void ServerList::addObserver(ServerListObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - { - observers_.push_back(o); - } -} - -void ServerList::removeObserver(ServerListObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - { - observers_.erase(it); - } -} - -void ServerList::broadcastChanged() -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - (*it)->notifyServerListChanged(); -} diff -Nru ecflow-4.9.0/Viewer/src/ServerListDialog.cpp ecflow-4.11.1/Viewer/src/ServerListDialog.cpp --- ecflow-4.9.0/Viewer/src/ServerListDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,974 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ServerListDialog.hpp" - -#include -#include -#include -#include -#include -#include - -#include "IconProvider.hpp" -#include "ServerFilter.hpp" -#include "ServerItem.hpp" -#include "ServerList.hpp" -#include "ServerListSyncWidget.hpp" -#include "SessionHandler.hpp" -#include "VConfig.hpp" -#include "WidgetNameProvider.hpp" - -static bool firstShowSysSyncLogW=true; - -//====================================== -// -// ServerDialogChecked -// -//====================================== - -bool ServerDialogChecker::checkName(QString name,QString oriName) -{ - if(name.simplified().isEmpty()) - { - error(QObject::tr("Name cannot be empty!")); - return false; - } - else if(name.contains(",")) - { - error(QObject::tr("Name cannot contain comma character!")); - return false; - } - - if(oriName != name && ServerList::instance()->find(name.toStdString()) ) - { - error(QObject::tr("The specified server already exists! Please select a different name!")); - return false; - } - - return true; -} - -bool ServerDialogChecker::checkHost(QString host) -{ - if(host.simplified().isEmpty()) - { - error(QObject::tr("Host cannot be empty!")); - return false; - } - else if(host.contains(",")) - { - error(QObject::tr("Host cannot contain comma character!")); - return false; - } - - return true; -} - -bool ServerDialogChecker::checkPort(QString port) -{ - if(port.simplified().isEmpty()) - { - error(QObject::tr("Port cannot be empty!")); - return false; - } - else if(port.contains(",")) - { - error(QObject::tr("Port cannot contain comma character!")); - return false; - } - return true; -} - - -void ServerDialogChecker::error(QString msg) -{ - QMessageBox::critical(0,QObject::tr("Server item"),errorText_ + "
      "+ msg); -} - -//====================================== -// -// ServerAddDialog -// -//====================================== - -ServerAddDialog::ServerAddDialog(QWidget *parent) : - QDialog(parent), - ServerDialogChecker(tr("Cannot create new server!")) -{ - setupUi(this); - - //nameEdit->setText(); - //hostEdit->setText(); - //portEdit->setText(); - addToCurrentCb->setChecked(true); - - //Validators - //nameEdit->setValidator(new QRegExpValidator("")); - //hostEdit->setValidator(new QRegExpValidator("")); - portEdit->setValidator(new QIntValidator(1025,65535,this)); -} - -void ServerAddDialog::accept() -{ - QString name=nameEdit->text(); - QString host=hostEdit->text(); - QString port=portEdit->text(); - - if(!checkName(name) || !checkHost(host) || !checkPort(port)) - return; - - QDialog::accept(); -} - -QString ServerAddDialog::name() const -{ - return nameEdit->text(); -} - -QString ServerAddDialog::host() const -{ - return hostEdit->text(); -} - -QString ServerAddDialog::port() const -{ - return portEdit->text(); -} - -bool ServerAddDialog::addToView() const -{ - return addToCurrentCb->isChecked(); -} - -//====================================== -// -// ServerEditDialog -// -//====================================== - -ServerEditDialog::ServerEditDialog(QString name, QString host, QString port,bool favourite,QWidget *parent) : - QDialog(parent), - ServerDialogChecker(tr("Cannot modify server!")), - oriName_(name) -{ - setupUi(this); - - nameEdit->setText(name); - hostEdit->setText(host); - portEdit->setText(port); - favCh->setChecked(favourite); - - //Validators - //nameEdit->setValidator(new QRegExpValidator("")); - //hostEdit->setValidator(new QRegExpValidator("")); - portEdit->setValidator(new QIntValidator(1025,65535,this)); -} - -void ServerEditDialog::accept() -{ - QString name=nameEdit->text(); - QString host=hostEdit->text(); - QString port=portEdit->text(); - - if(!checkName(name,oriName_) || !checkHost(host) || !checkPort(port)) - return; - - QDialog::accept(); -} - - -QString ServerEditDialog::name() const -{ - return nameEdit->text(); -} - -QString ServerEditDialog::host() const -{ - return hostEdit->text(); -} - -QString ServerEditDialog::port() const -{ - return portEdit->text(); -} - -bool ServerEditDialog::isFavourite() const -{ - return favCh->isChecked(); -} - -//====================================== -// -// ServerListDialog -// -//====================================== - -ServerListDialog::ServerListDialog(Mode mode,ServerFilter *filter,QWidget *parent) : - QDialog(parent), - filter_(filter), - mode_(mode) -{ - setupUi(this); - - QString wt=windowTitle(); - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - sortModel_=new ServerListFilterModel(this); - model_=new ServerListModel(filter_,this); - sortModel_->setSourceModel(model_); - sortModel_->setDynamicSortFilter(true); - - serverView->setRootIsDecorated(false); - serverView->setAllColumnsShowFocus(true); - serverView->setUniformRowHeights(true); - serverView->setAlternatingRowColors(true); - serverView->setSortingEnabled(true); - serverView->setSelectionMode(QAbstractItemView::ExtendedSelection); - serverView->sortByColumn(ServerListModel::NameColumn,Qt::AscendingOrder); - - serverView->setModel(sortModel_); - - //Add context menu actions to the view - QAction* sep1=new QAction(this); - sep1->setSeparator(true); - QAction* sep2=new QAction(this); - sep2->setSeparator(true); - QAction* sep3=new QAction(this); - sep3->setSeparator(true); - - serverView->addAction(actionAdd); - serverView->addAction(sep1); - serverView->addAction(actionDuplicate); - serverView->addAction(actionEdit); - serverView->addAction(sep2); - serverView->addAction(actionDelete); - serverView->addAction(sep3); - serverView->addAction(actionFavourite); - - //Add actions for the toolbuttons - addTb->setDefaultAction(actionAdd); - deleteTb->setDefaultAction(actionDelete); - editTb->setDefaultAction(actionEdit); - duplicateTb->setDefaultAction(actionDuplicate); - //rescanTb->setDefaultAction(actionRescan); - - checkActionState(); - -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - filterLe_->setPlaceholderText(tr("Filter")); -#endif - -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - filterLe_->setClearButtonEnabled(true); -#endif - - connect(filterLe_,SIGNAL(textEdited(QString)), - this,SLOT(slotFilter(QString))); - - connect(filterFavTb_,SIGNAL(clicked(bool)), - this,SLOT(slotFilterFavourite(bool))); - - //Load settings - readSettings(); - - //The selection changes in the view - connect(serverView->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); - - connect(serverView,SIGNAL(clicked(QModelIndex)), - this,SLOT(slotItemClicked(QModelIndex))); - - - for(int i=0; i < model_->columnCount()-1; i++) - serverView->resizeColumnToContents(i); - -#if 0 - QFont labelF; - labelF.setBold(true); - labelF.setPointSize(labelF.pointSize()-1); - - systemListLabel->setFont(labelF); - systemListLabel->setText("" + systemListLabel->text() + ""); -#endif - systemListLabel->hide(); - - //At the moment we do not want users to sync manually. It is done automatically - //on each startup. - sysSyncTb->hide(); - - //The synclog is hidden - sysSyncLogTb->setChecked(false); - sysSyncLogW_->hide(); - if(!ServerList::instance()->hasSystemFile()) - { - sysSyncLogTb->setEnabled(false); - } - - //Assign name to each object - WidgetNameProvider::nameChildren(this); -} - -ServerListDialog::~ServerListDialog() -{ - writeSettings(); -} - -void ServerListDialog::closeEvent(QCloseEvent* event) -{ - event->accept(); - writeSettings(); - ServerList::instance()->save(); -} - -void ServerListDialog::accept() -{ - //Overwrite ServerList with the actual data - writeSettings(); - QDialog::accept(); - ServerList::instance()->save(); -} - -void ServerListDialog::reject() -{ - //Overwrite ServerList with the actual data - writeSettings(); - QDialog::reject(); - ServerList::instance()->save(); -} - -void ServerListDialog::editItem(const QModelIndex& index) -{ - if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index))) - { - if(item->isSystem()) - return; - - ServerEditDialog d(QString::fromStdString(item->name()), - QString::fromStdString(item->host()), - QString::fromStdString(item->port()), - item->isFavourite(),this); - - //The dialog checks the name, host and port! - if(d.exec()== QDialog::Accepted) - { - ServerList::instance()->reset(item,d.name().toStdString(),d.host().toStdString(),d.port().toStdString()); - - if(item->isFavourite() != d.isFavourite()) - { - ServerList::instance()->setFavourite(item,d.isFavourite()); - QModelIndex idx=sortModel_->index(index.row(),ServerListModel::FavouriteColumn); - serverView->update(idx); - } - } - } -} - -void ServerListDialog::duplicateItem(const QModelIndex& index) -{ - if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index))) - { - std::string dname=ServerList::instance()->uniqueName(item->name()); - - ServerEditDialog d(QString::fromStdString(dname), - QString::fromStdString(item->host()), - QString::fromStdString(item->port()),item->isFavourite(),this); - - //The dialog checks the name, host and port! - if(d.exec() == QDialog::Accepted) - { - model_->dataIsAboutToChange(); - ServerList::instance()->add(d.name().toStdString(),d.host().toStdString(),d.port().toStdString(),false); - model_->dataChangeFinished(); - } - } -} - -void ServerListDialog::addItem() -{ - ServerAddDialog d(this); - - //The dialog checks the name, host and port! - if(d.exec() == QDialog::Accepted) - { - model_->dataIsAboutToChange(); - ServerItem* item=0; - try { - item=ServerList::instance()->add(d.name().toStdString(),d.host().toStdString(),d.port().toStdString(),false); - } - catch(std::exception& e) - { - - } - - model_->dataChangeFinished(); - if(d.addToView() && filter_) - { - filter_->addServer(item); - } - } -} - -void ServerListDialog::removeItem(const QModelIndex& index) -{ - ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); - - if(!item) - return; - - QString str=tr("Are you sure that you want to delete server: "); - str+=QString::fromStdString(item->name()) ; - str+="?"; - str+=tr("
      It will be removed from all the existing views!"); - - QMessageBox msgBox; - msgBox.setWindowTitle(tr("Delete server")); - msgBox.setText(str); - msgBox.setIcon(QMessageBox::Warning); - msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel); - msgBox.setDefaultButton(QMessageBox::Cancel); - int ret=msgBox.exec(); - - switch (ret) - { - case QMessageBox::Yes: - model_->dataIsAboutToChange(); - //It will delete item as well! - ServerList::instance()->remove(item); - model_->dataChangeFinished(); - break; - case QMessageBox::Cancel: - // Cancel was clicked - break; - default: - // should never be reached - break; - } -} - - -void ServerListDialog::setFavouriteItem(const QModelIndex& index,bool b) -{ - ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); - - if(!item) - return; - - ServerList::instance()->setFavourite(item,b); - - QModelIndex idx=sortModel_->index(index.row(),ServerListModel::FavouriteColumn); - serverView->update(idx); -} - - -void ServerListDialog::on_serverView_doubleClicked(const QModelIndex& index) -{ - int col=index.column(); - if(col == ServerListModel::NameColumn || col == ServerListModel::HostColumn || - col == ServerListModel::PortColumn) - { - editItem(index); - } -} - -void ServerListDialog::on_actionEdit_triggered() -{ - QModelIndex index=serverView->currentIndex(); - editItem(index); -} - -void ServerListDialog::on_actionAdd_triggered() -{ - addItem(); -} - -void ServerListDialog::on_actionDelete_triggered() -{ - QModelIndex index=serverView->currentIndex(); - removeItem(index); -} - -void ServerListDialog::on_actionDuplicate_triggered() -{ - QModelIndex index=serverView->currentIndex(); - duplicateItem(index); -} - -void ServerListDialog::on_actionFavourite_triggered(bool checked) -{ - QModelIndex index=serverView->currentIndex(); - setFavouriteItem(index,checked); -} - -void ServerListDialog::on_actionRescan_triggered() -{ - -} - -void ServerListDialog::on_sysSyncTb_clicked(bool) -{ -#if 0 - ServerList::instance()->syncSystemFile(); - on_sysSyncLogTb_toggled(true); -#endif -} - -void ServerListDialog::on_sysSyncLogTb_toggled(bool b) -{ - sysSyncLogW_->setVisible(b); - if(b && firstShowSysSyncLogW) - { - firstShowSysSyncLogW=false; - - //Set the initial splitter sizes - QList sList=splitter_->sizes(); - Q_ASSERT(sList.count()==2); - int h=sList[0]+sList[1]; - if(h==0) - { - sList[0]=75; - sList[1]=25; - } - else - { - sList[1]=h/2; - if(ServerList::instance()->hasSyncChange()) - { - if(h > 500) - sList[1]=250; - } - else - { - if(h > 100) - sList[1]=50; - } - sList[0]=h-sList[1]; - } - - splitter_->setSizes(sList); - } -} - -void ServerListDialog::showSysSyncLog() -{ - sysSyncLogTb->setChecked(true); -} - -void ServerListDialog::slotItemSelected(const QModelIndex& current,const QModelIndex& prev) -{ - checkActionState(); -} - -void ServerListDialog::slotItemClicked(const QModelIndex& current) -{ - if(ServerItem* item=model_->indexToServer(sortModel_->mapToSource(current))) - { - if(current.column() == ServerListModel::FavouriteColumn) - { - setFavouriteItem(current,!item->isFavourite()); - } - } - - checkActionState(); -} - -void ServerListDialog::checkActionState() -{ - QModelIndex index=serverView->currentIndex(); - - if(!index.isValid()) - { - actionEdit->setEnabled(false); - actionDuplicate->setEnabled(false); - actionDelete->setEnabled(false); - actionFavourite->setEnabled(false); - } - else - { - ServerItem* item=model_->indexToServer(sortModel_->mapToSource(index)); - - assert(item); - - actionEdit->setEnabled(!item->isSystem()); - actionDuplicate->setEnabled(true); - actionDelete->setEnabled(!item->isSystem()); - actionFavourite->setEnabled(true); - actionFavourite->setChecked(item->isFavourite()); - } -} - -void ServerListDialog::slotFilter(QString txt) -{ - sortModel_->setFilterStr(txt); -} - -void ServerListDialog::slotFilterFavourite(bool b) -{ - sortModel_->setFilterFavourite(b); -} - -void ServerListDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("ServerListDialog")), - QSettings::NativeFormat); - - //We have to clear it not to remember all the previous windows - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.setValue("filterFav",filterFavTb_->isChecked()); - settings.endGroup(); -} - -void ServerListDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("ServerListDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(350,500)); - } - - if(settings.contains("filterFav")) - { - //This does not emit the clicked signal - filterFavTb_->setChecked(settings.value("filterFav").toBool()); - //so we need to do it explicitly - sortModel_->setFilterFavourite(filterFavTb_->isChecked()); - } - - settings.endGroup(); -} - -//====================================== -// -// ServerListModel -// -//====================================== - -ServerListModel::ServerListModel(ServerFilter* filter,QObject *parent) : - QAbstractItemModel(parent), - filter_(filter) -{ - int id=IconProvider::add(":/viewer/favourite.svg","favourite"); - favPix_=IconProvider::pixmap(id,12); - - id=IconProvider::add(":/viewer/favourite_empty.svg","favourite_empty"); - favEmptyPix_=IconProvider::pixmap(id,12); - - id=IconProvider::add(":/viewer/system.svg","system"); - sysPix_=IconProvider::pixmap(id,12); - - loadFont_.setBold(true); -} - -ServerListModel::~ServerListModel() -{ -} - -void ServerListModel::dataIsAboutToChange() -{ - beginResetModel(); -} - -void ServerListModel::dataChangeFinished() -{ - endResetModel(); -} - -int ServerListModel::columnCount(const QModelIndex& parent) const -{ - return 6; -} - -int ServerListModel::rowCount(const QModelIndex& parent) const -{ - if(!parent.isValid()) - return static_cast(ServerList::instance()->count()); - - return 0; -} - -QVariant ServerListModel::data(const QModelIndex& index, int role) const -{ - if(!index.isValid() || - (role != Qt::DisplayRole && role != Qt::ForegroundRole && role != Qt::DecorationRole && - role != Qt::CheckStateRole && role != Qt::UserRole && role != Qt::FontRole && - role != IconStatusRole && role != Qt::ToolTipRole)) - { - return QVariant(); - } - - ServerItem* item=ServerList::instance()->itemAt(index.row()); - - if(!item) - return QVariant(); - - if(role == Qt::DisplayRole) - { - switch(index.column()) - { - case NameColumn: return QString::fromStdString(item->name()); - case HostColumn: return QString::fromStdString(item->host()); - case PortColumn: return QString::fromStdString(item->port()); - case UseColumn: - { - int n=item->useCnt(); - if(n > 0) - return "loaded (" + QString::number(n) + ")"; - - return QVariant(); - } - default: return QVariant(); - } - } - else if (role == Qt::ForegroundRole) - { - //return (item->isSystem())?QColor(70,71,72):QVariant(); - return (item->isSystem())?QColor(67,78,109):QVariant(); - } - else if (role == Qt::DecorationRole) - { - if(index.column() == SystemColumn) - return (item->isSystem())?sysPix_:QPixmap(); - - else if(index.column() == FavouriteColumn) - return (item->isFavourite())?favPix_:favEmptyPix_; - - return QVariant(); - } - else if (role == Qt::UserRole) - { - if(index.column() == FavouriteColumn) - return item->isFavourite(); - - return QVariant(); - } - else if (role == Qt::CheckStateRole) - { - if(index.column() == LoadColumn && filter_) - return (filter_->isFiltered(item))?QVariant(Qt::Checked):QVariant(Qt::Unchecked); - - return QVariant(); - } - else if (role == Qt::FontRole) - { - if(index.column() != LoadColumn && index.column() != FavouriteColumn && - filter_ && filter_->isFiltered(item)) - return loadFont_; - - return QVariant(); - } - else if (role == IconStatusRole) - { - if(index.column() == SystemColumn) - return item->isSystem(); - - else if(index.column() == FavouriteColumn) - return item->isFavourite(); - - return QVariant(); - } - - else if(role == Qt::ToolTipRole) - { - if(item->isSystem() && (index.column() == SystemColumn || index.column() == NameColumn || - index.column() == HostColumn || index.column() == PortColumn)) - { - return "This server appears in the central system server list and its name, \ - host or port cannot be modified."; - } - - return QString(); - } - - return QVariant(); -} - -QVariant ServerListModel::headerData(int section,Qt::Orientation ori,int role) const -{ - if(ori != Qt::Horizontal) - { - return QVariant(); - } - - if(role == Qt::DisplayRole) - { - switch(section) - { - case LoadColumn: return tr("L"); - case NameColumn: return tr("Name"); - case HostColumn: return tr("Host"); - case PortColumn: return tr("Port"); - case SystemColumn: return tr("S"); - case FavouriteColumn: return tr("F"); - case UseColumn: return tr("Loaded"); - default: return QVariant(); - } - } - else if(role == Qt::ToolTipRole) - { - switch(section) - { - case LoadColumn: return tr("Indicates if the server is loaded in the current tab"); - case NameColumn: return tr("Server name is a freely customisable nickname. It is only used by the
      viewer
      ."); - case HostColumn: return tr("Hostname of the server"); - case PortColumn: return tr("Port number of the server"); - case SystemColumn: return tr("Indicates if a server appears in the centrally maintained system server list. \ -
      The name, host and port of these server entries cannot be edited."); - case FavouriteColumn: return tr("Indicates if a server is a favourite. Only favourite and loaded servers \ - are appearing in the server list under the Servers menu in the menubar"); - case UseColumn: return tr("Indicates the number of tabs where the server is loaded."); - default: return QVariant(); - } - } - else if(role == Qt::TextAlignmentRole) - { - return Qt::AlignCenter; - } - - - - return QVariant(); -} - -bool ServerListModel::setData(const QModelIndex& idx, const QVariant & value, int role ) -{ - if(filter_ && idx.column() == LoadColumn && role == Qt::CheckStateRole) - { - if(ServerItem* item=ServerList::instance()->itemAt(idx.row())) - { - bool checked=(value.toInt() == Qt::Checked)?true:false; - if(checked) - filter_->addServer(item); - else - filter_->removeServer(item); - - QModelIndex startIdx=index(idx.row(),0); - QModelIndex endIdx=index(idx.row(),columnCount()-1); - - Q_EMIT dataChanged(startIdx,endIdx); - return true; - } - } - - return false; -} - - -QModelIndex ServerListModel::index(int row, int column, const QModelIndex& /*parent*/) const -{ - return createIndex(row,column,static_cast(0)); -} - -QModelIndex ServerListModel::parent(const QModelIndex &) const -{ - return QModelIndex(); -} - -Qt::ItemFlags ServerListModel::flags ( const QModelIndex & index) const -{ - Qt::ItemFlags defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; - - if(filter_ && index.column() == LoadColumn) - { - defaultFlags=defaultFlags | Qt::ItemIsUserCheckable; - } - - return defaultFlags; -} - -ServerItem* ServerListModel::indexToServer(const QModelIndex& index) -{ - return ServerList::instance()->itemAt(index.row()); -} - -//====================================== -// -// ServerListFilterModel -// -//====================================== - -ServerListFilterModel::ServerListFilterModel(QObject *parent) : - QSortFilterProxyModel(parent), - filterFavourite_(false) -{ - -} - -void ServerListFilterModel::setFilterStr(QString t) -{ - QString newStr=t.simplified(); - if(newStr != filterStr_) - { - filterStr_=newStr; - invalidateFilter(); - } -} - -void ServerListFilterModel::setFilterFavourite(bool b) -{ - if(b != filterFavourite_) - { - filterFavourite_=b; - invalidateFilter(); - } -} - - -bool ServerListFilterModel::filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const -{ - if(filterFavourite_) - { - QModelIndex idxLoad = sourceModel()->index(sourceRow, ServerListModel::LoadColumn, sourceParent); - QModelIndex idxFav = sourceModel()->index(sourceRow, ServerListModel::FavouriteColumn, sourceParent); - if(!sourceModel()->data(idxFav,Qt::UserRole).toBool() && - sourceModel()->data(idxLoad,Qt::CheckStateRole).toInt() != Qt::Checked) - return false; - } - - if(filterStr_.isEmpty()) - return true; - - QModelIndex idxName = sourceModel()->index(sourceRow,ServerListModel::NameColumn, sourceParent); - QModelIndex idxHost= sourceModel()->index(sourceRow,ServerListModel::HostColumn, sourceParent); - - if(sourceModel()->data(idxName).toString().contains(filterStr_,Qt::CaseInsensitive) || - sourceModel()->data(idxHost).toString().contains(filterStr_,Qt::CaseInsensitive)) - return true; - - return false; - -} - -bool ServerListFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const -{ - if(left.column() == ServerListModel::SystemColumn || left.column() == ServerListModel::FavouriteColumn) - { - return left.data(ServerListModel::IconStatusRole).toBool() < - right.data(ServerListModel::IconStatusRole).toBool(); - } - - return QSortFilterProxyModel::lessThan(left,right); -} - - diff -Nru ecflow-4.9.0/Viewer/src/ServerListDialog.hpp ecflow-4.11.1/Viewer/src/ServerListDialog.hpp --- ecflow-4.9.0/Viewer/src/ServerListDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,175 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SERVERLISTDIALOG_HPP_ -#define SERVERLISTDIALOG_HPP_ - -#include -#include - -#include "ui_ServerListDialog.h" -#include "ui_ServerEditDialog.h" -#include "ui_ServerAddDialog.h" - -class ServerFilter; -class ServerItem; -class ServerListModel; -class ServerListFilterModel; - -class ServerDialogChecker -{ -protected: - explicit ServerDialogChecker(QString txt) : errorText_(txt) {} - - bool checkName(QString name,QString oriName=QString()); - bool checkHost(QString host); - bool checkPort(QString port); - void error(QString msg); - - QString errorText_; -}; - - -class ServerEditDialog : public QDialog, private Ui::ServerEditDialog, public ServerDialogChecker -{ -Q_OBJECT - -public: - ServerEditDialog(QString name,QString host, QString port,bool favourite,QWidget* parent=0); - - QString name() const; - QString host() const; - QString port() const; - bool isFavourite() const; - -public Q_SLOTS: - void accept(); - -private: - QString oriName_; - -}; - -class ServerAddDialog : public QDialog, private Ui::ServerAddDialog, public ServerDialogChecker -{ -Q_OBJECT - -public: - explicit ServerAddDialog(QWidget* parent=0); - - QString name() const; - QString host() const; - QString port() const; - bool addToView() const; - -public Q_SLOTS: - void accept(); -}; - - -class ServerListDialog : public QDialog, protected Ui::ServerListDialog -{ -Q_OBJECT - -public: - enum Mode {SelectionMode,ManageMode}; - - ServerListDialog(Mode,ServerFilter*,QWidget *parent=0); - ~ServerListDialog(); - - void showSysSyncLog(); - -public Q_SLOTS: - void accept(); - void reject(); - -protected Q_SLOTS: - void on_actionEdit_triggered(); - void on_actionAdd_triggered(); - void on_actionDuplicate_triggered(); - void on_actionDelete_triggered(); - void on_actionRescan_triggered(); - void on_serverView_doubleClicked(const QModelIndex& index); - void on_actionFavourite_triggered(bool checked); - void on_sysSyncTb_clicked(bool); - void on_sysSyncLogTb_toggled(bool); - void slotItemSelected(const QModelIndex&,const QModelIndex&); - void slotItemClicked(const QModelIndex&); - void slotFilter(QString); - void slotFilterFavourite(bool); - -protected: - void closeEvent(QCloseEvent*); - void editItem(const QModelIndex& index); - void duplicateItem(const QModelIndex& index); - void addItem(); - void removeItem(const QModelIndex& index); - void setFavouriteItem(const QModelIndex& index,bool b); - void checkActionState(); - void writeSettings(); - void readSettings(); - - ServerFilter* filter_; - ServerListModel* model_; - ServerListFilterModel* sortModel_; - Mode mode_; -}; - - -class ServerListModel : public QAbstractItemModel -{ -public: - explicit ServerListModel(ServerFilter*,QObject *parent=0); - ~ServerListModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - bool setData( const QModelIndex &, const QVariant &, int role = Qt::EditRole ); - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - Qt::ItemFlags flags ( const QModelIndex & index) const; - - void dataIsAboutToChange(); - void dataChangeFinished(); - ServerItem* indexToServer(const QModelIndex& index); - - enum Columns {LoadColumn=0, NameColumn=1, HostColumn=2, PortColumn=3, SystemColumn=4, FavouriteColumn=5, UseColumn=6}; - enum CustomItemRole {IconStatusRole = Qt::UserRole+1}; - -protected: - ServerFilter* filter_; - QPixmap favPix_; - QPixmap favEmptyPix_; - QPixmap sysPix_; - QFont loadFont_; -}; - -class ServerListFilterModel : public QSortFilterProxyModel -{ -public: - explicit ServerListFilterModel(QObject *parent=0); - ~ServerListFilterModel() {} - void setFilterStr(QString); - void setFilterFavourite(bool b); - -protected: - bool filterAcceptsRow(int sourceRow,const QModelIndex &sourceParent) const; - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - - QString filterStr_; - bool filterFavourite_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/ServerListDialog.ui ecflow-4.11.1/Viewer/src/ServerListDialog.ui --- ecflow-4.9.0/Viewer/src/ServerListDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,391 +0,0 @@ - - - ServerListDialog - - - - 0 - 0 - 599 - 661 - - - - Manage servers - - - - - - - - - - - - - Show favourite and loaded servers only - - - Show favourite servers only - - - Favourites and loaded - - - - :/viewer/favourite.svg:/viewer/favourite.svg - - - true - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - Show change log for <b>system servers</b> - - - - - - - :/viewer/info.svg:/viewer/info.svg - - - true - - - Qt::ToolButtonIconOnly - - - false - - - - - - - - - - - - 0 - 0 - - - - &Edit server - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - &Add server - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - - 0 - 0 - - - - &Duplicate server - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - &Remove server - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - Qt::Vertical - - - QSizePolicy::Preferred - - - - 20 - 20 - - - - - - - - System servers: - - - - - - - - 0 - 0 - - - - &Update from central list - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Vertical - - - - Qt::ActionsContextMenu - - - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - - :/viewer/images/add.svg:/viewer/images/add.svg - - - &Add server - - - Add server - - - Ctrl+N - - - - - - :/viewer/close.svg:/viewer/close.svg - - - &Remove server - - - Delete server - - - Del - - - - - - :/viewer/images/configure.svg:/viewer/images/configure.svg - - - &Edit server - - - Edit server properties - - - Ctrl+E - - - - - - :/viewer/images/reload.svg:/viewer/images/reload.svg - - - Rescan - - - Rescan network for servers - - - - - Du&plicate server - - - Duplicate server - - - Ctrl+D - - - - - true - - - - :/viewer/favourite.svg:/viewer/favourite.svg - - - Set as &favourite - - - Ctrl+B - - - buttonBox - - - - - ServerListSyncWidget - QWidget -
      ServerListSyncWidget.hpp
      - 1 -
      -
      - - - - buttonBox - accepted() - ServerListDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - ServerListDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/ServerList.hpp ecflow-4.11.1/Viewer/src/ServerList.hpp --- ecflow-4.9.0/Viewer/src/ServerList.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerList.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVERLIST_HPP_ -#define SERVERLIST_HPP_ - -#include -#include - -#include - -class ServerItem; -class ServerList; - - -class ServerListObserver -{ -public: - ServerListObserver() {} - virtual ~ServerListObserver() {} - virtual void notifyServerListChanged()=0; - virtual void notifyServerListFavouriteChanged(ServerItem*)=0; -}; - -class ServerListTmpItem -{ -public: - ServerListTmpItem() {} - ServerListTmpItem(const std::string& name,const std::string& host, const std::string& port) : - name_(name), host_(host), port_(port) {} - explicit ServerListTmpItem(ServerItem* item); - ServerListTmpItem(const ServerListTmpItem& o) : name_(o.name_), host_(o.host_), port_(o.port_) {} - - const std::string& name() const {return name_;} - const std::string& host() const {return host_;} - const std::string& port() const {return port_;} - -protected: - std::string name_; - std::string host_; - std::string port_; -}; - -class ServerListSyncChangeItem -{ -public: - enum ChangeType {AddedChange,RemovedChange, MatchChange, SetSysChange,UnsetSysChange}; - - ServerListSyncChangeItem(const ServerListTmpItem& sys,const ServerListTmpItem& local,ChangeType type) : - sys_(sys), local_(local), type_(type) {} - - const ServerListTmpItem& sys() const {return sys_;} - const ServerListTmpItem& local() const {return local_;} - ChangeType type() const {return type_;} - - ServerListTmpItem sys_; - ServerListTmpItem local_; - ChangeType type_; -}; - -class ServerList -{ -public: - int count() const {return static_cast(items_.size());} - ServerItem* itemAt(int); - ServerItem* find(const std::string& name); - ServerItem* find(const std::string& name, const std::string& host, const std::string& port); - - //Can be added or changed only via these static methods - ServerItem* add(const std::string&,const std::string&,const std::string&,bool,bool saveIt=true); - void remove(ServerItem*); - void reset(ServerItem*,const std::string& name,const std::string& host,const std::string& port); - void setFavourite(ServerItem*,bool); - - std::string uniqueName(const std::string&); - - void init(); - void save(); - void rescan(); - void syncSystemFile(); - bool hasSystemFile() const; - const std::vector& syncChange() const {return syncChange_;} - bool hasSyncChange() const {return !syncChange_.empty();} - QDateTime syncDate() const {return syncDate_;} - - void addObserver(ServerListObserver*); - void removeObserver(ServerListObserver*); - - static ServerList* instance(); - -protected: - ServerList() {} - ~ServerList() {} - - static ServerList* instance_; - - bool load(); - bool readRcFile(); - //bool readSystemFile(); - void clearSyncChange(); - bool checkItemToAdd(const std::string& name,const std::string& host,const std::string& port, - bool checkDuplicate,std::string& errStr); - - void broadcastChanged(); - void broadcastChanged(ServerItem*); - - std::vector items_; - std::string localFile_; - std::string systemFile_; - std::vector observers_; - std::vector syncChange_; - QDateTime syncDate_; -}; - - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerListSyncWidget.cpp ecflow-4.11.1/Viewer/src/ServerListSyncWidget.cpp --- ecflow-4.9.0/Viewer/src/ServerListSyncWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListSyncWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,233 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ServerListSyncWidget.hpp" - -#include - -#include "ServerFilter.hpp" -#include "ServerItem.hpp" -#include "ServerList.hpp" - -ServerListSyncWidget::ServerListSyncWidget(QWidget *parent) : QWidget(parent) -{ - setupUi(this); - - title_->setShowTypeTitle(false); - browser_->setProperty("log","1"); - - //Set the list - if(ServerList::instance()->syncDate().isValid()) - { - QColor col(39,49,101); - - if(ServerList::instance()->hasSyncChange()) - { - QString t="Your copy of the system server list was updated "; - t+=" at " + - ServerList::instance()->syncDate().toString("yyyy-MM-dd HH:mm") + - ". This is the list of changes:"; - - title_->showInfo(t); - - const std::vector& items=ServerList::instance()->syncChange(); - int matchCnt=0, addedCnt=0, setCnt=0, unsetCnt=0; - for(size_t i=0; i < items.size(); i++) - { - if(items[i]->type() == ServerListSyncChangeItem::MatchChange) - matchCnt++; - else if(items[i]->type() == ServerListSyncChangeItem::AddedChange) - addedCnt++; - else if(items[i]->type() == ServerListSyncChangeItem::SetSysChange) - setCnt++; - else if(items[i]->type() == ServerListSyncChangeItem::UnsetSysChange) - unsetCnt++; - } - - if(matchCnt > 0) - { - QListWidgetItem *item=new QListWidgetItem("Host/port changed (" + QString::number(matchCnt) + ")"); - item->setData(Qt::UserRole,ServerListSyncChangeItem::MatchChange); - typeList_->addItem(item); - } - if(unsetCnt > 0) - { - QListWidgetItem *item=new QListWidgetItem("Server removed (" + QString::number(unsetCnt) + ")"); - item->setData(Qt::UserRole,ServerListSyncChangeItem::UnsetSysChange); - typeList_->addItem(item); - } - if(addedCnt > 0) - { - QListWidgetItem *item=new QListWidgetItem("New server (" + QString::number(addedCnt) + ")"); - item->setData(Qt::UserRole,ServerListSyncChangeItem::AddedChange); - typeList_->addItem(item); - } - if(setCnt > 0) - { - QListWidgetItem *item=new QListWidgetItem("Marked as system (" + QString::number(setCnt) + ")"); - item->setData(Qt::UserRole,ServerListSyncChangeItem::SetSysChange); - typeList_->addItem(item); - } - - QFont f; - QFontMetrics fm(f); - typeList_->setFixedWidth(fm.width("Host/port changed (2222)")); - - connect(typeList_,SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this,SLOT(slotTypeChanged(QListWidgetItem*,QListWidgetItem*))); - - typeList_->setCurrentRow(0); - } - else - { - QString t="Your copy of the system server list was updated "; - t+=" at " + - ServerList::instance()->syncDate().toString("yyyy-MM-dd HH:mm") + - " but no changes were found."; - title_->showInfo(t); - typeList_->hide(); - browser_->hide(); - } - } - else - { - QString t="Your copy of the system server list has not been updated!"; - title_->showInfo(t); - typeList_->hide(); - browser_->hide(); - } -} - -ServerListSyncWidget::~ServerListSyncWidget() -{ -} - -void ServerListSyncWidget::slotTypeChanged(QListWidgetItem* item,QListWidgetItem*) -{ - if(!item) - return; - - const std::vector& items=ServerList::instance()->syncChange(); - ServerListSyncChangeItem::ChangeType type= - static_cast(item->data(Qt::UserRole).toInt()); - - QString s; - - for(size_t i=0; i < items.size(); i++) - { - if(items[i]->type() == type) - { - if(!s.isEmpty()) - s+="
      "; - - switch(type) - { - case ServerListSyncChangeItem::MatchChange: - s+=buildMatchChange(items[i]); - break; - case ServerListSyncChangeItem::UnsetSysChange: - s+=buildUnsetSysChange(items[i]); - break; - case ServerListSyncChangeItem::AddedChange: - s+=buildAddedChange(items[i]); - break; - case ServerListSyncChangeItem::SetSysChange: - s+=buildSetSysChange(items[i]); - break; - default: - break; - } - } - } - - browser_->setHtml(s); -} - -QString ServerListSyncWidget::buildMatchChange(ServerListSyncChangeItem *t) -{ - QString hostTxt=QString::fromStdString(t->local().host()); - QString portTxt=QString::fromStdString(t->local().port()); - - QColor col(0,80,50); - - if(t->sys().host() != t->local().host()) - { - hostTxt+=" -> " + QString::fromStdString(t->sys().host()); - hostTxt="" + hostTxt + ""; - } - if(t->sys().port() != t->local().port()) - { - portTxt+=" -> " + QString::fromStdString(t->sys().port()); - portTxt="" + portTxt + ""; - } - - QString s=buildTable(QString::fromStdString(t->local().name()),hostTxt,portTxt); - return s; -} - -QString ServerListSyncWidget::buildAddedChange(ServerListSyncChangeItem *t) -{ -#if 0 - QString s="New server added"; - s+="" + buildTable(QString::fromStdString(t->sys().name()), - QString::fromStdString(t->sys().host()), - QString::fromStdString(t->sys().port())) + ""; - -#endif - - QString s=buildTable(QString::fromStdString(t->sys().name()), - QString::fromStdString(t->sys().host()), - QString::fromStdString(t->sys().port())); - return s; -} - - -QString ServerListSyncWidget::buildSetSysChange(ServerListSyncChangeItem *t) -{ -#if 0 - QString s="Existing server marked as system"; - s+="" + buildTable(QString::fromStdString(t->sys().name()), - QString::fromStdString(t->sys().host()), - QString::fromStdString(t->sys().port())) + ""; -#endif - - QString s=buildTable(QString::fromStdString(t->sys().name()), - QString::fromStdString(t->sys().host()), - QString::fromStdString(t->sys().port())); - return s; -} - -QString ServerListSyncWidget::buildUnsetSysChange(ServerListSyncChangeItem *t) -{ -#if 0 - QString s="Existing server unmarked as system"; - s+="" + buildTable(QString::fromStdString(t->local().name()), - QString::fromStdString(t->local().host()), - QString::fromStdString(t->local().port())) + ""; -#endif - QString s=buildTable(QString::fromStdString(t->local().name()), - QString::fromStdString(t->local().host()), - QString::fromStdString(t->local().port())); - - return s; -} - - - -QString ServerListSyncWidget::buildTable(QString name,QString host,QString port) const -{ - QColor col(60,60,60); - return "" + - "" + - "" + - "" + - "" + - "
       Name:" + name + "
       Host:" + host + "
       Port:" + port + "
      "; -} diff -Nru ecflow-4.9.0/Viewer/src/ServerListSyncWidget.hpp ecflow-4.11.1/Viewer/src/ServerListSyncWidget.hpp --- ecflow-4.9.0/Viewer/src/ServerListSyncWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListSyncWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SERVERLISTSYNCWIDGET_HPP -#define SERVERLISTSYNCWIDGET_HPP - -#include - -#include - -#include "ui_ServerListSyncWidget.h" - -class ServerListSyncChangeItem; -class QListWidgetItem; - -class ServerListSyncWidget : public QWidget, protected Ui::ServerListSyncWidget -{ - Q_OBJECT - -public: - ServerListSyncWidget(QWidget *parent = 0); - ~ServerListSyncWidget(); - -protected Q_SLOTS: - void slotTypeChanged(QListWidgetItem* item,QListWidgetItem*); - -private: - void build(); - QString buildAddedChange(ServerListSyncChangeItem*); - QString buildMatchChange(ServerListSyncChangeItem*); - QString buildSetSysChange(ServerListSyncChangeItem *t); - QString buildUnsetSysChange(ServerListSyncChangeItem *t); - QString buildTable(QString name,QString host,QString port) const; -}; - -#endif // SERVERLISTSYNCWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/src/ServerListSyncWidget.ui ecflow-4.11.1/Viewer/src/ServerListSyncWidget.ui --- ecflow-4.9.0/Viewer/src/ServerListSyncWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerListSyncWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ - - - ServerListSyncWidget - - - - 0 - 0 - 640 - 401 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - - - - - - - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/ServerObserver.hpp ecflow-4.11.1/Viewer/src/ServerObserver.hpp --- ecflow-4.9.0/Viewer/src/ServerObserver.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerObserver.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SERVEROBSERVER_HPP_ -#define SERVEROBSERVER_HPP_ - -#include -#include "Aspect.hpp" - -class ServerHandler; -class VServerChange; - -class ServerObserver -{ -public: - ServerObserver() {} - virtual ~ServerObserver() {} - virtual void notifyDefsChanged(ServerHandler* server, const std::vector& a)=0; - virtual void notifyServerDelete(ServerHandler* server)=0; - virtual void notifyBeginServerClear(ServerHandler* server) {} - virtual void notifyEndServerClear(ServerHandler* server) {} - virtual void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - virtual void notifyEndServerScan(ServerHandler* server) {} - virtual void notifyServerConnectState(ServerHandler* server) {} - virtual void notifyServerActivityChanged(ServerHandler* server) {} - virtual void notifyServerSuiteFilterChanged(ServerHandler* server) {} - virtual void notifyEndServerSync(ServerHandler* server) {} -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.cpp ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.cpp --- ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,129 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ServerSettingsItemWidget.hpp" - -#include "Node.hpp" -#include "ServerHandler.hpp" -#include "VNode.hpp" - -#include - -#include - -//======================================================== -// -// ServerSettingsItemWidget -// -//======================================================== - -ServerSettingsItemWidget::ServerSettingsItemWidget(QWidget *parent) : QWidget(parent) -{ - setupUi(this); - - connect(buttonBox_,SIGNAL(clicked(QAbstractButton*)), - this,SLOT(slotClicked(QAbstractButton *))); - - QPushButton* applyPb=buttonBox_->button(QDialogButtonBox::Apply); - assert(applyPb); - applyPb->setEnabled(false); - - connect(editor_,SIGNAL(changed()), - this,SLOT(slotEditorChanged())); -} - -QWidget* ServerSettingsItemWidget::realWidget() -{ - return this; -} - -void ServerSettingsItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - clearContents(); - - info_=info; - - if(info_ && info_->isServer() && info_->server()) - { - editor_->edit(info_->server()->conf()->guiProp(), - QString::fromStdString(info_->server()->name())); - } - else - { - - } -} - -void ServerSettingsItemWidget::clearContents() -{ - InfoPanelItem::clear(); - //TODO: properly set gui state -} - -void ServerSettingsItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(ActiveChanged)) - { - if(active_) - { - editor_->setEnabled(true); - buttonBox_->setEnabled(true); - } - } - - if(flags.isSet(SuspendedChanged)) - { - if(active_) - { - if(suspended_) - { - editor_->setEnabled(false); - buttonBox_->setEnabled(false); - } - else - { - editor_->setEnabled(true); - buttonBox_->setEnabled(true); - } - } - } - -} - -void ServerSettingsItemWidget::slotEditorChanged() -{ - QPushButton* applyPb=buttonBox_->button(QDialogButtonBox::Apply); - assert(applyPb); - applyPb->setEnabled(true); -} - -void ServerSettingsItemWidget::slotClicked(QAbstractButton* button) -{ - if(!active_) - return; - - switch(buttonBox_->standardButton(button)) - { - case QDialogButtonBox::Apply: - { - if(editor_->applyChange()) - { - if(info_ && info_->server()) - info_->server()->conf()->saveSettings(); - } - } - break; - default: - break; - } -} - -static InfoPanelItemMaker maker1("server_settings"); diff -Nru ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.hpp ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.hpp --- ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SERVERSETTINGSITEMWIDGET_HPP_ -#define SERVERSETTINGSITEMWIDGET_HPP_ - -#include - -#include "InfoPanelItem.hpp" -#include "PropertyEditor.hpp" -#include "VInfo.hpp" - -#include "ui_ServerSettingsItemWidget.h" - -class QAbstractButton; - -class VNode; - -class ServerSettingsItemWidget : public QWidget, public InfoPanelItem, protected Ui::ServerSettingsItemWidget -{ -Q_OBJECT - -public: - explicit ServerSettingsItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - -protected Q_SLOTS: - void slotClicked(QAbstractButton* button); - void slotEditorChanged(); - -protected: - void updateState(const ChangeFlags&); -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.ui ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.ui --- ecflow-4.9.0/Viewer/src/ServerSettingsItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ServerSettingsItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ - - - ServerSettingsItemWidget - - - - 0 - 0 - 579 - 594 - - - - Form - - - - - - - - - Qt::Vertical - - - QDialogButtonBox::Apply|QDialogButtonBox::Reset - - - false - - - - - - - - PropertyEditor - QWidget -
      PropertyEditor.hpp
      - 1 -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/SessionDialog.cpp ecflow-4.11.1/Viewer/src/SessionDialog.cpp --- ecflow-4.9.0/Viewer/src/SessionDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include -#include - - -#include "SessionDialog.hpp" -#include "ui_SessionDialog.h" - -#include "DirectoryHandler.hpp" -#include "SessionRenameDialog.hpp" - - -SessionDialog::SessionDialog(QWidget *parent) : QDialog(parent) -{ - //ui->setupUi(this); - setupUi(this); - - refreshListOfSavedSessions(); - - - // what was saved last time? - std::string lastSessionName = SessionHandler::instance()->lastSessionName(); - int index = SessionHandler::instance()->indexFromName(lastSessionName); - if (index != -1) - savedSessionsList_->setCurrentRow(index); // select this one in the table - - - if (SessionHandler::instance()->loadLastSessionAtStartup()) - restoreLastSessionCb_->setCheckState(Qt::Checked); - else - restoreLastSessionCb_->setCheckState(Qt::Unchecked); - - - newButton_->setVisible(false); // XXX TODO: enable New Session functionality - - // ensure the correct state of the Save button - on_sessionNameEdit__textChanged(); - setButtonsEnabledStatus(); -} - -SessionDialog::~SessionDialog() -{ - //delete ui; -} - -void SessionDialog::refreshListOfSavedSessions() -{ - - //sessionsTable_->clearContents(); - savedSessionsList_->clear(); - - // get the list of existing sessions - int numSessions = SessionHandler::instance()->numSessions(); - for (int i = 0; i < numSessions; i++) - { - SessionItem *s = SessionHandler::instance()->sessionFromIndex(i); - addSessionToTable(s); - } -} - -void SessionDialog::addSessionToTable(SessionItem *s) -{ - QListWidgetItem *item = new QListWidgetItem(QString::fromStdString(s->name())); - //item->setFlags(Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); - //item->setCheckState(Qt::Unchecked); - savedSessionsList_->addItem(item); -/* - int lastRow = sessionsTable_->rowCount()-1; - sessionsTable_->insertRow(lastRow+1); - - QTableWidgetItem *nameItem = new QTableWidgetItem(QString::fromStdString(s->name())); - sessionsTable_->setItem(lastRow+1, 0, nameItem); -*/ -} - - -std::string SessionDialog::selectedSessionName() -{ - QListWidgetItem *ci = savedSessionsList_->currentItem(); - if (ci) - return ci->text().toStdString(); - else - return ""; -} - - - -// --------------------------------------------------------------------------------------------- -// setButtonsEnabledStatus -// - checks which session has been chosen and enables/disables the action buttons appropriately -// --------------------------------------------------------------------------------------------- - -void SessionDialog::setButtonsEnabledStatus() -{ - std::string name = selectedSessionName(); - - // if somehow no session is selected, then we need different logic - if (name.empty()) - { - cloneButton_ ->setEnabled(false); - deleteButton_ ->setEnabled(false); - renameButton_ ->setEnabled(false); - switchToButton_->setEnabled(false); - } - else - { - // the default session is special and cannot be deleted or renamed - bool enable = (name == "default") ? false : true; - deleteButton_ ->setEnabled(enable); - renameButton_ ->setEnabled(enable); - switchToButton_->setEnabled(true); // always available for a valid session - cloneButton_ ->setEnabled(true); - } -} - - -bool SessionDialog::validSaveName(const std::string &name) -{ -/* - QString boxName(QObject::tr("Save session")); - // name empty? - if (name.empty()) - { - QMessageBox::critical(0,boxName, tr("Please enter a name for the session")); - return false; - } - - - // is there already a session with this name? - bool sessionWithThisName = (SessionHandler::instance()->find(name) != NULL); - - if (sessionWithThisName) - { - QMessageBox::critical(0,boxName, tr("A session with that name already exists - please choose another name")); - return false; - } - else - { - return true; - }*/ - return true; -} - -void SessionDialog::on_sessionNameEdit__textChanged() -{ - // only allow to save a non-empty session name -// saveButton_->setEnabled(!sessionNameEdit_->text().isEmpty()); -} - -void SessionDialog::on_savedSessionsList__currentRowChanged(int currentRow) -{ - setButtonsEnabledStatus(); -} - -void SessionDialog::on_cloneButton__clicked() -{ - std::string sessionName = selectedSessionName(); - assert(!sessionName.empty()); // it should not be possible for the name to be empty - - SessionRenameDialog renameDialog; - renameDialog.exec(); - - int result = renameDialog.result(); - if (result == QDialog::Accepted) - { - std::string newName = renameDialog.newName(); - SessionHandler::instance()->copySession(sessionName, newName); - refreshListOfSavedSessions(); - } -} - -void SessionDialog::on_deleteButton__clicked() -{ - std::string sessionName = selectedSessionName(); - assert(!sessionName.empty()); // it should not be possible for the name to be empty - - QString message = tr("Are you sure that you want to delete the session '") + QString::fromStdString(sessionName) + tr("'' from disk?"); - if(QMessageBox::question(0,tr("Confirm: remove session"), - message, - QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Ok) - { - SessionHandler::instance()->remove(sessionName); - refreshListOfSavedSessions(); - } -} - - -void SessionDialog::on_renameButton__clicked() -{ - std::string sessionName = selectedSessionName(); - assert(!sessionName.empty()); // it should not be possible for the name to be empty - - SessionItem *item = SessionHandler::instance()->find(sessionName); - assert(item); // it should not be possible for the name to be empty - - SessionRenameDialog renameDialog; - renameDialog.exec(); - - int result = renameDialog.result(); - if (result == QDialog::Accepted) - { - std::string newName = renameDialog.newName(); - SessionHandler::instance()->rename(item, newName); - refreshListOfSavedSessions(); - } -} - - -void SessionDialog::on_switchToButton__clicked() -{ - std::string sessionName = selectedSessionName(); - assert(!sessionName.empty()); // it should not be possible for the name to be empty - - SessionItem *item = SessionHandler::instance()->find(sessionName); - assert(item); // it should not be possible for the name to be empty - - SessionHandler::instance()->current(item); // set this session as the current one - - if (restoreLastSessionCb_->checkState() == Qt::Checked) // save details of the selected session? - SessionHandler::instance()->saveLastSessionName(); - else - SessionHandler::instance()->removeLastSessionName(); // no, so we can delete the file - - accept(); // close the dialogue and continue loading the main user interface -} - - -void SessionDialog::on_saveButton__clicked() -{ -/* - std::string name = sessionNameEdit_->text().toStdString(); - - if (validSaveName(name)) - { - SessionItem* newSession = SessionHandler::instance()->copySession(SessionHandler::instance()->current(), name); - if (newSession) - { - //SessionHandler::instance()->add(name); - refreshListOfSavedSessions(); - SessionHandler::instance()->current(newSession); - QMessageBox::information(0,tr("Session"), tr("Session saved")); - } - else - { - QMessageBox::critical(0,tr("Session"), tr("Failed to save session")); - } - close(); - }*/ -} - -// called when the user clicks the Save button -//void SaveSessionAsDialog::accept() -//{ -// -//} diff -Nru ecflow-4.9.0/Viewer/src/SessionDialog.hpp ecflow-4.11.1/Viewer/src/SessionDialog.hpp --- ecflow-4.9.0/Viewer/src/SessionDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SESSIONDIALOG_HPP -#define SESSIONDIALOG_HPP - -#include -#include "ui_SessionDialog.h" - -#include "SessionHandler.hpp" - -namespace Ui { -class SessionDialog; -} - -class SessionDialog : public QDialog, protected Ui::SessionDialog -{ - Q_OBJECT - -public: - explicit SessionDialog(QWidget *parent = 0); - ~SessionDialog(); - -public Q_SLOTS: - void on_saveButton__clicked(); - void on_sessionNameEdit__textChanged(); - void on_savedSessionsList__currentRowChanged(int currentRow); - void on_cloneButton__clicked(); - void on_deleteButton__clicked(); - void on_renameButton__clicked(); - void on_switchToButton__clicked(); - -private: - //Ui::SaveSessionAsDialog *ui; - void addSessionToTable(SessionItem *s); - bool validSaveName(const std::string &name); - void refreshListOfSavedSessions(); - void setButtonsEnabledStatus(); - std::string selectedSessionName(); -}; - -#endif // SESSIONDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/src/SessionDialog.ui ecflow-4.11.1/Viewer/src/SessionDialog.ui --- ecflow-4.9.0/Viewer/src/SessionDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ - - - SessionDialog - - - - 0 - 0 - 400 - 300 - - - - ecFlowUI Session Manager - - - - - - - - - - - - - New - - - - - - - Delete - - - - - - - Rename - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Clone - - - - - - - - - - - Use this session without asking at startup - - - - - - - - - Start with this session - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel - - - - - - - - - - - buttonBox - accepted() - SessionDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - SessionDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/SessionHandler.cpp ecflow-4.11.1/Viewer/src/SessionHandler.cpp --- ecflow-4.9.0/Viewer/src/SessionHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,482 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include -#include -#include -#include - -#include "SessionHandler.hpp" -#include "DirectoryHandler.hpp" -#include "Str.hpp" -#include "UserMessage.hpp" -#include "ServerList.hpp" - - -SessionHandler* SessionHandler::instance_=0; - - -SessionItem::SessionItem(const std::string& name) : - name_(name) -{ - checkDir(); - isTemporary_ = false; - askToPreserveTemporarySession_ = true; -} - -SessionItem::~SessionItem() -{ - // temporary session? if so, remove the directory on exit - if (isTemporary_) - { - std::string msg; - DirectoryHandler::removeDir(dirPath_, msg); - } -} - -void SessionItem::checkDir() -{ - dirPath_ = SessionHandler::sessionDirName(name_); - DirectoryHandler::createDir(dirPath_); - - qtPath_= SessionHandler::sessionQtDirName(name_); - DirectoryHandler::createDir(qtPath_); -} - -std::string SessionItem::sessionFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "session.json"); -} - -std::string SessionItem::windowFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "window.conf"); -} - -std::string SessionItem::settingsFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "settings.json"); -} - -std::string SessionItem::recentCustomCommandsFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "recent_custom_commands.json"); -} - -std::string SessionItem::savedCustomCommandsFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "saved_custom_commands.json"); -} - -std::string SessionItem::textFilterFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "text_filters.json"); -} - -std::string SessionItem::serverFile(const std::string& serverName) const -{ - return DirectoryHandler::concatenate(dirPath_, serverName + ".conf.json"); -} - -std::string SessionItem::infoPanelDialogFile() const -{ - return DirectoryHandler::concatenate(dirPath_, "info_panel_dialog.json"); -} - -std::string SessionItem::qtDir() const -{ - return qtPath_; -} - -std::string SessionItem::qtSettingsFile(const std::string name) const -{ - return DirectoryHandler::concatenate(qtPath_, name + ".conf"); -} - -//================================================= -// -// SessionHandler -// -//================================================= - -SessionHandler::SessionHandler() : - current_(0) -{ - //The default must always be exist! - current_=add(defaultSessionName()); - loadedLastSessionName_ = false; - - readSessionListFromDisk(); - readLastSessionName(); -} - -SessionHandler::~SessionHandler() -{ - for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) - { - delete (*it); - } -} - - -SessionHandler* SessionHandler::instance() -{ - if(!instance_) - { - instance_=new SessionHandler(); - } - - return instance_; -} - -void SessionHandler::destroyInstance() -{ - if (instance_) - delete instance_; - - instance_ = 0; -} - -std::string SessionHandler::sessionDirName(const std::string &sessionName) -{ - return DirectoryHandler::concatenate(DirectoryHandler::configDir(), sessionName + ".session"); -} - -std::string SessionHandler::sessionQtDirName(const std::string &sessionName) -{ - std::string basedir = sessionDirName(sessionName); - return DirectoryHandler::concatenate(basedir, "qt"); -} - -SessionItem* SessionHandler::find(const std::string& name) -{ - for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) - { - if((*it)->name() == name) - return *it; - } - return NULL; - -} - - -// returns -1 if the session name is not found -int SessionHandler::indexFromName(const std::string& name) -{ - int n = 0; - for(std::vector::const_iterator it=sessions_.begin(); it != sessions_.end(); ++it) - { - if((*it)->name() == name) - return n; - n++; - } - return -1; -} - -void SessionHandler::readSessionListFromDisk() -{ - // get the list of existing sessions (by listing the directories) - std::string configDir = DirectoryHandler::configDir(); - std::string filter = ".*\\.session"; - std::vector dirs; - DirectoryHandler::findDirs(configDir, filter, dirs); - - // add each session to our list (but remove the .session first) - for(std::vector::const_iterator it=dirs.begin(); it != dirs.end(); ++it) - { - std::string dirName = (*it); - std::string toRemove = ".session"; - std::string toReplaceWith = ""; - ecf::Str::replace(dirName, toRemove, toReplaceWith); - add(dirName); - } -} - -bool SessionHandler::loadLastSessionAtStartup() -{ - // if there was a last session file, then it means the user wanted to load at startup - return loadedLastSessionName_; -} - - - -SessionItem* SessionHandler::add(const std::string& name) -{ - // only add if not already there - if (find(name) == NULL) - { - SessionItem *item=new SessionItem(name); - sessions_.push_back(item); - return item; - } - else - return NULL; -} - -void SessionHandler::remove(const std::string& sessionName) -{ - SessionItem *session = find(sessionName); - assert(session); - - remove(session); -} - -void SessionHandler::remove(SessionItem* session) -{ - std::vector::iterator it = std::find(sessions_.begin(), sessions_.end(), session); - assert(it != sessions_.end()); // session was not found - should not be possible - - - // remove the session's directory - std::string errorMessage; - bool ok = DirectoryHandler::removeDir(sessionDirName(session->name()), errorMessage); - - if (ok) - { - // remove it from our list - sessions_.erase(it); - } - else - { - UserMessage::message(UserMessage::ERROR, true, errorMessage); - } -} - - -void SessionHandler::rename(SessionItem* item, const std::string& newName) -{ - std::string errorMessage; - bool ok = DirectoryHandler::renameDir(sessionDirName(item->name()), sessionDirName(newName), errorMessage); - - if (ok) - { - item->name(newName); - } - else - { - UserMessage::message(UserMessage::ERROR, true, errorMessage); - } -} - - -void SessionHandler::current(SessionItem* item) -{ - if(std::find(sessions_.begin(),sessions_.end(),item) != sessions_.end()) - { - current_=item; - load(); - } -} - - -SessionItem* SessionHandler::current() -{ - return current_; -} - -void SessionHandler::save() -{ - if(current_) - { - //current_->save(); - } -} - -void SessionHandler::load() -{ - -} - - -bool SessionHandler::requestStartupViaSessionManager() -{ - char *sm = getenv("ECFUI_SESSION_MANAGER"); - if (sm) - return true; - else - return false; -} - - -void SessionHandler::setTemporarySessionIfReqested() -{ - char *sh = getenv("ECFUI_TEMP_SESSION_HOST"); - if (sh) - { - char *sp = getenv("ECFUI_TEMP_SESSION_PORT"); - if (sp) - { - // create a session name, likely to be unique - if it already exists in the list, then use that - char sessName[1024]; - sprintf(sessName, "temporary_%s_%s_%d", sh, sp, getpid()); - std::string sname(sessName); - SessionItem *si = instance()->add(sname); - if (!si) - si = instance()->find(sname); - - instance()->current(si); - si->temporary(true); - - char *sask = getenv("ECFUI_TEMP_SESSION_PRESERVE_CONFIRM"); - if (sask && !strcmp(sask, "no")) - si->askToPreserveTemporarySession(false); - - std::string templateName("ecflow_ui_test_session_template.json"); - instance()->createSessionDirWithTemplate(sname, templateName); - - // construct an alias for this server - char calias[1024]; - sprintf(calias, "%s:%s", sh, sp); - std::string alias(calias); - si->temporaryServerAlias(alias); - - // does this exact server already exist in the user's list? - std::string host(sh); - std::string port(sp); - if(ServerList::instance()->find(alias, host, port) == 0) - { - // no - add it, and make sure it's got a unique alias - std::string uniqueName = ServerList::instance()->uniqueName(alias); - ServerList::instance()->add(uniqueName, host, port, false, true); - } - } - } -} - - -void SessionHandler::saveLastSessionName() -{ - std::string configDir = DirectoryHandler::configDir(); - std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); - - // open the last_session.txt file and try to read it - std::ofstream out(lastSessionFilename.c_str()); - - if (out.good()) - { - // the file is a one-line file containing the name of the current session - std::string line = current()->name(); - out << line << std::endl; - } -} - -void SessionHandler::removeLastSessionName() -{ - std::string configDir = DirectoryHandler::configDir(); - std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); - - std::string errorMessage; - bool ok = DirectoryHandler::removeFile(lastSessionFilename, errorMessage); - - if (!ok) - { - UserMessage::message(UserMessage::ERROR, true, errorMessage); - } -} - - -void SessionHandler::readLastSessionName() -{ - std::string configDir = DirectoryHandler::configDir(); - std::string lastSessionFilename = DirectoryHandler::concatenate(configDir, "last_session.txt"); - - // open the last_session.txt file and try to read it - std::ifstream in(lastSessionFilename.c_str()); - - if (in.good()) - { - // the file is a one-line file containing the name of the session we want - std::string line; - if (getline(in, line)) - { - loadedLastSessionName_ = true; - lastSessionName_ = line; - } - else - lastSessionName_ = defaultSessionName(); - } - else - { - // could not read the file, so just use the default - lastSessionName_ = defaultSessionName(); - } - - - // set this session as the current one if it exists - SessionItem *item = find(lastSessionName_); - if (item) - { - current(item); - } - else - { - lastSessionName_ = defaultSessionName(); // the last session file contained the name of a non-existant session - } -} - - -bool SessionHandler::createSessionDirWithTemplate(const std::string &sessionName, const std::string &templateFile) -{ - // we want to: - // 1) create a new directory for the session - // 2) copy the session template JSON file across to the new dir - - std::string sessionDir = sessionDirName(sessionName); - bool created = DirectoryHandler::createDir(sessionDir); - - if (created) - { - std::string errorMsg; - std::string fullTemplatePath = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), templateFile); - std::string fullDestPath = DirectoryHandler::concatenate(sessionDir, "session.json"); - bool copied = DirectoryHandler::copyFile(fullTemplatePath, fullDestPath, errorMsg); - if (!copied) - { - UserMessage::message(UserMessage::ERROR, true, errorMsg); - return false; - } - } - else - { - UserMessage::message(UserMessage::ERROR, true, "Could not create temporary session directory: " + sessionDir); - return false; - } - - return true; -} - - -SessionItem *SessionHandler::copySession(SessionItem* source, std::string &destName) -{ - // the main work is to make a copy of the source session's directory (recursively) - std::string errorMessage; - std::string sourceSessionDir = sessionDirName(source->name()); - std::string destSessionDir = sessionDirName(destName); - bool ok = DirectoryHandler::copyDir(sourceSessionDir, destSessionDir, errorMessage); - if (ok) - { - // add it to our list - SessionItem *newItem = add(destName); - return newItem; - } - else - { - UserMessage::message(UserMessage::ERROR, true, errorMessage); - return NULL; - } -} - -SessionItem *SessionHandler::copySession(std::string &source, std::string &destName) -{ - SessionItem *sourceSession = find(source); - assert(sourceSession); - - return copySession(sourceSession, destName); -} - diff -Nru ecflow-4.9.0/Viewer/src/SessionHandler.hpp ecflow-4.11.1/Viewer/src/SessionHandler.hpp --- ecflow-4.9.0/Viewer/src/SessionHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,105 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SESSIONHANDLER_HPP_ -#define SESSIONHANDLER_HPP_ - -#include -#include - -class ServerItem; - - -class SessionItem -{ -public: - explicit SessionItem(const std::string&); - virtual ~SessionItem(); - - void name(const std::string& name) {name_ = name;} - const std::string& name() const {return name_;} - - std::string sessionFile() const; - std::string windowFile() const; - std::string settingsFile() const ; - std::string recentCustomCommandsFile() const ; - std::string savedCustomCommandsFile() const ; - std::string textFilterFile() const ; - std::string serverFile(const std::string& serverName) const; - std::string infoPanelDialogFile() const; - std::string qtDir() const; - std::string qtSettingsFile(const std::string name) const; - void temporary(bool t) {isTemporary_ = t;} - bool temporary() const {return isTemporary_;} - void temporaryServerAlias(const std::string &alias) {temporaryServerAlias_ = alias;} - std::string temporaryServerAlias() const {return temporaryServerAlias_;} - void askToPreserveTemporarySession(bool a) {askToPreserveTemporarySession_ = a;} - bool askToPreserveTemporarySession() {return askToPreserveTemporarySession_;} - -protected: - void checkDir(); - - std::string name_; - std::string dirPath_; - std::string qtPath_; - std::string temporaryServerAlias_; - bool isTemporary_; - bool askToPreserveTemporarySession_; -}; - -class SessionHandler -{ -public: - SessionHandler(); - ~SessionHandler(); - - SessionItem* add(const std::string&); - void remove(const std::string&); - void remove(SessionItem*); - void rename(SessionItem*, const std::string&); - void current(SessionItem*); - SessionItem* current(); - void save(); - void load(); - int numSessions() {return sessions_.size();} - SessionItem *find(const std::string&); - int indexFromName(const std::string&); - SessionItem *sessionFromIndex(int i) {return sessions_[i];} - SessionItem *copySession(SessionItem* source, std::string &destName); - SessionItem *copySession(std::string &source, std::string &destName); - bool createSessionDirWithTemplate(const std::string &sessionName, const std::string &templateFile); - void saveLastSessionName(); - void removeLastSessionName(); - bool loadLastSessionAtStartup(); - std::string lastSessionName() {return lastSessionName_;} - - - const std::vector& sessions() const {return sessions_;} - - static std::string sessionDirName(const std::string &sessionName); // static because they are called from the constructor - static std::string sessionQtDirName(const std::string &sessionName); // static because they are called from the constructor - static SessionHandler* instance(); - static void destroyInstance(); - static bool requestStartupViaSessionManager(); - static void setTemporarySessionIfReqested(); - -protected: - void readSessionListFromDisk(); - std::string defaultSessionName() {return "default";} - void readLastSessionName(); - - static SessionHandler* instance_; - - std::vector sessions_; - SessionItem* current_; - bool loadedLastSessionName_; - std::string lastSessionName_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/SessionRenameDialog.cpp ecflow-4.11.1/Viewer/src/SessionRenameDialog.cpp --- ecflow-4.9.0/Viewer/src/SessionRenameDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionRenameDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - -#include "SessionHandler.hpp" - -#include "SessionRenameDialog.hpp" -#include "ui_SessionRenameDialog.h" - -SessionRenameDialog::SessionRenameDialog(QWidget *parent) : QDialog(parent) -{ - setupUi(this); -} - -SessionRenameDialog::~SessionRenameDialog() -{ -} - - -void SessionRenameDialog::on_buttonBox__accepted() -{ - // store the name - newName_ = nameEdit_->text().toStdString(); - - - // check it does not clash with an existing session name - if (SessionHandler::instance()->find(newName_)) - { - QMessageBox::critical(0,tr("Rename session"), tr("A session with that name already exists - please choose another name")); - } - else - { - // close the dialogue - accept(); - } -} - -void SessionRenameDialog::on_buttonBox__rejected() -{ - // close the dialogue - reject(); -} diff -Nru ecflow-4.9.0/Viewer/src/SessionRenameDialog.hpp ecflow-4.11.1/Viewer/src/SessionRenameDialog.hpp --- ecflow-4.9.0/Viewer/src/SessionRenameDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionRenameDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SESSIONRENAMEDIALOG_HPP -#define SESSIONRENAMEDIALOG_HPP - -#include -#include "ui_SessionRenameDialog.h" - -#include "SessionHandler.hpp" - -namespace Ui { -class SessionRenameDialog; -} - -class SessionRenameDialog : public QDialog, protected Ui::SessionRenameDialog -{ - Q_OBJECT - -public: - explicit SessionRenameDialog(QWidget *parent = 0); - ~SessionRenameDialog(); - - std::string newName() {return newName_;}; - -public Q_SLOTS: - void on_buttonBox__accepted(); - void on_buttonBox__rejected(); - -private: - std::string newName_; - }; - -#endif // SESSIONRENAMEDIALOG_HPP diff -Nru ecflow-4.9.0/Viewer/src/SessionRenameDialog.ui ecflow-4.11.1/Viewer/src/SessionRenameDialog.ui --- ecflow-4.9.0/Viewer/src/SessionRenameDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SessionRenameDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ - - - SessionRenameDialog - - - - 0 - 0 - 284 - 129 - - - - Add variable - - - true - - - - - - - 0 - 0 - - - - New name: - - - - - - - - - &Name: - - - label_1 - - - - - - - false - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/ShellCommand.cpp ecflow-4.11.1/Viewer/src/ShellCommand.cpp --- ecflow-4.9.0/Viewer/src/ShellCommand.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ShellCommand.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "ShellCommand.hpp" - -#include -#include -#include - -#include - -#include "CommandOutputDialog.hpp" -#include "DirectoryHandler.hpp" -#include "CommandOutput.hpp" -#include "UiLog.hpp" -#include "VFile.hpp" - -//static std::vector commands; - -bool ShellCommand::envChecked_=false; -bool ShellCommand::envHasToBeSet_=false; - -ShellCommand::ShellCommand(const std::string& cmdStr,const std::string& cmdDefStr) : - QObject(0), - proc_(0), - commandDef_(QString::fromStdString(cmdDefStr)) -{ - QString cmdIn=QString::fromStdString(cmdStr); - - //A valid shell command must start with "sh " - if(!cmdIn.startsWith("sh ")) - { - deleteLater(); - return; - } - - command_=cmdIn.mid(3); - - proc_=new QProcess(this); - - //Check environment - only has to be once - if(!envChecked_) - { - envChecked_=true; - - QString exeDir=QString::fromStdString(DirectoryHandler::exeDir()); - - //If there is an exe dir we check if it is added to the PATH env - //variable - if(!exeDir.isEmpty()) - { - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString envPath=env.value("PATH"); - if(!envPath.contains(exeDir)) - envHasToBeSet_=true; - } - } - - //If the shell command runs ecflow_client it has to be in the PATH env variable. - //When we develop the ui it is not the case so we need to add its path to PATH - //whenever is possible. - if(command_.contains("ecflow_client") && envHasToBeSet_) - { - QString exeDir=QString::fromStdString(DirectoryHandler::exeDir()); - Q_ASSERT(!exeDir.isEmpty()); - QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); - QString envPath=env.value("PATH"); - env.insert("PATH",exeDir + ":" + envPath); - proc_->setProcessEnvironment(env); - } - - connect(proc_,SIGNAL(finished(int,QProcess::ExitStatus)), - this,SLOT(procFinished(int,QProcess::ExitStatus))); - - connect(proc_,SIGNAL(readyReadStandardOutput()), - this,SLOT(slotStdOutput())); - - connect(proc_,SIGNAL(readyReadStandardError()), - this,SLOT(slotStdError())); - - startTime_=QDateTime::currentDateTime(); - proc_->start("/bin/sh -c \"" + command_ + "\""); -} - -QString ShellCommand::command() const -{ - return command_; -} - -ShellCommand* ShellCommand::run(const std::string& cmd,const std::string& cmdDef) -{ - return new ShellCommand(cmd,cmdDef); -} - -void ShellCommand::procFinished(int exitCode, QProcess::ExitStatus exitStatus) -{ - if(item_) - { - if(exitCode == 0 && exitStatus == QProcess::NormalExit) - CommandOutputHandler::instance()->finished(item_); - else - CommandOutputHandler::instance()->failed(item_); - } - deleteLater(); - -} - -void ShellCommand::slotStdOutput() -{ - if(!item_) - { - item_=CommandOutputHandler::instance()->addItem(command_,commandDef_,startTime_); - } - Q_ASSERT(item_); - if(item_->isEnabled()) - { - QString txt=proc_->readAllStandardOutput(); - CommandOutputHandler::instance()->appendOutput(item_,txt); - } -} - -void ShellCommand::slotStdError() -{ - if(!item_) - { - item_=CommandOutputHandler::instance()->addItem(command_,commandDef_,startTime_); - } - Q_ASSERT(item_); - if(item_->isEnabled()) - { - QString txt=proc_->readAllStandardError(); - CommandOutputHandler::instance()->appendError(item_,txt); - } -} diff -Nru ecflow-4.9.0/Viewer/src/ShellCommand.hpp ecflow-4.11.1/Viewer/src/ShellCommand.hpp --- ecflow-4.9.0/Viewer/src/ShellCommand.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ShellCommand.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SHELLCOMMAND_HPP -#define SHELLCOMMAND_HPP - -#include -#include -#include - -#include -#include - -#include - -#include "CommandOutput.hpp" - -class ShellCommand : public QObject -{ - Q_OBJECT -public: - static ShellCommand* run(const std::string&,const std::string&); - - QString command() const; - QString commandDef() const {return commandDef_;} - QDateTime startTime() const {return startTime_;} - -protected Q_SLOTS: - void procFinished(int exitCode, QProcess::ExitStatus exitStatus); - void slotStdOutput(); - void slotStdError(); - -protected: - ShellCommand(const std::string&,const std::string&); - - QProcess *proc_; - QString command_; - QString commandDef_; - QDateTime startTime_; - CommandOutput_ptr item_; - static bool envChecked_; - static bool envHasToBeSet_; -}; - -#endif // SHELLCOMMAND_HPP diff -Nru ecflow-4.9.0/Viewer/src/Sound.cpp ecflow-4.11.1/Viewer/src/Sound.cpp --- ecflow-4.9.0/Viewer/src/Sound.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Sound.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "Sound.hpp" - -#include "DirectoryHandler.hpp" -#include "UiLog.hpp" -#include "VConfig.hpp" -#include "VConfigLoader.hpp" -#include "VProperty.hpp" - -#include -#include - -//#ifdef ECFLOW_QT5 -//#include -//#endif - - -#include -#include -#include - -Sound* Sound::instance_=NULL; - - -/* - if(sound) - { - const char *soundCmd = "play -q /usr/share/xemacs/xemacs-packages/etc/sounds/boing.wav"; - if (system(soundCmd)) - UiLog().dbg() << "ChangeNotify:add() could not play sound alert"; -*/ -//#ifdef ECFLOW_QT5 -// QSoundEffect effect(dialog_); -// effect.setSource(QUrl::fromLocalFile("file:/usr/share/xemacs/xemacs-packages/etc/sounds/boing.wav")); -// effect.setLoopCount(1); -// effect.setVolume(0.25f); -// effect.play(); -//#endif - - -Sound::Sound() : - prevPlayedAt_(0), - delay_(2), - prop_(NULL) -{ - formats_=".+\\.(wav|mp3|ogg|oga)"; - - sysDir_=DirectoryHandler::concatenate(DirectoryHandler::etcDir(), "sounds"); - - std::vector res; - - DirectoryHandler::findFiles(sysDir_,formats_,res); - - for(unsigned int i=0; i < res.size(); i++) - { - sysSounds_.push_back(res.at(i)); - } -} - -Sound* Sound::instance() -{ - if(!instance_) - instance_=new Sound(); - - return instance_; -} - -void Sound::playSystem(const std::string& fName,int loopCount) -{ - std::string fullName=DirectoryHandler::concatenate(sysDir_, fName); - play(fullName,loopCount); -} - -void Sound::play(const std::string& fName,int loopCount) -{ - assert(loopCount < 6); - - time_t t=time(NULL); - if(t < prevPlayedAt_+delay_) - return; - - if(currentCmd_.empty()) - { - - } - else - { - std::string cmd=currentCmd_; - boost::replace_first(cmd,"%FILE%",fName); - boost::replace_first(cmd,"%REPEAT%",boost::lexical_cast(loopCount-1)); - if(system(cmd.c_str())) - { - UiLog().dbg() << "Sound::play() could not play sound alert. Command: " << cmd; - } - } - - prevPlayedAt_=time(NULL); -} - -void Sound::setCurrentPlayer(const std::string& current) -{ - std::map::const_iterator it=players_.find(current); - if(it != players_.end()) - { - currentPlayer_=it->first; - currentCmd_=it->second; - } - else - assert(0); -} - -bool Sound::isSoundFile(const std::string& fName) const -{ - const boost::regex expr(formats_); - boost::smatch what; - if(boost::regex_match(fName, what,expr)) - return true; - return false; -} - -void Sound::load(VProperty* prop) -{ - UiLog().dbg() << "Sound:load() -- > begin"; - - if(prop->name() != "sound") - { - UiLog().err() << "Sound:load() -- > no property found!"; - return; - } - - Sound::instance_->prop_=prop; - - if(VProperty *pp=prop->findChild("players")) - { - Q_FOREACH(VProperty* p,pp->children()) - { - Sound::instance_->players_[p->strName()]=p->param("command").toStdString(); - } - } - - if(VProperty *pp=prop->findChild("player")) - { - Sound::instance_->setCurrentPlayer(pp->valueAsStdString()); - } - - UiLog().dbg() << "Sound:load() -- > end"; -} - -static SimpleLoader loaderSound("sound"); diff -Nru ecflow-4.9.0/Viewer/src/Sound.hpp ecflow-4.11.1/Viewer/src/Sound.hpp --- ecflow-4.9.0/Viewer/src/Sound.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Sound.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,54 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SOUND_HPP_ -#define SOUND_HPP_ - -#include -#include - -#include "VProperty.hpp" - -class Sound : public VPropertyObserver -{ -public: - static Sound* instance(); - - const std::vector& sysSounds() const {return sysSounds_;} - const std::string sysDir() const {return sysDir_;} - - void playSystem(const std::string&,int repeat); - void play(const std::string&,int repeat); - bool isSoundFile(const std::string& fName) const; - - void notifyChange(VProperty*) {}; - - //Called from VConfigLoader - static void load(VProperty* group); - -protected: - Sound(); - void setCurrentPlayer(const std::string&); - - static Sound* instance_; - - std::map players_; - std::string currentPlayer_; - std::string currentCmd_; - - std::string formats_; - std::string sysDir_; - std::vector sysSounds_; - time_t prevPlayedAt_; - int delay_; - VProperty* prop_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/StandardView.cpp ecflow-4.11.1/Viewer/src/StandardView.cpp --- ecflow-4.9.0/Viewer/src/StandardView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StandardView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,792 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "StandardView.hpp" - -#include "ExpandState.hpp" -#include "TreeNodeModel.hpp" -#include "TreeNodeViewDelegate.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include - -//#define _UI_STANDARDVIEW_DEBUG - -StandardView::StandardView(TreeNodeModel* model,QWidget* parent) : - AbstractNodeView(model,parent), - expandIndicatorBoxWidth_(20), - expandIndicatorWidth_(10) -{ - //Overwrite some base class values - drawConnector_=false; - indentation_=10; - - //This is needed for making the context menu work - setProperty("view","tree"); - - //we cannot call it from the constructor of the base class - //because it calls a pure virtual method - reset(); -} - -StandardView::~StandardView() -{ - -} - -//Creates and initialize the viewItem structure of the children of the element -// parentId: the items whose children are to be expanded -// recursiveExpanding: all the children will be expanded -// afterIsUninitialized: when we recurse from layout(-1) it indicates -// the items after 'i' are not yet initialized and need not to be moved - -void StandardView::layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated) -{ - //This is the root item. - if(parentId == -1) - { - rowCount_=0; - maxRowWidth_=0; - } - - QModelIndex parentIndex = (parentId < 0) ? root_ : modelIndex(parentId); - - if(parentId >=0 && !parentIndex.isValid()) - { - //modelIndex() should never return something invalid for the real items. - //This can happen if columncount has been set to 0. - //To avoid infinite loop we stop here. - return; - } - - int count=model_->rowCount(parentIndex); - bool expanding=true; - - //This is the root item. viewItems must be empty at this point. - if(parentId == -1) - { - Q_ASSERT(viewItems_.empty()); - Q_ASSERT(preAllocated == false); - viewItems_.resize(count); - afterIsUninitialized = true; //It can only be true when we expand from the root! - } - //The count of the stored children does not match the actual count - else if(viewItems_[parentId].total != (uint)count) - { - //Expand - if(!afterIsUninitialized) - { - //We called expandall for a non-root item. All the new items need must be - //already instered at this point. This is the duty of the caller routine. - //const int itemsCount = viewItems_.size(); - //if(recursiveExpanding) - if(preAllocated) - { - //We called expandAll() for a non-root item. All the needed items need must already be - //inserted at this point. This is the duty of the caller routine! - //When layout() is finished we need to adjust the parent of all the items - //after the insertion position. This is the duty of the caller routine. We - //have chosen this solution for performance reasons! - } - else - { - insertViewItems(parentId + 1, count, TreeNodeViewItem()); - } - } - //ExpandAll from the root - else if(count > 0) - viewItems_.resize(viewItems_.size() + count); - } - else - { - expanding=false; - } - - int first = parentId + 1; - int last = 0; - int children = 0; - int level=(parentId >=0?viewItems_[parentId].level+1:0); - TreeNodeViewItem *item=0; - - -#ifdef _UI_STANDARDVIEW_DEBUG - //if(parentId >=0) - // UiLog().dbg() << "layout parent=" << viewItems_[parentId].index.data().toString(); -#endif - - //Iterate through the direct children of parent item. At this point all the items - //needed in the loop below are pre-allocated but not yet initialised. - for(int i=first; i < first+count; i++) - { - QModelIndex currentIndex=model_->index(i-first,0,parentIndex); - - last = i + children; - item = &viewItems_[last]; - item->parentItem = parentId; - item->index=currentIndex; - item->hasMoreSiblings=(i < first+count-1); - item->level=level; - item->expanded = false; - item->total = 0; - - //We compute the size of the item. For attributes we delay the width computation until we - //actually paint them and we set their width to 300. - int w,h; - delegate_->sizeHint(currentIndex,w,h); - item->width=w; - item->height=h; - - int xp=leftMargin_+expandIndicatorBoxWidth_; // no indentation for the root - - if(parentId >=0) - { - //item->widestInSiblings=widest; - xp=viewItems_[parentId].x+indentation_+expandIndicatorBoxWidth_; - } - else - { - //item->widestInSiblings=item->width; - } - - item->x=xp; - - if(item->alignedRight() > maxRowWidth_) - maxRowWidth_=item->right(); - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " item=" << currentIndex.data().toString(); -#endif - //We need to expand the item - if(recursiveExpanding || isIndexExpanded(currentIndex)) - { - if(recursiveExpanding) - expandedIndexes.insert(currentIndex); - - item->expanded = true; - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " is expanded"; -#endif - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " before " << item->index.data().toString() << " total=" << item->total; -#endif - //Add the children to the layout - layout(last,recursiveExpanding,afterIsUninitialized,preAllocated); - - item = &viewItems_[last]; - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " after " << item->index.data().toString() << " total=" << item->total; -#endif - children+=item->total; - item->hasChildren = item->total > 0; - } - else - { - item->hasChildren = model_->hasChildren(currentIndex); - } - } - - if(!expanding) - return; // nothing changed - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " update parent total"; -#endif - - int pp=parentId; - while (pp > -1) - { - viewItems_[pp].total += count; - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " parent=" << viewItems_[pp].index.data().toString() << - // " total=" << viewItems_[pp].total; -#endif - - pp = viewItems_[pp].parentItem; - } -} - -//Paint the rows intersecting with the given region -void StandardView::paint(QPainter *painter,const QRegion& region) -{ - //Even though the viewport palette is set correctly at the - //beginning something sets it to another value. Here we try - //to detect it and correct the palette with the right colour. - if(expectedBg_.isValid()) - { - QPalette p=viewport()->palette(); - if(p.color(QPalette::Window) != expectedBg_) - { - p.setColor(QPalette::Window,expectedBg_); - viewport()->setPalette(p); - viewport()->update(); - expectedBg_=QColor(); - return; - } - } - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "StandardView::paint -->"; - //UiLog().dbg() << "sizeof(TreeNodeViewItem)=" << sizeof(TreeNodeViewItem); - //UiLog().dbg() << "region=" << region; -#endif - - int firstVisibleOffset=0; - - //The first visible item at the top of the viewport - int firstVisible=firstVisibleItem(firstVisibleOffset); -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "firstVisible " << firstVisible; -#endif - - if(firstVisible<0) - return; - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "scrollX" << horizontalScrollBar()->value() << " " << viewport()->width(); -#endif - - int xOffset=0; - if(horizontalScrollBar()->value() > 0) - { - xOffset=horizontalScrollBar()->value(); - painter->translate(-xOffset,0); - } - - const int itemsCount = viewItems_.size(); - const int viewportWidth = viewport()->width(); - QVector rects = region.rects(); - QVector drawn; - bool multipleRects = (rects.size() > 1); - - //Iterate through the rectangles in the region - for(int a = 0; a < rects.size(); ++a) - { - const QRect area = (multipleRects - ? QRect(0, rects.at(a).y(), viewportWidth, rects.at(a).height()) - : rects.at(a)); -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " area=" << area; -#endif - std::vector indentVec; - - if(drawConnector_) - { - //Initialise indentVec. For each indentation level it tells us if - //a connector line is to be drawn. Here we scan up to the - //toplevel item in the firstVisible item's branch. - indentVec=std::vector(1000,0); - if(firstVisible >0) - { - TreeNodeViewItem* item=&viewItems_[firstVisible]; - int level=item->level; - while(item->parentItem >= 0 && level >0) - { - TreeNodeViewItem* pt=&viewItems_[item->parentItem]; - if(item->hasMoreSiblings) - { - indentVec[item->level]=connectorPos(item); - } - UI_ASSERT(pt->level == level-1, "item->parentItem=" << item->parentItem << - " pt->level=" << pt->level << " level=" << level); - item=pt; - level--; - } - } - } - - int i = firstVisible; // the first item at the top of the viewport - int y = firstVisibleOffset; // we may only see part of the first item - - //start at the top of the viewport and iterate down through the update area - for (; i < itemsCount; i++) - { - int itemHeight=viewItems_[i].height; - - if(drawConnector_) - { - //Adjust indentVec - if(viewItems_[i].hasMoreSiblings) - { - indentVec[viewItems_[i].level]=connectorPos(&viewItems_[i]); - } - else - indentVec[viewItems_[i].level]=0; - } - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "row: " << i << " " << itemHeight; -#endif - //Try to find the first item int the current rect - if(y + itemHeight > area.top()) - break; - y += itemHeight; - } - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "y: " << y << " " << area.bottom(); -#endif - - //Paint the visible rows in the current rect - for (; i < itemsCount && y <= area.bottom(); i++) - { - if(!multipleRects || !drawn.contains(i)) - { - //Draw a whole row. It will update y,itemsInRow and indentVec!! - drawRow(painter,i,xOffset,y,indentVec); - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " row rendered - item=" << i << " y=" << y; -#endif - } - else - { - int rh=viewItems_[i].height; - y+=rh; -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " row skipped - item=" << i << " y=" << y; -#endif - } - - if(multipleRects) - drawn.append(i); - } - } -} - -//Draw a whole row -void StandardView::drawRow(QPainter* painter,int start,int xOffset,int& yp,std::vector& indentVec) -{ - TreeNodeViewItem* item=&(viewItems_[start]); - - //Get the rowheight - int rh=item->height; - - //See if there are no multiline items in this row - bool singleRow=delegate_->isSingleHeight(rh); - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " item=" << " " << item->index.data().toString(); -#endif - - //Find out the indentation level of the row - int firstLevel=item->level; - - //Init style option - QStyleOptionViewItem opt; - if(selectionModel_->isSelected(item->index)) - opt.state |= QStyle::State_Selected; - - int optWidth=2000; - if(item->width > optWidth) - optWidth=item->width; - opt.rect=QRect(item->x,yp,optWidth,item->height); - - //We do not render the item if it is outisde the viewport and - //its parent's right is also outside the viewport. Here we considered that - //the connector line is always drawn from the child to the parent. - bool needToDraw=true; -#if 0 - if(item->parentItem >=0) - { - if(viewItems_[item->parentItem].right() >= translation() + viewportWidth) - needToDraw=false; - } -#endif - if(needToDraw) - { - //For single rows we center items halfway through the rowHeight -#if 0 - if(singleRow) - { - if(item->height < rh) - { - opt.rect.moveTop(yp+(rh-item->height)/2); - } - } -#endif - - //QRect vr=visualRect(item->index); - //painter->fillRect(vr,QColor(120,120,120,120)); - -//#ifdef _UI_STANDARDVIEW_DEBUG -// UiLog().dbg() << " optRect=" << opt.rect << " visRect=" << vr; -//#endif - - //Draw the item with the delegate - QSize paintedSize; - delegate_->paint(painter,opt,item->index,paintedSize); - - //we have to know if the item width/height is the same that we expected. - //This can happen when: - // -we set a fixed initial width for the item (e.g. for an attribute) - // and now we got the real width - // -the number of icons or additional extra information - // changed for a node (so the width changed) - // -the number of lines changed in a multiline label (so the height changed) - // -the node becomes submitted - bool wChanged=paintedSize.width() != item->width; - bool hChanged=paintedSize.height() != item->height; - - if(wChanged || hChanged) - { - //set new size - item->width=paintedSize.width(); - item->height=paintedSize.height(); - - if(item->right() > maxRowWidth_) - { - maxRowWidth_=item->right(); - doDelayedWidthAdjustment(); - } - else if(hChanged) - { - doDelayedWidthAdjustment(); - } - } - - //draw expand indicator - if(item->hasChildren) - { - //We draw a triangle into the middle of the expand indicator box - float indX=item->x-expandIndicatorBoxWidth_/2; - float indY=yp+item->height/2; - - //painter->drawRect(QRect(indX-expandIndicatorBoxWidth_/2,indY-item->height/2, - // expandIndicatorBoxWidth_,expandIndicatorBoxWidth_)); - - float tw=expandIndicatorWidth_; - float th=tw/2.*0.95; - - QPolygonF shape; - if(item->expanded) - { - shape << QPointF(indX-tw/2,indY-0.2*th) << - QPointF(indX+tw/2,indY-0.2*th) << - QPointF(indX,indY+0.8*th); - } - else - { - shape << QPointF(indX,indY-tw/2.) << - QPointF(indX,indY+tw/2.) << - QPointF(indX+th,indY); - } - - QPen oriPen=painter->pen(); - painter->setPen(Qt::NoPen); - painter->setBrush(QColor(71,71,70)); - painter->setRenderHint(QPainter::Antialiasing,true); - painter->drawPolygon(shape); - painter->setRenderHint(QPainter::Antialiasing,false); - painter->setPen(oriPen); - } - - //Draw the connector lines - if(drawConnector_) - { - painter->setPen(connectorColour_); - - int lineX1=item->x-expandIndicatorBoxWidth_/2; - int lineY=yp+item->height/2; - - //For multiline labels the connector shoudl be close to the top - //not in the middle. We use the parentitem's height to find the proper - //y position. - if(!singleRow && item->parentItem >=0) - { - lineY=yp+viewItems_[item->parentItem].height/2; - } - - if(item->hasMoreSiblings) - { - indentVec[item->level]=lineX1; - } - else - indentVec[item->level]=0; - - //If not a top level item (e.i. not a server) and a leaf we need to draw - //a connector line straight to the item - if(item->parentItem >=0 && item->hasChildren == 0) - { - int lineX2=item->x-connectorGap_; - - //First child - if(item->index.row() == 0) - { - //horizontal line to the node - painter->drawLine(lineX1,lineY,lineX2,lineY); - - //vertical line to the parent - painter->drawLine(lineX1,lineY,lineX1,yp); - - //line towards the siblings - downwards - if(item->hasMoreSiblings) - { - painter->drawLine(lineX1,lineY,lineX1,yp+rh); - } - } - - //Child in the middle - has sibling both upwards and downwards - else if(item->hasMoreSiblings) - { - //horizontal line to the node - painter->drawLine(lineX1,lineY,lineX2,lineY); - - //vertical line to the parent and sibling - painter->drawLine(lineX1,yp,lineX1,yp+rh); - } - - //The last child - has sibling only upwards - else - { - //horizontal line to the node - painter->drawLine(lineX1,lineY,lineX2,lineY); - - //vertical line to the parent - painter->drawLine(lineX1,lineY,lineX1,yp); - } - } - - //Draw the vertical connector lines for all the levels - //preceding the first level in the row! - painter->setPen(connectorColour_); - for(int j=0; j < firstLevel; j++) - { - int xp=indentVec[j]; - if(xp != 0) - painter->drawLine(xp,yp,xp,yp+rh); - } - } - } - - yp+=rh; -} - -int StandardView::connectorPos(TreeNodeViewItem* item) const -{ - return item->x-expandIndicatorBoxWidth_/2; -} - -int StandardView::itemRow(int item) const -{ - return item; -} - -int StandardView::firstVisibleItem(int &offset) const -{ - const int value = verticalScrollBar()->value(); -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "CompactNodeView::firstVisibleItem --> value=" << value; -#endif - - if (verticalScrollMode_ == ScrollPerItem) - { - offset = 0; - //value is the row number - - if(value <0 || value >= rowCount_) - return -1; - - return value; - } - - return -1; -} - - -//This has to be very quick. Called after each collapse/expand. -void StandardView::updateRowCount() -{ - rowCount_=static_cast(viewItems_.size()); - -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "CompactNodeView::updateRowCount --> " << rowCount_; -#endif -} - -void StandardView::updateScrollBars() -{ -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << "CompactNodeView::updateScrollBars -->"; -#endif - - QSize viewportSize = viewport()->size(); - if(!viewportSize.isValid()) - viewportSize = QSize(0, 0); - - if(viewItems_.empty()) - { - //doItemsLayout(); - } - - int itemsInViewport = 0; - - const int itemsCount = viewItems_.size(); - if(itemsCount ==0) - return; - - const int viewportHeight = viewportSize.height(); - for(int height = 0, item = itemsCount - 1; item >= 0; item--) - { - //UiLog().dbg() << "item=" << item; - height +=viewItems_[item].height; - if(height > viewportHeight) - break; - itemsInViewport++; - } -#ifdef _UI_STANDARDVIEW_DEBUG - //UiLog().dbg() << " itemsCount=" << itemsCount << " rowCount=" << rowCount_; - //UiLog().dbg() << " itemsInViewport " << itemsInViewport; -#endif - - if(verticalScrollMode_ == ScrollPerItem) - { - if(!viewItems_.empty()) - itemsInViewport = qMax(1, itemsInViewport); - - //verticalScrollBar()->setRange(0, itemsCount - itemsInViewport); - verticalScrollBar()->setRange(0, rowCount_ - itemsInViewport); - verticalScrollBar()->setPageStep(itemsInViewport); - verticalScrollBar()->setSingleStep(1); - } - else - { - // scroll per pixel - } - - //Horizontal scrollbar - if(viewportSize.width() < maxRowWidth_) - { - horizontalScrollBar()->setRange(0,maxRowWidth_+10-viewportSize.width()); - horizontalScrollBar()->setPageStep(viewportSize.width()); - horizontalScrollBar()->setSingleStep(1); - } - else - { - horizontalScrollBar()->setRange(0,0); - } -} - -/* - Returns the rectangle on the viewport occupied by the item at \a index. - If the index is not visible or explicitly hidden, the returned rectangle is invalid. -*/ -QRect StandardView::visualRect(const QModelIndex &index) const -{ - //if (!d->isIndexValid(index) || isIndexHidden(index)) - // return QRect(); - - //d->executePostedLayout(); - - int vi = viewIndex(index); - if (vi < 0) - return QRect(); - - int y =coordinateForItem(vi); - if(y >=0) - { - //return QRect(viewItems_[vi].x, y, viewItems_[vi].width,rh); //TODO: optimise it - return QRect(viewItems_[vi].x-1-translation(), y, viewItems_[vi].width+2,viewItems_[vi].height); - } - return QRect(); -} - -//Returns the viewport y coordinate for item. -int StandardView::coordinateForItem(int item) const -{ - if(verticalScrollMode_ == ScrollPerItem) - { - int offset = 0; - //firstVisibleItem must always start a row!!!! - int topViewItemIndex=firstVisibleItem(offset); - if (item >= topViewItemIndex) - { - // search in the visible area first and continue down - // ### slow if the item is not visible - - const int itemsCount = viewItems_.size(); - const int viewportHeight = viewport()->size().height(); - - for(int height = 0, viewItemIndex = topViewItemIndex; - height <= viewportHeight && viewItemIndex < itemsCount; viewItemIndex++) - { - int h=viewItems_[viewItemIndex].height; - if(viewItemIndex ==item) - { - return height; - } - height +=h; - } - } - } - - - return -1; -} - -//coordinate is in viewport coordinates -int StandardView::itemAtCoordinate(const QPoint& coordinate) const -{ - const std::size_t itemCount = viewItems_.size(); - if(itemCount == 0) - return -1; - - if(verticalScrollMode_ == ScrollPerItem) - { - //int topRow = verticalScrollBar()->value(); - - int offset = 0; - int topViewItemIndex=firstVisibleItem(offset); - - if(coordinate.y() >= 0) - { - // the coordinate is in or below the viewport - int viewItemCoordinate = 0; - for(std::size_t viewItemIndex = topViewItemIndex; viewItemIndex < itemCount; viewItemIndex++) - { - viewItemCoordinate += viewItems_[viewItemIndex].height; - if (viewItemCoordinate > coordinate.y()) - { - //viewItemIndex=itemAtRowCoordinate(viewItemIndex,itemsInRow,coordinate.x()+translation()); - return (viewItemIndex >= itemCount ? -1 : viewItemIndex); - } - } - } - } - - return -1; -} - -bool StandardView::isPointInExpandIndicator(int item,QPoint p) const -{ - const int itemCount = static_cast(viewItems_.size()); - return item >=0 && item < itemCount && - p.x() > viewItems_[item].x-expandIndicatorBoxWidth_ && - p.x() < viewItems_[item].x-2; -} - -void StandardView::updateViewport(const QRect rect) -{ - if(rect.right() < viewport()->rect().right()) - viewport()->update(rect.adjusted(0,0,viewport()->rect().right()-rect.right(),0)); - else - viewport()->update(rect); -} diff -Nru ecflow-4.9.0/Viewer/src/StandardView.hpp ecflow-4.11.1/Viewer/src/StandardView.hpp --- ecflow-4.9.0/Viewer/src/StandardView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StandardView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef STANDARDVIEW_HPP -#define STANDARDVIEW_HPP - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "AbstractNodeView.hpp" - -class TreeNodeModel; -class GraphNodeViewItem; -class QStyledItemDelegate; - -//Implements a standard tree view (similar to QTreeView) where there is -//one item per row - -class StandardView : public AbstractNodeView -{ -public: - explicit StandardView(TreeNodeModel* model,QWidget *parent=0); - ~StandardView(); - - QRect visualRect(const QModelIndex &index) const; - -protected: - void paint(QPainter *painter,const QRegion& region); - void drawRow(QPainter* painter,int start,int xOffset,int &yp,std::vector&); - - void layout(int parentId, bool recursiveExpanding,bool afterIsUninitialized,bool preAllocated); - - int itemRow(int item) const; - int coordinateForItem(int item) const; - int itemAtCoordinate(const QPoint& coordinate) const; - bool isPointInExpandIndicator(int,QPoint) const; - - int firstVisibleItem(int &offset) const; - void updateRowCount(); - void updateScrollBars(); - void updateViewport(const QRect rect); - - int expandIndicatorBoxWidth_; - int expandIndicatorWidth_; - -private: - int connectorPos(TreeNodeViewItem* item) const; -}; - -#endif // STANDARDVIEW_HPP diff -Nru ecflow-4.9.0/Viewer/src/StringMatchCombo.cpp ecflow-4.11.1/Viewer/src/StringMatchCombo.cpp --- ecflow-4.9.0/Viewer/src/StringMatchCombo.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StringMatchCombo.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "StringMatchCombo.hpp" - -#include -#include - -StringMatchTb::StringMatchTb(QWidget* parent) : QToolButton(parent) -{ - QIcon ic(QPixmap(":/viewer/edit.svg")); - setIcon(ic); - setAutoRaise(true); - QMenu *menu=new QMenu(this); - menu->addAction("Contains"); //,StringMatchMode::ContainsMatch); - menu->addAction("Matches"); //,StringMatchMode::WildcardMatch); - menu->addAction("Regexp"); //,StringMatchMode::RegexpMatch); - setMenu(menu); - setPopupMode(QToolButton::InstantPopup); -} - -StringMatchCombo::StringMatchCombo(QWidget* parent) : QComboBox(parent) -{ - addItem("contains",StringMatchMode::ContainsMatch); - addItem("matches",StringMatchMode::WildcardMatch); - addItem("regexp",StringMatchMode::RegexpMatch); - - setCurrentIndex(1); -} - -StringMatchMode::Mode StringMatchCombo::matchMode(int index) const -{ - if(index >=0 && index < count()) - { - return static_cast(itemData(index).toInt()); - } - return StringMatchMode::WildcardMatch; -} - -StringMatchMode::Mode StringMatchCombo::currentMatchMode() const -{ - return matchMode(currentIndex()); -} - -void StringMatchCombo::setMatchMode(const StringMatchMode& mode) -{ - int im=mode.toInt(); - for(int i=0; i < count(); i++) - { - if(itemData(i).toInt() == im) - { - setCurrentIndex(i); - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/StringMatchCombo.hpp ecflow-4.11.1/Viewer/src/StringMatchCombo.hpp --- ecflow-4.9.0/Viewer/src/StringMatchCombo.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StringMatchCombo.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,37 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_STRINGMATCHCOMBO_HPP_ -#define VIEWER_SRC_STRINGMATCHCOMBO_HPP_ - -#include -#include - -#include "StringMatchMode.hpp" - -class StringMatchTb : public QToolButton -{ -public: - StringMatchTb(QWidget* parent=0); -}; - -class StringMatchCombo : public QComboBox -{ -Q_OBJECT - -public: - explicit StringMatchCombo(QWidget* parent=0); - - StringMatchMode::Mode matchMode(int) const; - StringMatchMode::Mode currentMatchMode() const; - void setMatchMode(const StringMatchMode& mode); -}; - -#endif /* VIEWER_SRC_STRINGMATCHCOMBO_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/StringMatchMode.cpp ecflow-4.11.1/Viewer/src/StringMatchMode.cpp --- ecflow-4.9.0/Viewer/src/StringMatchMode.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StringMatchMode.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,56 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - - -#include "StringMatchMode.hpp" - -std::map StringMatchMode::matchOper_; - -StringMatchMode::StringMatchMode() : - mode_(WildcardMatch) -{ - init(); -} - -StringMatchMode::StringMatchMode(Mode mode) : - mode_(mode) -{ - init(); -} - -void StringMatchMode::init() -{ - if(matchOper_.empty()) - { - matchOper_[ContainsMatch]="~"; - matchOper_[WildcardMatch]="="; - matchOper_[RegexpMatch]="=~"; - } -} - -const std::string& StringMatchMode::matchOperator() const -{ - static std::string emptyStr; - std::map::const_iterator it=matchOper_.find(mode_); - if(it != matchOper_.end()) - return it->second; - - return emptyStr; -} - -StringMatchMode::Mode StringMatchMode::operToMode(const std::string& op) -{ - for(std::map::const_iterator it=matchOper_.begin(); it != matchOper_.end(); ++it) - { - if(op == it->second) - return it->first; - } - return InvalidMatch; -} diff -Nru ecflow-4.9.0/Viewer/src/StringMatchMode.hpp ecflow-4.11.1/Viewer/src/StringMatchMode.hpp --- ecflow-4.9.0/Viewer/src/StringMatchMode.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/StringMatchMode.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_STRINGMATCHMODE_HPP_ -#define VIEWER_SRC_STRINGMATCHMODE_HPP_ - -#include -#include - -class StringMatchMode -{ -public: - enum Mode {InvalidMatch=-1,ContainsMatch=0,WildcardMatch=1,RegexpMatch=2}; - - StringMatchMode(); - StringMatchMode(Mode m); - StringMatchMode(const StringMatchMode& r) {mode_=r.mode_;} - - Mode mode() const {return mode_;} - void setMode(Mode m) {mode_=m;} - const std::string& matchOperator() const; - int toInt() const {return static_cast(mode_);} - - static Mode operToMode(const std::string&); - -private: - void init(); - - Mode mode_; - static std::map matchOper_; -}; - - -#endif /* VIEWER_SRC_STRINGMATCHMODE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/SuiteFilter.cpp ecflow-4.11.1/Viewer/src/SuiteFilter.cpp --- ecflow-4.9.0/Viewer/src/SuiteFilter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteFilter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,382 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "SuiteFilter.hpp" - -#include "SuiteFilterObserver.hpp" -#include "UserMessage.hpp" -#include "VSettings.hpp" -#include "VProperty.hpp" - -#include -#include - -#define _UI_SUITEFILTER_DEBUG - -std::string SuiteFilter::dummySuite_="__DUMMY_FOR_UI__"; - -//================================================================= -// -// SuiteFilterItem -// -//================================================================= - -SuiteFilterItem::SuiteFilterItem(const SuiteFilterItem& other) : - name_(other.name_), - loaded_(other.loaded_), - filtered_(other.filtered_) -{ -} - -//================================================================= -// -// SuiteFilter -// -//================================================================= - -SuiteFilter::~SuiteFilter() -{ - std::vector obsCopy=observers_; - for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) - { - (*it)->notifyDelete(this); - } -} - -void SuiteFilter::clear() -{ - items_.clear(); - broadcastChange(); -} - -void SuiteFilter::adjustLoaded(const std::vector& loaded) -{ - bool changed=false; - - std::vector currentLoaded; - for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) - { - bool ld=(std::find(loaded.begin(), loaded.end(),(*it).name_) != loaded.end()); - if((*it).loaded_ != ld) - { - (*it).loaded_=ld; - if(ld && - loadedInitialised_ && enabled_ && autoAddNew_) - { - (*it).filtered_=true; - } - changed=true; - } - if(ld) - currentLoaded.push_back((*it).name_); - } - - - //TODO: do we need to check enabled_ - for(std::vector::const_iterator it=loaded.begin(); it != loaded.end(); ++it) - { - if(std::find(currentLoaded.begin(), currentLoaded.end(),*it) == currentLoaded.end()) - { - bool filtered=loadedInitialised_ && enabled_ && autoAddNew_; - items_.push_back(SuiteFilterItem(*it,true,filtered)); - changed=true; - } - } - - - if(!loadedInitialised_ && loaded.size() > 0) - loadedInitialised_=true; - - if(changed) - broadcastChange(); -} - -std::vector SuiteFilter::loaded() const -{ - std::vector fv; - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it).loaded_) - fv.push_back((*it).name()); - } - return fv; -} - - -void SuiteFilter::adjustFiltered(const std::vector& filtered) -{ - bool changed=false; - - std::vector currentFiltered; - for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) - { - bool ld=(std::find(filtered.begin(), filtered.end(),(*it).name_) != filtered.end()); - if((*it).filtered_ != ld) - { - (*it).filtered_=ld; - changed=true; - } - if(ld) - currentFiltered.push_back((*it).name_); - } - - for(std::vector::const_iterator it=filtered.begin(); it != filtered.end(); ++it) - { - if(std::find(currentFiltered.begin(), currentFiltered.end(),*it) == currentFiltered.end()) - { - items_.push_back(SuiteFilterItem(*it,false,true)); - changed=true; - } - } - - if(changed) - broadcastChange(); -} - -std::vector SuiteFilter::filter() const -{ - std::vector fv; - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it).filtered_) - fv.push_back((*it).name()); - } - return fv; -} - -void SuiteFilter::setFiltered(int index,bool val) -{ - Q_ASSERT(index >=0 && index < count()); - items_[index].filtered_=val; -} - - -bool SuiteFilter::isOnlyOneFiltered(const std::string& oneSuite) const -{ - const std::vector& current=filter(); - return (current.size() == 1 && current[0] != oneSuite); -} - -void SuiteFilter::selectOnlyOne(const std::string& oneSuite) -{ - if(isOnlyOneFiltered(oneSuite)) - return; - - unselectAll(); - for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) - { - if((*it).name() == oneSuite) - { - (*it).filtered_=true; - return; - } - } -} - -SuiteFilter* SuiteFilter::clone() -{ - SuiteFilter* sf=new SuiteFilter(); - sf->items_=items_; - sf->enabled_=enabled_; - sf->autoAddNew_=autoAddNew_; - - return sf; -} - -bool SuiteFilter::loadedSameAs(const std::vector& loaded) const -{ - int cnt=0; - - for(std::vector::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - bool ld=(std::find(loaded.begin(), loaded.end(),(*it).name()) != loaded.end()); - if((*it).loaded() != ld) - return false; - else if(ld && (*it).loaded()) - cnt++; - } - - return cnt == static_cast(loaded.size()); -} - -bool SuiteFilter::setLoaded(const std::vector& loaded,bool checkDiff) -{ - bool same=false; - if(checkDiff) - same=loadedSameAs(loaded); - - if(!checkDiff || !same) - { - adjustLoaded(loaded); - return true; - } - - return !same; -} - -bool SuiteFilter::sameAs(const SuiteFilter* sf) const -{ - if(autoAddNew_ != sf->autoAddNewSuites()) - return false; - - if(enabled_ != sf->isEnabled()) - return false; - - if(sf->count() != count()) - return false; - - for(size_t i=0; i < items_.size(); i++) - { - if(items_[i] != sf->items()[i]) - { - return false; - } - } - - return true; -} - -bool SuiteFilter::update(SuiteFilter* sf) -{ - changeFlags_.clear(); - - assert(sf); - - if(autoAddNew_ != sf->autoAddNewSuites()) - { - autoAddNew_ = sf->autoAddNewSuites(); - changeFlags_.set(AutoAddChanged); - } - - if(enabled_ != sf->isEnabled()) - { - enabled_ = sf->isEnabled(); - changeFlags_.set(EnabledChanged); - } - - if(sf->count() != count()) - { - items_=sf->items(); - changeFlags_.set(ItemChanged); - } - else - { - for(size_t i=0; i < items_.size(); i++) - { - if(items_[i] != sf->items()[i]) - { - items_=sf->items(); - changeFlags_.set(ItemChanged); - break; - } - } - } - - return (changeFlags_.isEmpty() == false); -} - -void SuiteFilter::selectAll() -{ - for(size_t i=0; i < items_.size(); i++) - { - items_[i].filtered_=true; - } -} - -void SuiteFilter::unselectAll() -{ - for(size_t i=0; i < items_.size(); i++) - { - items_[i].filtered_=false; - } -} - -bool SuiteFilter::removeUnloaded() -{ - std::vector v; - - bool changed=false; - for(size_t i=0; i < items_.size(); i++) - { - if(items_[i].loaded()) - { - v.push_back(items_[i]); - } - else - { - changed=true; - } - } - - if(changed) - items_=v; - - return changed; -} - -bool SuiteFilter::hasUnloaded() const -{ - for(size_t i=0; i < items_.size(); i++) - { - if(!items_[i].loaded()) - return true; - } - return false; -} - -void SuiteFilter::addObserver(SuiteFilterObserver* o) -{ - assert(o); - - std::vector::const_iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - observers_.push_back(o); -} - -void SuiteFilter::removeObserver(SuiteFilterObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - observers_.erase(it); -} - -void SuiteFilter::broadcastChange() -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - { - (*it)->notifyChange(this); - } -} - -void SuiteFilter::readSettings(VSettings *vs) -{ - enabled_=vs->getAsBool("enabled",enabled_); - autoAddNew_=vs->getAsBool("autoAddNew",autoAddNew_); - - std::vector filter; - if(vs->contains("suites")) - { - vs->get("suites",filter); - } - - adjustFiltered(filter); - - changeFlags_.clear(); - changeFlags_.set(ItemChanged); -} - -void SuiteFilter::writeSettings(VSettings *vs) -{ - vs->putAsBool("enabled",enabled_); - vs->putAsBool("autoAddNew",autoAddNew_); - - std::vector fv=filter(); - if(fv.size() > 0) - vs->put("suites",fv); -} diff -Nru ecflow-4.9.0/Viewer/src/SuiteFilter.hpp ecflow-4.11.1/Viewer/src/SuiteFilter.hpp --- ecflow-4.9.0/Viewer/src/SuiteFilter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteFilter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef SUITEFILTER_HPP_ -#define SUITEFILTER_HPP_ - -#include -#include - -#include "FlagSet.hpp" - -class SuiteFilter; -class SuiteFilterObserver; -class VSettings; - -#if 0 -template -class FlagSet -{ -public: - FlagSet() : flags_(0) {} - - void clear() {flags_=0;} - void set(T flag ) { flags_ |= (1 << flag); } - void unset(T flag ) { flags_ &= ~ (1 << flag); } - bool isSet(T flag) const { return (flags_ >> flag) & 1; } - bool isEmpty() const {return flags_==0;} - bool sameAs(T flag) const {return flags_ == flag;} - -private: - int flags_; - -}; - -#endif - -class SuiteFilterItem -{ - friend class SuiteFilter; -public: - SuiteFilterItem(const std::string& name,bool loaded, bool filtered) : - name_(name), loaded_(loaded), filtered_(filtered) {} - - SuiteFilterItem(const SuiteFilterItem& other); - - bool operator!=(const SuiteFilterItem& rhs) const {return name_ != rhs.name_ || loaded_ != rhs.loaded_ || - filtered_ != rhs.filtered_;} - - const std::string& name() const {return name_;} - bool loaded() const {return loaded_;} - bool filtered() const {return filtered_;} - -protected: - std::string name_; - bool loaded_; - bool filtered_; -}; - -class SuiteFilter -{ -public: - SuiteFilter() : autoAddNew_(false), enabled_(false), loadedInitialised_(false) {} - ~SuiteFilter(); - - enum ChangeFlag {AutoAddChanged=1,EnabledChanged=2,ItemChanged=4}; - - SuiteFilter* clone(); - - std::vector filter() const; - std::vector loaded() const; - const std::vector& items() const {return items_;} - - void current(const std::vector& suites); - int count() const {return static_cast(items_.size());} - void setFiltered(int index,bool val); - bool isOnlyOneFiltered(const std::string& oneSuite) const; - bool isLoadedInitialised() const {return loadedInitialised_;} - bool autoAddNewSuites() const {return autoAddNew_;} - bool isEnabled() const {return enabled_;} - - void setLoadedInitialised(bool b) {loadedInitialised_=b;} - void setAutoAddNewSuites(bool b) {autoAddNew_=b;} - void setEnabled(bool b) {enabled_=b;} - void selectOnlyOne(const std::string& oneSuite); - void selectAll(); - void unselectAll(); - bool removeUnloaded(); - bool hasUnloaded() const; - - bool sameAs(const SuiteFilter*) const; - bool update(SuiteFilter*); - bool setLoaded(const std::vector& loaded,bool checkDiff=true); - bool loadedSameAs(const std::vector& loaded) const; - const FlagSet& changeFlags() {return changeFlags_;} - - bool hasObserver() const {return !observers_.empty();} - void addObserver(SuiteFilterObserver*); - void removeObserver(SuiteFilterObserver*); - - void readSettings(VSettings *vs); - void writeSettings(VSettings *vs); - - static const std::string dummySuite() {return dummySuite_;} - -private: - void clear(); - void adjust(); - void broadcastChange(); - void adjustLoaded(const std::vector& loaded); - void adjustFiltered(const std::vector& filtered); - - std::vector items_; - bool autoAddNew_; - bool enabled_; - bool loadedInitialised_; - FlagSet changeFlags_; - std::vector observers_; - static std::string dummySuite_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/SuiteFilterObserver.hpp ecflow-4.11.1/Viewer/src/SuiteFilterObserver.hpp --- ecflow-4.9.0/Viewer/src/SuiteFilterObserver.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteFilterObserver.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ -#define VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ - -class SuiteFilter; - -class SuiteFilterObserver -{ -public: - SuiteFilterObserver() {}; - virtual ~SuiteFilterObserver() {}; - - virtual void notifyChange(SuiteFilter*)=0; - virtual void notifyDelete(SuiteFilter*)=0; -}; -#endif /* VIEWER_SRC_SUITEFILTEROBSERVER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/SuiteItemWidget.cpp ecflow-4.11.1/Viewer/src/SuiteItemWidget.cpp --- ecflow-4.9.0/Viewer/src/SuiteItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,336 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "SuiteItemWidget.hpp" - -#include - -#include "InfoProvider.hpp" -#include "ServerHandler.hpp" -#include "SuiteFilter.hpp" -#include "SuiteModel.hpp" -#include "VNode.hpp" -#include "VReply.hpp" - -//======================================================== -// -// SuiteItemWidget -// -//======================================================== - -SuiteItemWidget::SuiteItemWidget(QWidget *parent) : - QWidget(parent), - columnsAdjusted_(false) -{ - setupUi(this); - - infoProvider_=new SuiteProvider(this); - - model_=new SuiteModel(this); - QSortFilterProxyModel* sortModel=new QSortFilterProxyModel(this); - sortModel->setSourceModel(model_); - - suiteView->setUniformRowHeights(true); - suiteView->setSortingEnabled(true); - suiteView->setModel(sortModel); - - connect(model_,SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this,SLOT(slotModelEdited(QModelIndex,QModelIndex))); - - messageLabel->setShowTypeTitle(false); - messageLabel->setNarrowMode(true); - messageLabel->hide(); - - QFont labelF; - labelF.setBold(true); - labelF.setPointSize(labelF.pointSize()-1); - - controlLabel->setFont(labelF); - controlLabel->setText("" + controlLabel->text() + ""); - - selectLabel->setFont(labelF); - selectLabel->setText("" + selectLabel->text() + ""); - - loadedLabel->setFont(labelF); - loadedLabel->setText("" + loadedLabel->text() + ""); - - QPalette pal=okTb->palette(); - QColor col(10,150,10); - pal.setColor(QPalette::Active,QPalette::Button,col); - pal.setColor(QPalette::Active,QPalette::ButtonText,QColor(Qt::white)); - //okTb->setPalette(pal); - - okTb->setEnabled(false); - enableTb->setChecked(false); - - checkActionState(); - - okTb->setEnabled(false); -} - -QWidget* SuiteItemWidget::realWidget() -{ - return this; -} - -void SuiteItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - - info_=info; - - if(info_ && info_->isServer() && info_->server()) - { - //Get the current suitefilter - SuiteFilter *sf=info_->server()->suiteFilter(); - - assert(sf); - - //The model will be an observer of the suitefilter - model_->setData(sf,info_->server()); - - if(!columnsAdjusted_) - { - for(int i=0; i < model_->columnCount()-1; i++) - { - suiteView->resizeColumnToContents(i); - suiteView->setColumnWidth(i,suiteView->columnWidth(i) + ((i==0)?25:15)); - } - columnsAdjusted_=true; - } - - suiteView->sortByColumn(0,Qt::AscendingOrder); - - model_->filter()->addObserver(this); - - enableTb->setChecked(sf->isEnabled()); - autoCb->setChecked(sf->autoAddNewSuites()); - - checkActionState(); - - //We update the filter because it might not show the current status. If - //there is a change the model will be notified - - //If the filter is disabled we update the filter with the - //current list of suites in the defs. These are all the suites - //loaded to the server. - if(!sf->isEnabled()) - { - info_->server()->updateSuiteFilterWithDefs(); - } - //If the filter is enabled we need to fetch the total list of suites - //loaded onto the server directly from the server (through the thread) - else - { - //inforReady or infoFailed will always be called. - infoProvider_->info(info_); - } - } -} - -void SuiteItemWidget::updateData() -{ - /*if(info_.get() && info_->isServer() && info_->server()) - { - model_->updateData(info_->server()->suiteFilter()); - }*/ -} - -void SuiteItemWidget::infoReady(VReply* reply) -{ - suiteView->sortByColumn(0,Qt::AscendingOrder); - //updateData(); -} - -void SuiteItemWidget::infoFailed(VReply* reply) -{ - //commandSent_=false; - //QString s=QString::fromStdString(reply->errorText()); - //checkActionState(); -} - -void SuiteItemWidget::clearContents() -{ - model_->setData(0,0); - InfoPanelItem::clear(); - okTb->setEnabled(false); - messageLabel->hide(); -} - -void SuiteItemWidget::updateState(const FlagSet&) -{ - checkActionState(); -} - -void SuiteItemWidget::checkActionState() -{ - if(suspended_) - { - enableTb->setEnabled(false); - autoCb->setEnabled(false); - selectAllTb->setEnabled(false); - unselectAllTb->setEnabled(false); - syncTb->setEnabled(false); - removeTb->setEnabled(false); - suiteView->setEnabled(false); - okTb->setEnabled(false); - return; - } - else - { - enableTb->setEnabled(true); - suiteView->setEnabled(true); - } - - if(enableTb->isChecked()) - { - autoCb->setEnabled(true); - selectAllTb->setEnabled(true); - unselectAllTb->setEnabled(true); - syncTb->setEnabled(true); - - if(SuiteFilter* sf=model_->filter()) - { - autoCb->setChecked(sf->autoAddNewSuites()); - removeTb->setEnabled(sf->hasUnloaded()); - syncTb->setEnabled(true); - - } - } - else - { - autoCb->setEnabled(false); - selectAllTb->setEnabled(false); - unselectAllTb->setEnabled(false); - syncTb->setEnabled(false); - removeTb->setEnabled(false); - } -} - -void SuiteItemWidget::on_autoCb_clicked(bool val) -{ - if(SuiteFilter* sf=model_->filter()) - { - sf->setAutoAddNewSuites(val); - settingsChanged(); - } -} - -void SuiteItemWidget::on_enableTb_clicked(bool val) -{ - if(SuiteFilter* sf=model_->filter()) - { - sf->setEnabled(val); - model_->reloadData(); - settingsChanged(); - } - - checkActionState(); -} - -void SuiteItemWidget::on_selectAllTb_clicked(bool) -{ - if(SuiteFilter* sf=model_->filter()) - { - sf->selectAll(); - model_->reloadData(); - settingsChanged(); - } -} - -void SuiteItemWidget::on_unselectAllTb_clicked(bool) -{ - if(SuiteFilter* sf=model_->filter()) - { - sf->unselectAll(); - model_->reloadData(); - settingsChanged(); - } -} - -//get a fresh suite list from the server -void SuiteItemWidget::on_syncTb_clicked(bool) -{ - if(info_.get() && info_->isServer() && info_->server()) - { - infoProvider_->info(info_); - } -} - -void SuiteItemWidget::on_removeTb_clicked(bool val) -{ - if(SuiteFilter* sf=model_->filter()) - { - if(sf->removeUnloaded()) - { - model_->reloadData(); - settingsChanged(); - } - } - - checkActionState(); -} - -void SuiteItemWidget::on_okTb_clicked(bool) -{ - if(info_.get() && info_->isServer() && info_->server()) - { - //This replace the edited filter in model the one - //stored by the server - info_->server()->updateSuiteFilter(model_->filter()); - okTb->setEnabled(false); - messageLabel->clear(); - messageLabel->hide(); - } -} - -void SuiteItemWidget::slotModelEdited(const QModelIndex&,const QModelIndex&) -{ - settingsChanged(); -} - -void SuiteItemWidget::settingsChanged() -{ - SuiteFilter *sf=model_->filter(); - SuiteFilter *oriSf=model_->realFilter(); - if(sf && oriSf) - { - bool st=oriSf->sameAs(sf); - okTb->setEnabled(!st); - if(st) - { - messageLabel->clear(); - messageLabel->hide(); - } - else - { - messageLabel->showTip("The suite filter has changed! Please click Apply to submit the changes to the server!"); - } - } -} - -void SuiteItemWidget::notifyChange(SuiteFilter *filter) -{ - settingsChanged(); - checkActionState(); -} - -void SuiteItemWidget::notifyDelete(SuiteFilter *filter) -{ - Q_ASSERT(filter); - filter->removeObserver(this); -} - - -static InfoPanelItemMaker maker1("suite"); diff -Nru ecflow-4.9.0/Viewer/src/SuiteItemWidget.hpp ecflow-4.11.1/Viewer/src/SuiteItemWidget.hpp --- ecflow-4.9.0/Viewer/src/SuiteItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef SUITEITEMWIDGET_HPP_ -#define SUITEITEMWIDGET_HPP_ - -#include - -#include "InfoPanelItem.hpp" -#include "VInfo.hpp" -#include "SuiteFilterObserver.hpp" - -#include "ui_SuiteItemWidget.h" - -class SuiteModel; - -class SuiteItemWidget : public QWidget, public InfoPanelItem, public SuiteFilterObserver, protected Ui::SuiteItemWidget -{ -Q_OBJECT - -public: - explicit SuiteItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - void infoReady(VReply*); - void infoFailed(VReply*); - void infoProgress(VReply*) {} - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - - void notifyChange(SuiteFilter *filter); - void notifyDelete(SuiteFilter *filter); - -protected Q_SLOTS: - void on_autoCb_clicked(bool); - void on_enableTb_clicked(bool); - void on_selectAllTb_clicked(bool); - void on_unselectAllTb_clicked(bool); - void on_syncTb_clicked(bool); - void on_okTb_clicked(bool); - void on_removeTb_clicked(bool); - void slotModelEdited(const QModelIndex&,const QModelIndex&); - -protected: - void updateData(); - void updateState(const ChangeFlags&); - void settingsChanged(); - void checkActionState(); - - SuiteModel *model_; - bool columnsAdjusted_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/SuiteItemWidget.ui ecflow-4.11.1/Viewer/src/SuiteItemWidget.ui --- ecflow-4.9.0/Viewer/src/SuiteItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,341 +0,0 @@ - - - SuiteItemWidget - - - - 0 - 0 - 564 - 470 - - - - - 0 - 0 - - - - Form - - - - - - - 1 - - - 0 - - - 3 - - - 2 - - - 0 - - - - - - - - - - - - - - - Qt::ActionsContextMenu - - - true - - - false - - - true - - - false - - - true - - - false - - - - - - - - - - - - 0 - 0 - - - - Apply changes - - - &Apply - - - - :/viewer/submit_round.svg:/viewer/submit_round.svg - - - Qt::ToolButtonTextBesideIcon - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 15 - - - - - - - - Control: - - - - - - - - 0 - 0 - - - - <html><head/><body><p>By specifiying a <span style=" font-weight:600;">suite filter</span> we can define a subset of suites that ecflowUI needs to keep track of. This can significantly reduce network bandwith and <span style=" font-weight:600;">speed up communication</span> to the server.</p></body></html> - - - Enable filter - - - - :/viewer/filter_decor.svg:/viewer/filter_decor.svg - - - true - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - <html><head/><body><p>By selecting this option all <span style=" font-weight:600;">new suites</span> loaded to the server will be <span style=" font-weight:600;">automatically</span> added to the suite filter and become visible in the views.</p></body></html> - - - Auto add new suites - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 8 - - - - - - - - Selection: - - - - - - - - 0 - 0 - - - - Select all suites - - - Select &all - - - - :/viewer/select_all.svg:/viewer/select_all.svg - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - - 0 - 0 - - - - Unselect all suites - - - &Unselect all - - - - :/viewer/unselect_all.svg:/viewer/unselect_all.svg - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 8 - - - - - - - - Load/unload status: - - - - - - - - 0 - 0 - - - - <html><head/><body><p>Get the<span style=" font-weight:600;"> current</span> list of <span style=" text-decoration: underline;">loaded suites</span> from the server. </p></body></html> - - - &Fetch load status - - - - :/viewer/sync_black.svg:/viewer/sync_black.svg - - - Qt::ToolButtonTextBesideIcon - - - false - - - - - - - - 0 - 0 - - - - Remove all currently unloaded suites from the suite filter list - - - &Remove unloaded - - - - :/viewer/remove.svg:/viewer/remove.svg - - - Qt::ToolButtonTextBesideIcon - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/SuiteModel.cpp ecflow-4.11.1/Viewer/src/SuiteModel.cpp --- ecflow-4.9.0/Viewer/src/SuiteModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,277 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "SuiteModel.hpp" - -#include "ServerHandler.hpp" -#include "SuiteFilter.hpp" -#include "VNode.hpp" - -SuiteModel::SuiteModel(QObject *parent) : - QAbstractItemModel(parent), - server_(0), - data_(0), - realData_(0), - presentCol_(QColor(1,128,73)), - notPresentCol_(QColor(255,0,0)) -{ - -} - -SuiteModel::~SuiteModel() -{ - beginResetModel(); - clearData(); - endResetModel(); -} - -void SuiteModel::clearData() -{ - server_=0; - - if(data_) - delete data_; - - data_=0; - - if(realData_) - realData_->removeObserver(this); - - realData_=0; -} - -void SuiteModel::setData(SuiteFilter* filter,ServerHandler* server) -{ - beginResetModel(); - - if(data_ && data_ != filter) - { - clearData(); - } - - if(filter) - { - server_=server; - Q_ASSERT(server); - data_=filter->clone(); - realData_=filter; - realData_->addObserver(this); - } - - endResetModel(); -} - -void SuiteModel::notifyChange(SuiteFilter *filter) -{ - if(filter && filter == realData_) - { - updateData(); - } -} - -void SuiteModel::notifyDelete(SuiteFilter *filter) -{ - if(filter && filter == realData_) - { - beginResetModel(); - clearData(); - endResetModel(); - } -} - -void SuiteModel::updateData() -{ - if(realData_ && data_ && - !data_->loadedSameAs(realData_->loaded())) - { - beginResetModel(); - data_->setLoaded(realData_->loaded()); - endResetModel(); - } -} - -void SuiteModel::reloadData() -{ - beginResetModel(); - endResetModel(); -} - -bool SuiteModel::hasData() const -{ - return (data_ && data_->count() > 0); -} - -int SuiteModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 3; -} - -int SuiteModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return data_->count(); - } - - return 0; -} - -Qt::ItemFlags SuiteModel::flags ( const QModelIndex & index) const -{ - Qt::ItemFlags defaultFlags; - - if(data_->isEnabled()) - { - defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; // | Qt::ItemIsEditable; - } - - return defaultFlags; -} - -QVariant SuiteModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData()) - { - return QVariant(); - } - - //Data lookup can be costly so we immediately return a default value for all - //the cases where the default should be used. - //if(role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::ForegroundRole) - //{ - // return QVariant(); - //} - - int row=index.row(); - if(row < 0 || row >= data_->count()) - return QVariant(); - - if(role == Qt::DisplayRole) - { - switch(index.column()) - { - case 0: - return QString::fromStdString(data_->items().at(row).name()); - break; - case 1: - return (data_->items().at(row).loaded())?"loaded":"not loaded"; - break; - case 2: - { - if(data_->items().at(row).loaded()) - { - Q_ASSERT(server_); - int n=server_->vRoot()->totalNumOfTopLevel(data_->items().at(row).name()); - if(n != -1) - return n; - else - return QVariant(); - } - else - return QVariant(); - } - break; - default: - break; - } - } - else if(role == Qt::CheckStateRole) - { - if(index.column()==0) - return (data_->items().at(row).filtered())?QVariant(Qt::Checked):QVariant(Qt::Unchecked); - } - else if(role == Qt::ForegroundRole) - { - if(!data_->isEnabled()) - { - return QVariant(); - } - else if(index.column() == 1) - { - return (data_->items().at(row).loaded())?presentCol_:notPresentCol_; - } - return QVariant(); - } - - return QVariant(); -} - -bool SuiteModel::setData(const QModelIndex& index, const QVariant & value, int role ) -{ - if(index.column() == 0 && role == Qt::CheckStateRole) - { - int row=index.row(); - if(row >=0 && row < data_->count()) - { - bool checked=(value.toInt() == Qt::Checked)?true:false; - data_->setFiltered(row,checked); - Q_EMIT dataChanged(index,index); - - return true; - } - } - - return false; -} - -QVariant SuiteModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) - return QAbstractItemModel::headerData( section, orient, role ); - - if(role == Qt::DisplayRole) - { - switch ( section ) - { - case 0: return tr("Suite"); - case 1: return tr("Load status on server"); - case 2: return tr("Number of children"); - default: return QVariant(); - } - } - else if(role== Qt::ToolTipRole) - { - switch ( section ) - { - case 0: return tr("Filter status of suite"); - case 1: return tr("Indicates if the suite is currently loaded on the server.

      \ - Please note: this information might not be up to date for unfiltered suites. Use Fetch load status \ - to get the actual load status list."); - case 2: return tr("Number of children"); - default: return QVariant(); - } - } - return QVariant(); -} - -QModelIndex SuiteModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex SuiteModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} diff -Nru ecflow-4.9.0/Viewer/src/SuiteModel.hpp ecflow-4.11.1/Viewer/src/SuiteModel.hpp --- ecflow-4.9.0/Viewer/src/SuiteModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/SuiteModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -#ifndef SUITEMODEL_H -#define SUITEMODEL_H - -#include -#include - -#include "SuiteFilterObserver.hpp" - -class ServerHandler; -class SuiteFilter; - -class SuiteModel : public QAbstractItemModel, public SuiteFilterObserver -{ -public: - explicit SuiteModel(QObject *parent=0); - ~SuiteModel(); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - bool setData(const QModelIndex & index, const QVariant & value, int role ); - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - void setData(SuiteFilter* filter,ServerHandler* server); - void reloadData(); - - SuiteFilter* filter() const {return data_;} - SuiteFilter* realFilter() const {return realData_;} - - void notifyChange(SuiteFilter*); - void notifyDelete(SuiteFilter*); - -protected: - bool hasData() const; - void clearData(); - void updateData(); - - ServerHandler* server_; - SuiteFilter* data_; - SuiteFilter* realData_; - QColor presentCol_; - QColor notPresentCol_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TableFilterWidget.cpp ecflow-4.11.1/Viewer/src/TableFilterWidget.cpp --- ecflow-4.9.0/Viewer/src/TableFilterWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableFilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TableFilterWidget.hpp" - -#include -#include -#include - -#include "FilterWidget.hpp" -#include "NodeFilterDialog.hpp" -#include "NodeQuery.hpp" -#include "NodeQueryOption.hpp" -#include "ServerFilter.hpp" -#include "UiLog.hpp" -#include "VFilter.hpp" - -#include - -TableFilterWidget::TableFilterWidget(QWidget *parent) : - QWidget(parent), - filterDef_(0), - serverFilter_(0) -{ - setupUi(this); - - connect(queryTe_,SIGNAL(clicked()), - this,SLOT(slotEdit())); - - numLabel_->hide(); - - slotTotalNumChanged(0); - - //queryTe_->setFixedHeight(18); -} - -void TableFilterWidget::slotEdit() -{ - assert(filterDef_); - assert(serverFilter_); - - NodeFilterDialog d(this); - d.setServerFilter(serverFilter_); - d.setQuery(filterDef_->query()); - if(d.exec() == QDialog::Accepted) - { - filterDef_->setQuery(d.query()); - UiLog().dbg() << "new table query: " << filterDef_->query()->query(); - } -} - -void TableFilterWidget::build(NodeFilterDef* def,ServerFilter *sf) -{ - filterDef_=def; - serverFilter_=sf; - - connect(filterDef_,SIGNAL(changed()), - this,SLOT(slotDefChanged())); - - slotDefChanged(); -} - -void TableFilterWidget::slotDefChanged() -{ - QColor bg(240,240,240); - queryTe_->setHtml(filterDef_->query()->sqlQuery()); -} - -void TableFilterWidget::slotHeaderFilter(QString column,QPoint globalPos) -{ - NodeQuery *q=filterDef_->query()->clone(); - if(column == "status") - { - QMenu *menu=new QMenu(this); - //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); - - NodeStateFilter sf; - NodeQueryListOption* op=q->stateOption(); - Q_ASSERT(op); - //if(!op->selection().isEmpty()) - sf.setCurrent(op->selection()); - - //The menu takes ownership of it - new VParamFilterMenu(menu,&sf,"Status filter", - VParamFilterMenu::FilterMode,VParamFilterMenu::ColourDecor); - - if(menu->exec(globalPos) != NULL) - { - //if(sf.isComplete()) - // op->setSelection(QStringList()); - //else - op->setSelection(sf.currentAsList()); - - //this will create deep copy so we can delet q in the end - filterDef_->setQuery(q); - } - - delete menu; - } - - delete q; -} - -void TableFilterWidget::slotTotalNumChanged(int n) -{ - numLabel_->setText(tr("Total: ") + QString::number(n)); -} diff -Nru ecflow-4.9.0/Viewer/src/TableFilterWidget.hpp ecflow-4.11.1/Viewer/src/TableFilterWidget.hpp --- ecflow-4.9.0/Viewer/src/TableFilterWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableFilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TABLEFILTERWIDGET_INC_ -#define TABLEFILTERWIDGET_INC_ - -#include "ui_TableFilterWidget.h" - -#include - -class NodeFilterDef; -class ServerFilter; - -class TableFilterWidget : public QWidget, private Ui::TableFilterWidget -{ -Q_OBJECT - -public: - explicit TableFilterWidget(QWidget *parent=0); - ~TableFilterWidget() {} - - void build(NodeFilterDef*,ServerFilter*); - -public Q_SLOTS: - void slotEdit(); - void slotDefChanged(); - void slotHeaderFilter(QString column,QPoint globalPos); - void slotTotalNumChanged(int); - -private: - NodeFilterDef* filterDef_; - ServerFilter* serverFilter_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/TableFilterWidget.ui ecflow-4.11.1/Viewer/src/TableFilterWidget.ui --- ecflow-4.9.0/Viewer/src/TableFilterWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableFilterWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,79 +0,0 @@ - - - TableFilterWidget - - - - 0 - 0 - 412 - 118 - - - - Form - - - - 2 - - - 0 - - - 0 - - - 0 - - - - - Filter: - - - - - - - - - - - - - - - - - true - - - Breadcrumbs - - - Show breadcrumbs - - - - - true - - - Frozen - - - Do not update panel when node changes - - - - - - OneLineTextEdit - QTextEdit -
      OneLineTextEdit.hpp
      -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/TableNodeModel.cpp ecflow-4.11.1/Viewer/src/TableNodeModel.cpp --- ecflow-4.9.0/Viewer/src/TableNodeModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,449 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TableNodeModel.hpp" - -#include - -#include "ModelColumn.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VFilter.hpp" -#include "VIcon.hpp" -#include "VModelData.hpp" -#include "VNode.hpp" -#include "VNState.hpp" - -//#define _UI_TABLENODEMODEL_DEBUG - -//static int hitCount=0; - -static std::map attrTypes; -static VAttributeType* columnToAttrType(TableNodeModel::ColumnType ct); - -VAttributeType* columnToAttrType(TableNodeModel::ColumnType ct) -{ - std::map::const_iterator it= - attrTypes.find(ct); - return (it != attrTypes.end())?it->second:0; -} - - -//======================================================= -// -// TableNodeModel -// -//======================================================= - - -TableNodeModel::TableNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef,QObject *parent) : - AbstractNodeModel(parent), - data_(0), - columns_(0) -{ - columns_=ModelColumn::def("table_columns"); - - Q_ASSERT(columns_); - - //Check the mapping between the enum and column ids - Q_ASSERT(columns_->id(PathColumn) == "path"); - Q_ASSERT(columns_->id(StatusColumn) == "status"); - Q_ASSERT(columns_->id(TypeColumn) == "type"); - Q_ASSERT(columns_->id(TriggerColumn) == "trigger"); - Q_ASSERT(columns_->id(LabelColumn) == "label"); - Q_ASSERT(columns_->id(EventColumn) == "event"); - Q_ASSERT(columns_->id(MeterColumn) == "meter"); - Q_ASSERT(columns_->id(StatusChangeColumn) == "statusChange"); - - if(attrTypes.empty()) - { - QList ctLst; - ctLst << TriggerColumn << LabelColumn << EventColumn << MeterColumn; - Q_FOREACH(ColumnType ct,ctLst) - { - VAttributeType* t=VAttributeType::find(columns_->id(ct).toStdString()); - Q_ASSERT(t); - attrTypes[ct]=t; - } - } - - //Create the data handler for the model. - data_=new VTableModelData(filterDef,this); - - data_->reset(serverFilter); -} - -VModelData* TableNodeModel::data() const -{ - return data_; -} - -int TableNodeModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return columns_->count(); -} - -int TableNodeModel::rowCount( const QModelIndex& parent) const -{ -#ifdef _UI_TABLENODEMODEL_DEBUG - UiLog().dbg() << "rowCount=" << parent; -#endif - - //There are no servers - if(!hasData()) - { - return 0; - } - //We use only column 0 - else if(parent.column() > 0) - { - return 0; - } - //"parent" is the root - else if(!parent.isValid()) - { - //The total number of nodes displayed - int cnt=0; - for(int i=0; i < data_->count(); i++) - { - if(!data_->server(i)->inScan()) - cnt+=data_->numOfNodes(i); - } -#ifdef _UI_TABLENODEMODEL_DEBUG - //UiLog().dbg() << "table count " << cnt; -#endif - return cnt; - } - - return 0; -} - - -QVariant TableNodeModel::data( const QModelIndex& index, int role ) const -{ - //Data lookup can be costly so we immediately return a default value for all - //the cases where the default should be used. - if( !index.isValid() || - (role != Qt::DisplayRole && role != Qt::ToolTipRole && - role != Qt::BackgroundRole && role != IconRole && role != SortRole)) - { - return QVariant(); - } - - //We only display nodes!! - return nodeData(index,role); -} - -QVariant TableNodeModel::nodeData(const QModelIndex& index, int role) const -{ - VNode* vnode=indexToNode(index); - if(!vnode || !vnode->node()) - return QVariant(); - - ColumnType id=static_cast(index.column()); - - if(role == Qt::DisplayRole) - { - //QString id=columns_->id(index.column()); - - if(id == PathColumn) - { - return QString::fromStdString(vnode->absNodePath()); - } - else if(id == StatusColumn) - return vnode->stateName(); - else if(id == TypeColumn) - return QString::fromStdString(vnode->nodeType()); - - //Attributes - else if(id == EventColumn || id == LabelColumn || id == MeterColumn || - id == TriggerColumn) - { - if(VAttribute* a=vnode->attributeForType(0,columnToAttrType(id))) - return a->data(true); - else - return QVariant(); - } - - else if(id == StatusChangeColumn) - { - QString s; - vnode->statusChangeTime(s); - return s; - } - - //else if(id == "icon") - // return VIcon::pixmapList(vnode,0); - } - else if(role == Qt::BackgroundRole) - { - return vnode->stateColour(); - } - else if(role == IconRole) - { - if(id == PathColumn) - return VIcon::pixmapList(vnode,0); - else - return QVariant(); - } - else if(role == SortRole) - { - if(id == StatusChangeColumn) - { - return vnode->statusChangeTime(); - } - } - - return QVariant(); -} - -QVariant TableNodeModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || (role != Qt::DisplayRole && role != Qt::UserRole )) - return QAbstractItemModel::headerData( section, orient, role ); - - if (section < 0 || section >= columns_->count()) // this can happen during a server reset - return QVariant(); - - if(role == Qt::DisplayRole) - return columns_->label(section); - else if(role == Qt::UserRole) - return columns_->id(section); - - return QVariant(); -} - - -QModelIndex TableNodeModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0 || parent.isValid()) - { - return QModelIndex(); - } - - if(VNode *node=data_->nodeAt(row)) - { - return createIndex(row,column,node); - } - - return QModelIndex(); -} - -QModelIndex TableNodeModel::parent(const QModelIndex& /*child*/) const -{ - //Parent is always the root!!! - return QModelIndex(); -} - -//---------------------------------------------- -// -// Server to index mapping and lookup -// -//---------------------------------------------- - -VNode* TableNodeModel::indexToNode( const QModelIndex & index) const -{ - if(index.isValid()) - { - return static_cast(index.internalPointer()); - } - return NULL; -} - -QModelIndex TableNodeModel::nodeToIndex(const VNode* node, int column) const -{ - if(!node) - return QModelIndex(); - - int row=0; - if((row=data_->position(node)) != -1) - { - return createIndex(row,column,const_cast(node)); - } - return QModelIndex(); -} - - -//Find the index for the node when we know what the server is! -QModelIndex TableNodeModel::nodeToIndex(VTableServer* server,const VNode* node, int column) const -{ - if(!node) - return QModelIndex(); - - int row=0; - if((row=data_->position(server,node)) != -1) - { - return createIndex(row,column,const_cast(node)); - } - return QModelIndex(); -} - -QModelIndex TableNodeModel::attributeToIndex(const VAttribute* a, int column) const -{ - if(!a) - return QModelIndex(); - - VNode* node=a->parent(); - if(!node) - return QModelIndex(); - - int row=0; - if((row=data_->position(node)) != -1) - { - return createIndex(row,column,const_cast(node)); - } - return QModelIndex(); -} - -void TableNodeModel::selectionChanged(QModelIndexList lst) -{ -#if 0 - Q_FOREACH(QModelIndex idx,lst) - { - VInfo_ptr info=nodeInfo(idx); - - for(int i=0; i < data_->count(); i++) - { - VTableServer *ts=data_->server(i)->tableServer(); - Q_ASSERT(ts); - ts->clearForceShow(info->item()); - } - } -#endif -} - -VInfo_ptr TableNodeModel::nodeInfo(const QModelIndex& index) -{ - VNode *n=indexToNode(index); - if(n) - { - return VInfoNode::create(n); - } - - VInfo_ptr info; - return info; -} - -//Server is about to be added -void TableNodeModel::slotServerAddBegin(int /*row*/) -{ -} - -//Addition of the new server has finished -void TableNodeModel::slotServerAddEnd() -{ -} - -//Server is about to be removed -void TableNodeModel::slotServerRemoveBegin(VModelServer* server,int num) -{ - Q_ASSERT(active_ == true); - Q_ASSERT(server); - - if(num >0) - { - int start=-1; - int count=-1; - data_->position(server->tableServer(),start,count); - - Q_ASSERT(start >=0); - Q_ASSERT(count == num); - - beginRemoveRows(QModelIndex(),start,start+count-1); - } -} - -//Removal of the server has finished -void TableNodeModel::slotServerRemoveEnd(int num) -{ - assert(active_ == true); - - if(num >0) - endRemoveRows(); -} - -//The node changed (it status etc) -void TableNodeModel::slotNodeChanged(VTableServer* server,const VNode* node) -{ - if(!node) - return; - - QModelIndex index=nodeToIndex(server,node,0); - - if(!index.isValid()) - return; - - Q_EMIT dataChanged(index,index); -} - -void TableNodeModel::slotBeginServerScan(VModelServer* server,int num) -{ - Q_ASSERT(active_ == true); - Q_ASSERT(server); - -#ifdef _UI_TABLENODEMODEL_DEBUG - UiLog().dbg() << "TableNodeModel::slotBeginServerScan --> " << server->realServer()->name() << - " " << num; -#endif - - if(num >0) - { - int count=num; - int start=data_->position(server->tableServer()); - beginInsertRows(QModelIndex(),start,start+count-1); - } -} - -void TableNodeModel::slotEndServerScan(VModelServer* server,int num) -{ - assert(active_ == true); - -#ifdef _UI_TABLENODEMODEL_DEBUG - UiLog().dbg() << "TableNodeModel::slotEndServerScan --> " << server->realServer()->name() << - " " << num; - QTime t; - t.start(); -#endif - - if(num >0) - endInsertRows(); - -#ifdef _UI_TABLENODEMODEL_DEBUG - UiLog().dbg() << " elapsed: " << t.elapsed() << " ms"; - UiLog().dbg() << "<-- slotEndServerScan"; -#endif -} - -void TableNodeModel::slotBeginServerClear(VModelServer* server,int num) -{ - Q_ASSERT(active_ == true); - Q_ASSERT(server); - - if(num >0) - { - int start=-1; - int count=-1; - data_->position(server->tableServer(),start,count); - - Q_ASSERT(start >=0); - Q_ASSERT(count == num); - - beginRemoveRows(QModelIndex(),start,start+count-1); - } -} - -void TableNodeModel::slotEndServerClear(VModelServer* server,int num) -{ - assert(active_ == true); - - if(num >0) - endRemoveRows(); -} diff -Nru ecflow-4.9.0/Viewer/src/TableNodeModel.hpp ecflow-4.11.1/Viewer/src/TableNodeModel.hpp --- ecflow-4.9.0/Viewer/src/TableNodeModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,94 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TABLENODEMODEL_H -#define TABLENODEMODEL_H - -#include - -#include "AbstractNodeModel.hpp" -#include "VInfo.hpp" - -class ModelColumn; -class Node; -class NodeFilterDef; -class ServerFilter; -class ServerHandler; -class VTableModelData; -class VTableServer; - -class TableNodeModel : public AbstractNodeModel -{ -Q_OBJECT - - friend class TableNodeSortModel; - -public: - TableNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef,QObject *parent=0); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - VInfo_ptr nodeInfo(const QModelIndex&); - void selectionChanged(QModelIndexList lst); - - VModelData* data() const; - ModelColumn* columns() const {return columns_;} - - //To speed up identifying a column. The mapping here must match the definition of - //"table_columns" in ecflowview_view_conf.json !!! - enum ColumnType {PathColumn=0,StatusColumn=1,TypeColumn=2,TriggerColumn=3, - LabelColumn=4, EventColumn=5, MeterColumn=6, StatusChangeColumn=7}; - -public Q_SLOTS: - void slotServerAddBegin(int); - void slotServerAddEnd(); - void slotServerRemoveBegin(VModelServer* server,int); - void slotServerRemoveEnd(int); - - void slotDataChanged(VModelServer*) {} - void slotNodeChanged(VTableServer*,const VNode*); - void slotAttributesChanged(VModelServer*,const VNode*) {} - void slotBeginAddRemoveAttributes(VModelServer*,const VNode*,int,int) {} - void slotEndAddRemoveAttributes(VModelServer*,const VNode*,int,int) {} - - void slotBeginServerScan(VModelServer* server,int); - void slotEndServerScan(VModelServer* server,int); - void slotBeginServerClear(VModelServer* server,int); - void slotEndServerClear(VModelServer* server,int); - -Q_SIGNALS: - void filterChangeBegun(); - void filterChangeEnded(); - -protected: - bool isServer(const QModelIndex & index) const {return false;} - ServerHandler* indexToRealServer(const QModelIndex & index) const {return NULL;} - VModelServer* indexToServer(const QModelIndex & index) const {return NULL;} - QModelIndex serverToIndex(ServerHandler*) const {return QModelIndex();} - - QModelIndex nodeToIndex(VTableServer* server,const VNode* node, int column) const; - QModelIndex nodeToIndex(const VNode*,int column=0) const; - VNode* indexToNode( const QModelIndex & index) const; - - QModelIndex attributeToIndex(const VAttribute* a, int column=0) const; - - QVariant nodeData(const QModelIndex& index,int role) const; - - VTableModelData* data_; - ModelColumn* columns_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TableNodeSortModel.cpp ecflow-4.11.1/Viewer/src/TableNodeSortModel.cpp --- ecflow-4.9.0/Viewer/src/TableNodeSortModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeSortModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,71 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TableNodeSortModel.hpp" - -#include "TableNodeModel.hpp" -#include "ModelColumn.hpp" - -TableNodeSortModel::TableNodeSortModel(TableNodeModel* nodeModel,QObject *parent) : - QSortFilterProxyModel(parent), - nodeModel_(nodeModel) -{ - //connect(nodeModel_,SIGNAL(filterChanged()), - // this,SLOT(slotFilterChanged())); - - QSortFilterProxyModel::setSourceModel(nodeModel_); - - setDynamicSortFilter(false); -} - -TableNodeSortModel::~TableNodeSortModel() -{ -} - -VInfo_ptr TableNodeSortModel::nodeInfo(const QModelIndex& index) -{ - return nodeModel_->nodeInfo(mapToSource(index)); -} - -QModelIndex TableNodeSortModel::infoToIndex(VInfo_ptr info) -{ - return mapFromSource(nodeModel_->infoToIndex(info)); -} - -QModelIndex TableNodeSortModel::nodeToIndex(const VNode *node) -{ - return mapFromSource(nodeModel_->nodeToIndex(node)); -} - -void TableNodeSortModel::selectionChanged(QModelIndexList lst) -{ - QModelIndexList lstm; - Q_FOREACH(QModelIndex idx,lst) - lstm << mapToSource(idx); - - nodeModel_->selectionChanged(lstm); -} - - -bool TableNodeSortModel::lessThan(const QModelIndex &left, - const QModelIndex &right) const -{ - TableNodeModel::ColumnType id=static_cast(left.column()); - - if(id == TableNodeModel::StatusChangeColumn) - { - return left.data(AbstractNodeModel::SortRole).toUInt() < - right.data(AbstractNodeModel::SortRole).toUInt(); - } - - QVariant leftData = nodeModel_->data(left); - QVariant rightData = nodeModel_->data(right); - - return leftData.toString() < rightData.toString(); -} diff -Nru ecflow-4.9.0/Viewer/src/TableNodeSortModel.hpp ecflow-4.11.1/Viewer/src/TableNodeSortModel.hpp --- ecflow-4.9.0/Viewer/src/TableNodeSortModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeSortModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TABLENODEFILTERMODEL_H -#define TABLENODEFILTERMODEL_H - -#include - -#include "VInfo.hpp" - -class TableNodeModel; -class NodeFilterDef; - -class TableNodeSortModel : public QSortFilterProxyModel -{ -public: - TableNodeSortModel(TableNodeModel*,QObject *parent=0); - ~TableNodeSortModel(); - - //From QSortFilterProxyModel: - //we set the source model in the constructor. So this function should not do anything. - void setSourceModel(QAbstractItemModel*) {} - - VInfo_ptr nodeInfo(const QModelIndex&); - QModelIndex infoToIndex(VInfo_ptr); - QModelIndex nodeToIndex(const VNode *node); - void selectionChanged(QModelIndexList lst); - -protected: - bool lessThan(const QModelIndex &left, - const QModelIndex &right) const; - - TableNodeModel* nodeModel_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TableNodeView.cpp ecflow-4.11.1/Viewer/src/TableNodeView.cpp --- ecflow-4.9.0/Viewer/src/TableNodeView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,679 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TableNodeView.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ActionHandler.hpp" -#include "FilterWidget.hpp" -#include "IconProvider.hpp" -#include "TableNodeSortModel.hpp" -#include "PropertyMapper.hpp" -#include "TableNodeModel.hpp" -#include "TableNodeViewDelegate.hpp" -#include "UiLog.hpp" -#include "VFilter.hpp" -#include "VSettings.hpp" - -#define _UI_TABLENODEVIEW_DEBUG - -TableNodeView::TableNodeView(TableNodeSortModel* model,NodeFilterDef* filterDef,QWidget* parent) : - QTreeView(parent), - NodeViewBase(filterDef), - model_(model), - needItemsLayout_(false), - prop_(NULL), - setCurrentIsRunning_(false) -{ - setObjectName("view"); - setProperty("style","nodeView"); - setProperty("view","table"); - - setRootIsDecorated(false); - - setSortingEnabled(true); - //sortByColumn(0,Qt::AscendingOrder); - - setAllColumnsShowFocus(true); - setUniformRowHeights(true); - setMouseTracking(true); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - //!!!!We need to do it because: - //The background colour between the views left border and the nodes cannot be - //controlled by delegates or stylesheets. It always takes the QPalette::Highlight - //colour from the palette. Here we set this to transparent so that Qt could leave - //this area empty and we fill it appropriately in our delegate. - QPalette pal=palette(); - pal.setColor(QPalette::Highlight,QColor(128,128,128,0)); - setPalette(pal); - - //Context menu - setContextMenuPolicy(Qt::CustomContextMenu); - - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - - //Selection - connect(this,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClickItem(const QModelIndex))); - - actionHandler_=new ActionHandler(this,this); - - //expandAll(); - - //Header - header_=new TableNodeHeader(this); - - setHeader(header_); - - //Set header ContextMenuPolicy - header_->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(header_,SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotHeaderContextMenu(const QPoint &))); - - connect(header_,SIGNAL(customButtonClicked(QString,QPoint)), - this,SIGNAL(headerButtonClicked(QString,QPoint))); - - //for(int i=0; i < model_->columnCount(QModelIndex())-1; i++) - // resizeColumnToContents(i); - - /*connect(header(),SIGNAL(sectionMoved(int,int,int)), - this, SLOT(slotMessageTreeColumnMoved(int,int,int)));*/ - - QTreeView::setModel(model_); - - //Create delegate to the view - TableNodeViewDelegate *delegate=new TableNodeViewDelegate(this); - setItemDelegate(delegate); - - connect(delegate,SIGNAL(sizeHintChangedGlobal()), - this,SLOT(slotSizeHintChangedGlobal())); - - //Properties - std::vector propVec; - propVec.push_back("view.table.background"); - prop_=new PropertyMapper(propVec,this); - - //Initialise bg - adjustBackground(prop_->find("view.table.background")->value().value()); -} - -TableNodeView::~TableNodeView() -{ - delete prop_; -} - -void TableNodeView::setModel(TableNodeSortModel *model) -{ - model_= model; - - //Set the model. - QTreeView::setModel(model_); -} - -QWidget* TableNodeView::realWidget() -{ - return this; -} - -QObject* TableNodeView::realObject() -{ - return this; -} - -//Collects the selected list of indexes -QModelIndexList TableNodeView::selectedList() -{ - QModelIndexList lst; - Q_FOREACH(QModelIndex idx,selectedIndexes()) - if(idx.column() == 0) - lst << idx; - return lst; -} - - -// reimplement virtual function from QTreeView - called when the selection is changed -void TableNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ - QModelIndexList lst=selectedIndexes(); - if(lst.count() > 0) - { - VInfo_ptr info=model_->nodeInfo(lst.front()); - if(info && !info->isEmpty()) - { -#ifdef _UI_TABLENODEVIEW_DEBUG - UiLog().dbg() << "TableNodeView::selectionChanged --> emit=" << info->path(); -#endif - Q_EMIT selectionChanged(info); - } - } - QTreeView::selectionChanged(selected, deselected); - - //The model has to know about the selection in order to manage the - //nodes that are forced to be shown - model_->selectionChanged(lst); -} - -VInfo_ptr TableNodeView::currentSelection() -{ - QModelIndexList lst=selectedIndexes(); - if(lst.count() > 0) - { - return model_->nodeInfo(lst.front()); - } - return VInfo_ptr(); -} - -void TableNodeView::setCurrentSelection(VInfo_ptr info) -{ - //While the current is being selected we do not allow - //another setCurrent call go through - if(setCurrentIsRunning_) - return; - - setCurrentIsRunning_=true; - QModelIndex idx=model_->infoToIndex(info); - if(idx.isValid()) - { -#ifdef _UI_TABLENODEVIEW_DEBUG - if(info) - UiLog().dbg() << "TableNodeView::setCurrentSelection --> " << info->path(); -#endif - setCurrentIndex(idx); - } - setCurrentIsRunning_=false; -} - -void TableNodeView::slotDoubleClickItem(const QModelIndex&) -{ -} - -void TableNodeView::slotContextMenu(const QPoint &position) -{ - QModelIndexList lst=selectedList(); - //QModelIndex index=indexAt(position); - QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); - - handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); -} - - -void TableNodeView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) -{ - //Node actions - if(indexClicked.isValid() && indexClicked.column() == 0) //indexLst[0].isValid() && indexLst[0].column() == 0) - { - UiLog().dbg() << "context menu " << indexClicked; - - std::vector nodeLst; - for(int i=0; i < indexLst.count(); i++) - { - VInfo_ptr info=model_->nodeInfo(indexLst[i]); - if(!info->isEmpty()) - nodeLst.push_back(info); - } - - actionHandler_->contextMenu(nodeLst,globalPos); - } - - //Desktop actions - else - { - } -} - -void TableNodeView::slotViewCommand(VInfo_ptr info,QString cmd) -{ -} - -void TableNodeView::rerender() -{ - if(needItemsLayout_) - { - doItemsLayout(); - needItemsLayout_=false; - } - else - { - viewport()->update(); - } -} - -void TableNodeView::slotRerender() -{ - rerender(); -} - -void TableNodeView::slotSizeHintChangedGlobal() -{ - needItemsLayout_=true; -} - -void TableNodeView::adjustBackground(QColor col) -{ - if(col.isValid()) - { - QString sh="QTreeView { background : " + col.name() + ";}"; - setStyleSheet(sh); - } -} - -void TableNodeView::notifyChange(VProperty* p) -{ - if(p->path() == "view.table.background") - { - adjustBackground(p->value().value()); - } -} - -//========================================= -// Header -//========================================= - -void TableNodeView::slotHeaderContextMenu(const QPoint &position) -{ - int section=header_->logicalIndexAt(position); - - if(section< 0 || section >= header_->count()) - return; - - int visCnt=0; - for(int i=0; i count(); i++) - if(!header_->isSectionHidden(i)) - visCnt++; - - QList lst; - QMenu *menu=new QMenu(this); - QAction *ac; - - for(int i=0; i count(); i++) - { - QString name=header_->model()->headerData(i,Qt::Horizontal).toString(); - ac=new QAction(menu); - ac->setText(name); - ac->setCheckable(true); - ac->setData(i); - - bool vis=!header_->isSectionHidden(i); - ac->setChecked(vis); - - if(vis && visCnt <=1) - { - ac->setEnabled(false); - } - - menu->addAction(ac); - } - - //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); - //VParamFilterMenu stateFilterMenu(menu,filterDef_->nodeState(),VParamFilterMenu::ColourDecor); - - ac=menu->exec(header_->mapToGlobal(position)); - if(ac && ac->isEnabled() && ac->isCheckable()) - { - int i=ac->data().toInt(); - header_->setSectionHidden(i,!ac->isChecked()); - } - delete menu; -} - -void TableNodeView::readSettings(VSettings* vs) -{ - vs->beginGroup("column"); - - std::vector orderVec; - std::vector visVec, wVec; - - vs->get("order",orderVec); - vs->get("visible",visVec); - vs->get("width",wVec); - - vs->endGroup(); - - if(orderVec.size() != visVec.size() || orderVec.size() != wVec.size()) - return; - - for(size_t i=0; i < orderVec.size(); i++) - { - std::string id=orderVec[i]; - for(int j=0; j < model_->columnCount(QModelIndex()); j++) - { - if(model_->headerData(j,Qt::Horizontal,Qt::UserRole).toString().toStdString() == id) - { - if(visVec[i] == 0) - header()->setSectionHidden(j,true); - - else if(wVec[i] > 0) - setColumnWidth(j,wVec[i]); - - break; - } - } - } - - if(header_->count() > 0) - { - int visCnt=0; - for(int i=0; i < header_->count(); i++) - if(!header_->isSectionHidden(i)) - visCnt++; - - if(visCnt==0) - header()->setSectionHidden(0,false); - } -} - -void TableNodeView::writeSettings(VSettings* vs) -{ - vs->beginGroup("column"); - - std::vector orderVec; - std::vector visVec, wVec; - for(int i=0; i < model_->columnCount(QModelIndex()); i++) - { - std::string id=model_->headerData(i,Qt::Horizontal,Qt::UserRole).toString().toStdString(); - orderVec.push_back(id); - visVec.push_back((header()->isSectionHidden(i))?0:1); - wVec.push_back(columnWidth(i)); - } - - vs->put("order",orderVec); - vs->put("visible",visVec); - vs->put("width",wVec); - - vs->endGroup(); -} - -//========================================= -// TableNodeHeader -//========================================= - -TableNodeHeader::TableNodeHeader(QWidget *parent) : QHeaderView(Qt::Horizontal, parent) -{ - setStretchLastSection(true); - - connect(this, SIGNAL(sectionResized(int, int, int)), - this, SLOT(slotSectionResized(int))); - - int pixId=IconProvider::add(":viewer/filter_decor.svg","filter_decor"); - - customPix_=IconProvider::pixmap(pixId,10); - - - //connect(this, SIGNAL(sectionMoved(int, int, int)), this, - // SLOT(handleSectionMoved(int, int, int))); - - //setMovable(true); -} - -void TableNodeHeader::showEvent(QShowEvent *e) -{ - /* for(int i=0;isetGeometry(sectionViewportPosition(i),0, - sectionSize(i),height()); - widgets_[i]->show(); - } - } - */ - - QHeaderView::showEvent(e); -} - -void TableNodeHeader::slotSectionResized(int i) -{ - /*for (int j=visualIndex(i);jsetGeometry(sectionViewportPosition(logical), height()/2, - sectionSize(logical) - 16, height()); - } - }*/ -} - -QSize TableNodeHeader::sizeHint() const -{ - return QHeaderView::sizeHint(); - - QSize s = size(); - //s.setHeight(headerSections[0]->minimumSizeHint().height() + 35); - //s.setHeight(2*35); - return s; -} - -void TableNodeHeader::setModel(QAbstractItemModel *model) -{ - if(model) - { - for(int i=0; i< model->columnCount(); i++) - { - QString id=model->headerData(i,Qt::Horizontal,Qt::UserRole).toString(); - if(id == "status") - customButton_.insert(i,TableNodeHeaderButton(id)); - } - } - QHeaderView::setModel(model); -} - -void TableNodeHeader::paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const -{ - painter->save(); - //QHeaderView::paintSection(painter, rect, logicalIndex); - //painter->restore(); - - - /*QPixmap customPix(":viewer/filter_decor.svg"); - QRect cbRect(0,0,12,12); - cbRect.moveCenter(QPoint(rect.right()-16-6,rect.center().y())); - customButton_[logicalIndex].setRect(cbRect); - painter->drawPixmap(cbRect,pix);*/ - - if (!rect.isValid()) - return; - - QStyleOptionHeader opt; - initStyleOption(&opt); - QStyle::State state = QStyle::State_None; - if(isEnabled()) - state |= QStyle::State_Enabled; - if(window()->isActiveWindow()) - state |= QStyle::State_Active; - - bool clickable; - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - clickable=sectionsClickable(); -#else - clickable=isClickable(); -#endif - - if(clickable) - { - /*if (logicalIndex == d->hover) - state |= QStyle::State_MouseOver; - if (logicalIndex == d->pressed) - state |= QStyle::State_Sunken; - else if (d->highlightSelected) { - if (d->sectionIntersectsSelection(logicalIndex)) - state |= QStyle::State_On; - if (d->isSectionSelected(logicalIndex)) - state |= QStyle::State_Sunken; - }*/ - - } - - // if(isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) - // opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder) - // ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; - - // setup the style options structure - //QVariant textAlignment = model->headerData(logicalIndex, d->orientation, - // Qt::TextAlignmentRole); - opt.rect = rect; - opt.section = logicalIndex; - opt.state |= state; - //opt.textAlignment = Qt::Alignment(textAlignment.isValid() - // ? Qt::Alignment(textAlignment.toInt()) - // : d->defaultAlignment); - - //opt.text = model()->headerData(logicalIndex, Qt::Horizontal), - // Qt::DisplayRole).toString(); - - QVariant foregroundBrush; - if (foregroundBrush.canConvert()) - opt.palette.setBrush(QPalette::ButtonText, qvariant_cast(foregroundBrush)); - - QPointF oldBO = painter->brushOrigin(); - QVariant backgroundBrush; - if (backgroundBrush.canConvert()) - { - opt.palette.setBrush(QPalette::Button, qvariant_cast(backgroundBrush)); - opt.palette.setBrush(QPalette::Window, qvariant_cast(backgroundBrush)); - painter->setBrushOrigin(opt.rect.topLeft()); - } - - // the section position - int visual = visualIndex(logicalIndex); - assert(visual != -1); - - if (count() == 1) - opt.position = QStyleOptionHeader::OnlyOneSection; - else if (visual == 0) - opt.position = QStyleOptionHeader::Beginning; - else if (visual == count() - 1) - opt.position = QStyleOptionHeader::End; - else - opt.position = QStyleOptionHeader::Middle; - - opt.orientation = Qt::Horizontal; - - // the selected position - /*bool previousSelected = d->isSectionSelected(logicalIndex(visual - 1)); - bool nextSelected = d->isSectionSelected(logicalIndex(visual + 1)); - if (previousSelected && nextSelected) - opt.selectedPosition = QStyleOptionHeader::NextAndPreviousAreSelected; - else if (previousSelected) - opt.selectedPosition = QStyleOptionHeader::PreviousIsSelected; - else if (nextSelected) - opt.selectedPosition = QStyleOptionHeader::NextIsSelected; - else - opt.selectedPosition = QStyleOptionHeader::NotAdjacent; - */ - - // draw the section - style()->drawControl(QStyle::CE_Header, &opt, painter, this); - painter->setBrushOrigin(oldBO); - - painter->restore(); - - - int rightPos=rect.right(); - if(isSortIndicatorShown() && sortIndicatorSection() == logicalIndex) - opt.sortIndicator = (sortIndicatorOrder() == Qt::AscendingOrder) - ? QStyleOptionHeader::SortDown : QStyleOptionHeader::SortUp; - if (opt.sortIndicator != QStyleOptionHeader::None) - { - QStyleOptionHeader subopt = opt; - subopt.rect = style()->subElementRect(QStyle::SE_HeaderArrow, &opt, this); - rightPos=subopt.rect.left(); - style()->drawPrimitive(QStyle::PE_IndicatorHeaderArrow, &subopt, painter, this); - } - - - QMap::iterator it=customButton_.find(logicalIndex); - if(it != customButton_.end()) - { - //Custom button - QStyleOptionButton optButton; - - //visPbOpt.text="Visualise"; - optButton.state = QStyle::State_AutoRaise ; //QStyle::State_Active | QStyle::State_Enabled; - //optButton.icon=customIcon_; - //optButton.iconSize=QSize(12,12); - - int buttonWidth=customPix_.width(); - int buttonHeight=buttonWidth; - optButton.rect = QRect(rightPos-4-buttonWidth,(rect.height()-buttonWidth)/2, - buttonWidth,buttonHeight); - - painter->drawPixmap(optButton.rect,customPix_); - - rightPos=optButton.rect.left(); - it.value().setRect(optButton.rect); - } - - QString text=model()->headerData(logicalIndex,Qt::Horizontal).toString(); - QRect textRect=rect; - textRect.setRight(rightPos-5); - - painter->drawText(textRect,Qt::AlignHCenter | Qt::AlignVCenter,text); - - - //style()->drawControl(QStyle::CE_PushButton, &optButton,painter,this); -} - -void TableNodeHeader::mousePressEvent(QMouseEvent *event) -{ - QMap::const_iterator it = customButton_.constBegin(); - while(it != customButton_.constEnd()) - { - if(it.value().rect_.contains(event->pos())) - { - UiLog().dbg() << "header " << it.key() << " clicked"; - Q_EMIT customButtonClicked(it.value().id(),event->globalPos()); - } - ++it; - } - - QHeaderView::mousePressEvent(event); -} - -/*void TableNodeHeader::mouseMoveEvent(QMouseEvent *event) -{ - int prevIndex=hoverIndex_; - QMap::const_iterator it = customButton_.constBegin(); - while(it != customButton_.constEnd()) - { - if(it.value().rect_.contains(event->pos())) - { - hoverIndex_=it.key(); - if(hoveIndex != prevIndex) - { - rerender; - } - } - ++it; - } - - if(preIndex !=-1) - { - - } - hoverIndex_=-1; -}*/ - diff -Nru ecflow-4.9.0/Viewer/src/TableNodeViewDelegate.cpp ecflow-4.11.1/Viewer/src/TableNodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/src/TableNodeViewDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,266 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TableNodeViewDelegate.hpp" - -#include -#include -#include -#include -#include - -#include - -#include "AbstractNodeModel.hpp" -#include "Animation.hpp" -#include "IconProvider.hpp" -#include "ModelColumn.hpp" -#include "PropertyMapper.hpp" - -static std::vector propVec; - -//Define node renderer properties -struct TableNodeDelegateBox : public NodeDelegateBox -{ - TableNodeDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=3; - rightMargin=0; - topPadding=0; - bottomPadding=0; - leftPadding=2; - rightPadding=1; - } -}; - -//Define attribute renderer properties -struct TableAttrDelegateBox : public AttrDelegateBox -{ - TableAttrDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=1; - rightMargin=0; - topPadding=0; - bottomPadding=0; - leftPadding=0; - rightPadding=0; - } -}; - -TableNodeViewDelegate::TableNodeViewDelegate(QWidget *parent) -{ - borderPen_=QPen(QColor(230,230,230)); - - columns_=ModelColumn::def("table_columns"); - - nodeBox_=new TableNodeDelegateBox; - attrBox_=new TableAttrDelegateBox; - - nodeBox_->adjust(font_); - attrFont_=font_; - attrBox_->adjust(attrFont_); - - //Property - if(propVec.empty()) - { - propVec.push_back("view.table.font"); - - //Base settings - addBaseSettings(propVec); - } - - prop_=new PropertyMapper(propVec,this); - - updateSettings(); -} - -TableNodeViewDelegate::~TableNodeViewDelegate() -{ -} - -void TableNodeViewDelegate::updateSettings() -{ - if(VProperty* p=prop_->find("view.table.font")) - { - QFont newFont=p->value().value(); - - if(font_ != newFont) - { - font_=newFont; - attrFont_=newFont; - nodeBox_->adjust(font_); - attrBox_->adjust(attrFont_); - Q_EMIT sizeHintChangedGlobal(); - } - } - - //Update the settings handled by the base class - updateBaseSettings(); -} - -QSize TableNodeViewDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const -{ - QSize size=QStyledItemDelegate::sizeHint(option,index); - return QSize(size.width(),nodeBox_->sizeHintCache.height()); -} - - -void TableNodeViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ - //Background -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QStyleOptionViewItem vopt(option); -#else - QStyleOptionViewItemV4 vopt(option); -#endif - - initStyleOption(&vopt, index); - - const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); - const QWidget* widget = vopt.widget; - - //Save painter state - painter->save(); - - QString id=columns_->id(index.column()); - - if(id == "path") - { - QString text=index.data(Qt::DisplayRole).toString(); - renderNode(painter,index,vopt,text); - } - else if(id == "status") - { - renderStatus(painter,index,vopt); - } - - //Render attributes - else if(id == "event" || id == "label" || id == "meter" || id == "trigger") - { - QVariant va=index.data(Qt::DisplayRole); - if(va.type() == QVariant::StringList) - { - QStringList lst=va.toStringList(); - if(lst.count() > 0) - { - QMap::const_iterator it=attrRenderers_.find(lst.at(0)); - if(it != attrRenderers_.end()) - { - QSize size; - AttributeRendererProc a=it.value(); - (this->*a)(painter,lst,vopt,size); - } - } - } - } - - //rest of the columns - else - { - QString text=index.data(Qt::DisplayRole).toString(); - QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); - painter->setFont(font_); - painter->setPen(Qt::black); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - } - - //Render the horizontal border for rows. We only render the top border line. - //With this technique we miss the bottom border line of the last row!!! - //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); - QRect bgRect=option.rect; - painter->setPen(borderPen_); - painter->drawLine(bgRect.topLeft(),bgRect.topRight()); - - painter->restore(); -} - -void TableNodeViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const -{ - bool selected=option.state & QStyle::State_Selected; - QFontMetrics fm(font_); - - //The initial filled rect (we will adjust its width) - QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); - - //The text rectangle - QRect textRect = itemRect; - - int textWidth=fm.width(text); - textRect.setWidth(textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding); - - //Adjust the filled rect width - int currentRight=textRect.x()+textRect.width(); - - //Icons area - QList pixLst; - QList pixRectLst; - - QVariant va=index.data(AbstractNodeModel::IconRole); - if(va.type() == QVariant::List) - { - QVariantList lst=va.toList(); - if(lst.count() >0) - { - int xp=currentRight+nodeBox_->iconPreGap; - int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; - for(int i=0; i < lst.count(); i++) - { - int id=lst[i].toInt(); - if(id != -1) - { - pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); - pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); - xp+=nodeBox_->iconSize+nodeBox_->iconGap; - } - } - - if(!pixLst.isEmpty()) - { - currentRight=xp-nodeBox_->iconGap; - } - } - } - - //Define clipping - int rightPos=currentRight+1; - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw text - QColor fg=index.data(Qt::ForegroundRole).value(); - painter->setPen(fg); - painter->setFont(font_); - painter->drawText(textRect,Qt::AlignHCenter | Qt::AlignVCenter,text); - - if(selected) - { - QRect sr=textRect; - sr.setX(option.rect.x()+nodeBox_->leftMargin); - renderSelectionRect(painter,sr); - } - - //Draw icons - for(int i=0; i < pixLst.count(); i++) - { - painter->drawPixmap(pixRectLst[i],pixLst[i]); - } - - if(setClipRect) - { - painter->restore(); - } -} diff -Nru ecflow-4.9.0/Viewer/src/TableNodeViewDelegate.hpp ecflow-4.11.1/Viewer/src/TableNodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/src/TableNodeViewDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TABLENODEVIEWDELEGATE_HPP -#define TABLENODEVIEWDELEGATE_HPP - -#include -#include -#include -#include -#include - -#include "NodeViewDelegate.hpp" -#include "VProperty.hpp" - -#include - -class ModelColumn; - -class TableNodeViewDelegate : public NodeViewDelegate -{ - Q_OBJECT -public: - explicit TableNodeViewDelegate(QWidget *parent=0); - ~TableNodeViewDelegate(); - - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - -Q_SIGNALS: - void sizeHintChangedGlobal(); - -protected: - void updateSettings(); - - void renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const; - - ModelColumn* columns_; - QPen borderPen_; -}; - -#endif // TABLENODEVIEWDELEGATE_HPP - diff -Nru ecflow-4.9.0/Viewer/src/TableNodeView.hpp ecflow-4.11.1/Viewer/src/TableNodeView.hpp --- ecflow-4.9.0/Viewer/src/TableNodeView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TABLENODEVIEW_HPP_ -#define TABLENODEVIEW_HPP_ - -#include -#include - -#include "NodeViewBase.hpp" - -#include "VInfo.hpp" -#include "VProperty.hpp" - -class QComboBox; - -class ActionHandler; -class TableNodeModel; -class TableNodeSortModel; -class NodeFilterDef; -class PropertyMapper; -class TableNodeHeader; - -class TableNodeView : public QTreeView, public NodeViewBase, public VPropertyObserver -{ -Q_OBJECT - -public: - - explicit TableNodeView(TableNodeSortModel* model,NodeFilterDef* filterDef,QWidget *parent=0); - ~TableNodeView(); - - void reload() {} - void rerender(); - QWidget* realWidget(); - QObject* realObject(); - VInfo_ptr currentSelection(); - void setCurrentSelection(VInfo_ptr n); - void selectFirstServer() {} - void setModel(TableNodeSortModel *model); - - void notifyChange(VProperty* p); - - void readSettings(VSettings*); - void writeSettings(VSettings*); - -public Q_SLOTS: - void slotDoubleClickItem(const QModelIndex&); - void slotContextMenu(const QPoint &position); - void slotViewCommand(VInfo_ptr,QString); - void slotHeaderContextMenu(const QPoint &position); - void slotSizeHintChangedGlobal(); - void slotRerender(); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - void infoPanelCommand(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - void headerButtonClicked(QString,QPoint); - -protected: - QModelIndexList selectedList(); - void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); - void adjustBackground(QColor col); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - - TableNodeSortModel* model_; - ActionHandler* actionHandler_; - TableNodeHeader* header_; - bool needItemsLayout_; - PropertyMapper* prop_; - bool setCurrentIsRunning_; -}; - -class TableNodeHeaderButton -{ -public: - TableNodeHeaderButton(QString id) : id_(id) {} - - QString id() const {return id_;} - void setRect(QRect r) {rect_=r;} - QRect rect() const {return rect_;} - - QString id_; - QRect rect_; -}; - -class TableNodeHeader : public QHeaderView -{ -Q_OBJECT - -public: - explicit TableNodeHeader(QWidget *parent=0); - - QSize sizeHint() const; - void setModel(QAbstractItemModel *model); - -public Q_SLOTS: - void slotSectionResized(int i); - -Q_SIGNALS: - void customButtonClicked(QString,QPoint); - -protected: - void showEvent(QShowEvent *QSize); - void paintSection(QPainter *painter, const QRect &rect, int logicalIndex) const; - void mousePressEvent(QMouseEvent *event); - - QPixmap customPix_; - mutable QMap customButton_; -}; - -#endif - - - diff -Nru ecflow-4.9.0/Viewer/src/TableNodeWidget.cpp ecflow-4.11.1/Viewer/src/TableNodeWidget.cpp --- ecflow-4.9.0/Viewer/src/TableNodeWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,222 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#include "TableNodeWidget.hpp" - -#include -#include - -#include "AbstractNodeModel.hpp" -#include "DashboardDock.hpp" -#include "FilterWidget.hpp" -#include "NodePathWidget.hpp" -#include "NodeViewBase.hpp" -#include "TableFilterWidget.hpp" -#include "TableNodeModel.hpp" -#include "TableNodeSortModel.hpp" -#include "TableNodeView.hpp" -#include "VFilter.hpp" -#include "VSettings.hpp" -#include "WidgetNameProvider.hpp" - -#include - -TableNodeWidget::TableNodeWidget(ServerFilter* serverFilter,QWidget * parent) : - NodeWidget("table",serverFilter,parent), - sortModel_(0) -{ - //Init qt-creator form - setupUi(this); - - bcWidget_=new NodePathWidget(this); - - //This defines how to filter the nodes in the tree. We only want to filter according to node status. - filterDef_=new NodeFilterDef(serverFilter_,NodeFilterDef::GeneralScope); - - //Create the table model. It uses the datahandler to access the data. - TableNodeModel* tModel=new TableNodeModel(serverFilter_,filterDef_,this); - model_=tModel; - - //Create a filter model for the tree. - sortModel_=new TableNodeSortModel(tModel,this); - - //Build the filter widget - filterW_->build(filterDef_,serverFilter_); - - //Create the view - QHBoxLayout *hb=new QHBoxLayout(viewHolder_); - hb->setContentsMargins(0,0,0,0); - hb->setSpacing(0); - TableNodeView *tv=new TableNodeView(sortModel_,filterDef_,this); - hb->addWidget(tv); - - //Store the pointer to the (non-QObject) base class of the view!!! - view_=tv; - - //Signals-slots - - connect(view_->realWidget(),SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChangedInView(VInfo_ptr))); - - connect(view_->realWidget(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(popInfoPanel(VInfo_ptr,QString))); - - connect(view_->realWidget(),SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); - - connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), - this,SLOT(slotSelectionChangedInBc(VInfo_ptr))); - - connect(view_->realWidget(),SIGNAL(headerButtonClicked(QString,QPoint)), - filterW_,SLOT(slotHeaderFilter(QString,QPoint))); - -#if 0 - connect(model_,SIGNAL(clearBegun(const VNode*)), - view_->realWidget(),SLOT(slotSaveExpand(const VNode*))); - - connect(model_,SIGNAL(scanEnded(const VNode*)), - view_->realWidget(),SLOT(slotRestoreExpand(const VNode*))); -#endif - - connect(model_,SIGNAL(rerender()), - view_->realWidget(),SLOT(slotRerender())); - - //This will not emit the trigered signal of the action!! - //Synchronise the action and the breadcrumbs state - actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); - - //The node status filter is exposed via a menu. So we need a reference to it. - states_=filterDef_->nodeState(); - - WidgetNameProvider::nameChildren(this); -} - -TableNodeWidget::~TableNodeWidget() -{ - -} - -void TableNodeWidget::populateDockTitleBar(DashboardDockTitleWidget* tw) -{ - //Builds the menu for the settings tool button - QMenu *menu=new QMenu(this); - menu->setTearOffEnabled(true); - - menu->addAction(actionBreadcrumbs); - -#if 0 - QMenu *menuState=menu->addMenu(tr("Status")); - menuState->setTearOffEnabled(true); - - //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); - stateFilterMenu_=new VParamFilterMenu(menuState,states_,"Status filter", - //VParamFilterMenu::FilterMode,VParamFilterMenu::ColourDecor); - VParamFilterMenu::ShowMode,VParamFilterMenu::ColourDecor); - -#endif - //Sets the menu on the toolbutton - tw->optionsTb()->setMenu(menu); - - //Add the bc to the titlebar - tw->setBcWidget(bcWidget_); - - //Sets the title - //tw->slotUpdateTitle("Table"); - - QList acLst; - QAction* acFilterEdit=new QAction(this); - acFilterEdit->setIcon(QPixmap(":viewer/filter_edit.svg")); - acFilterEdit->setToolTip("Edit filter ..."); - acLst << acFilterEdit; - - connect(acFilterEdit,SIGNAL(triggered()), - filterW_,SLOT(slotEdit())); - - tw->addActions(acLst); -} - - -void TableNodeWidget::slotSelectionChangedInView(VInfo_ptr info) -{ - updateActionState(info); - bcWidget_->setPath(info); - if(broadcastSelection()) - Q_EMIT selectionChanged(info); -} - -void TableNodeWidget::on_actionBreadcrumbs_triggered(bool b) -{ - if(b) - { - bcWidget_->setMode(NodePathWidget::GuiMode); - } - else - { - bcWidget_->setMode(NodePathWidget::TextMode); - } -} - -void TableNodeWidget::rerender() -{ - bcWidget_->rerender(); - view_->rerender(); -} - - -void TableNodeWidget::writeSettings(VComboSettings* vs) -{ - vs->put("type",type_); - vs->put("dockId",id_); - - bcWidget_->writeSettings(vs); - - states_->writeSettings(vs); - filterDef_->writeSettings(vs); - - view_->writeSettings(vs); - - DashboardWidget::writeSettings(vs); -} - -void TableNodeWidget::readSettings(VComboSettings* vs) -{ - std::string type=vs->get("type",""); - if(type != type_) - return; - - //This will not emit the changed signal. So the "observers" will - //not notice the change. - states_->readSettings(vs); - filterDef_->readSettings(vs); - - //The model at this point is inactive (not using its data). We make it active: - // -it will instruct its data provider to filter the data according - // to the current settings - // -it will load and display the data - model_->active(true); - - //-------------------------- - //Breadcrumbs - //-------------------------- - - bcWidget_->readSettings(vs); - - //Synchronise the action and the breadcrumbs state - //This will not emit the trigered signal of the action!! - actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); - - view_->readSettings(vs); - - DashboardWidget::readSettings(vs); -} - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TableNodeWidget.hpp ecflow-4.11.1/Viewer/src/TableNodeWidget.hpp --- ecflow-4.9.0/Viewer/src/TableNodeWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#ifndef TABLENODEWIDGET_HPP_ -#define TABLENODEWIDGET_HPP_ - -#include "ui_TableNodeWidget.h" - -#include "NodeWidget.hpp" - -class NodeStateFilter; -class TableNodeSortModel; -class VParamFilterMenu; -class VSettings; - -class TableNodeWidget : public NodeWidget, protected Ui::TableNodeWidget -{ -Q_OBJECT - -public: - TableNodeWidget(ServerFilter* servers,QWidget* parent=0); - ~TableNodeWidget(); - - void populateDockTitleBar(DashboardDockTitleWidget* tw); - void rerender(); - - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - -protected Q_SLOTS: - void on_actionBreadcrumbs_triggered(bool b); - void slotSelectionChangedInView(VInfo_ptr info); - -protected: - void detachedChanged() {} - -private: - TableNodeSortModel *sortModel_; - VParamFilterMenu *stateFilterMenu_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/TableNodeWidget.ui ecflow-4.11.1/Viewer/src/TableNodeWidget.ui --- ecflow-4.9.0/Viewer/src/TableNodeWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TableNodeWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ - - - TableNodeWidget - - - - 0 - 0 - 683 - 491 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - 1 - - - - - - - - 0 - 0 - - - - - - - - - - - true - - - Breadcrumbs - - - Show breadcrumbs - - - - - - TableFilterWidget - QWidget -
      TableFilterWidget.hpp
      - 1 -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/TabWidget.cpp ecflow-4.11.1/Viewer/src/TabWidget.cpp --- ecflow-4.9.0/Viewer/src/TabWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TabWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,403 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TabWidget.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "UserMessage.hpp" - -void IconTabBar::paintEvent(QPaintEvent *e) -{ - QStylePainter painter(this); - for(int i = 0; i < count(); ++i) - { - QStyleOptionTabV2 option; - initStyleOption(&option, i); - painter.drawItemPixmap(option.rect, Qt::AlignTop|Qt::AlignHCenter, option.icon.pixmap(option.iconSize)); - //painter.drawItemText(option.rect, Qt::AlignBottom|Qt::AlignHCenter, palette(), 1, option.text); - } -} - -TabWidget::TabWidget(QWidget* parent) : - QWidget(parent), - beingCleared_(false) -{ - //Main layout - QVBoxLayout* layout = new QVBoxLayout(this); - layout->setSpacing(0); - layout->setContentsMargins(0, 0, 0, 0); - - //Horizontal layout for the tab bar - QHBoxLayout* hb = new QHBoxLayout(this); - hb->setSpacing(0); - hb->setContentsMargins(0, 0, 0, 0); - layout->addLayout(hb); - - //Tab bar - bar_ = new QTabBar(this); - bar_->setObjectName("bar"); - hb->addWidget(bar_, 1); - - bar_->setProperty("nodePanel","1"); - bar_->setMovable(true); - //bar_->setExpanding(true); - - //QString st=bar_->styleSheet(); - //st+="QTabBar::tab{padding: 4px;}"; - //st+="QTabBar::tab {margin-left: 4px;}"; - //st+="QTabBar::tab:selected {font: bold;}"; - //bar_->setStyleSheet(st); - - //Add tab button on the right - addTb_ = new QToolButton(this); - addTb_->setObjectName("addTb"); - addTb_->setAutoRaise(true); - addTb_->setIcon(QPixmap(":/viewer/add_tab.svg")); - addTb_->setToolTip(tr("Open a new tab")); - hb->addWidget(addTb_); - - //Tab list menu - tabListTb_=new QToolButton(this); - tabListTb_->setObjectName("tabListTb"); - tabListTb_->setAutoRaise(true); - tabListTb_->setIcon(QPixmap(":/viewer/menu_arrow_down.svg")); - tabListTb_->setToolTip(tr("List all tabs")); - hb->addWidget(tabListTb_); - - //Stacked widget to store the actual tab widgets - stacked_ = new QStackedWidget(this); - stacked_->setObjectName("stacked"); - stacked_->setMinimumHeight(1); - stacked_->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); - layout->addWidget(stacked_); - - //Context menu for tha tabs - bar_->setContextMenuPolicy(Qt::CustomContextMenu); - - connect(bar_,SIGNAL(customContextMenuRequested(const QPoint &)), - this,SLOT(slotContextMenu(const QPoint &))); - - connect(bar_,SIGNAL(tabMoved(int,int)), - this,SLOT(tabMoved(int,int))); - - connect(bar_,SIGNAL(currentChanged(int)), - this,SLOT(currentTabChanged(int))); - - connect(bar_,SIGNAL(tabCloseRequested(int)), - this,SLOT(removeTab(int))); - - connect(addTb_,SIGNAL(clicked()), - this,SIGNAL(newTabRequested())); - - connect(tabListTb_,SIGNAL(clicked()), - this,SLOT(slotTabList())); -} - -void TabWidget::slotContextMenu(const QPoint& pos) { - - if (pos.isNull()) - return; - - int index = bar_->tabAt(pos); - if(index < 0 || index > bar_->count()) - return; - - QList lst; - QAction *closeAc=new QAction(QPixmap(":/viewer/close.svg"),"&Close tab",this); - lst << closeAc; - - if(QAction *ac=QMenu::exec(lst,mapToGlobal(pos),closeAc,this)) - { - if(ac == closeAc) - removeTab(index); - } - - qDeleteAll(lst); - - - /*MvQContextItemSet *cms = cmSet(); - if (!cms) - return; - - int index = bar_->tabAt(pos); - - QString selection = MvQContextMenu::instance()->exec(cms->icon(), - mapToGlobal(pos), this, - //QString::number(bar_->count())); - "path=" + folderPath(index)); - if (!selection.isEmpty()) - tabBarCommand(selection, index);*/ -} - -int TabWidget::count() const -{ - return bar_->count(); -} - -int TabWidget::currentIndex() const -{ - return bar_->currentIndex(); -} - -void TabWidget::setCurrentIndex(int index) -{ - bar_->setCurrentIndex(index); -} - -QWidget* TabWidget::widget(int index) const -{ - if (index >= 0 && index < bar_->count()) - { - return stacked_->widget(index); - } - - return 0; -} - -QWidget* TabWidget::currentWidget() const -{ - return widget(bar_->currentIndex()); -} - -int TabWidget::indexOfWidget(QWidget *w) const -{ - for (int i = 0; i < stacked_->count(); i++) - if (w == stacked_->widget(i)) - return i; - - return -1; -} - -void TabWidget::clear() -{ - beingCleared_=true; - while (bar_->count() > 0) { - removeTab(0); - } - beingCleared_=false; -} - -void TabWidget::addTab(QWidget *w, QPixmap pix, QString name) -{ - stacked_->addWidget(w); - bar_->addTab(pix, name); - bar_->setCurrentIndex(count() - 1); - checkTabStatus(); -} - -void TabWidget::removeTab(int index) -{ - if (index >= 0 && index < bar_->count()) { - QWidget *w = stacked_->widget(index); - stacked_->removeWidget(w); - bar_->removeTab(index); - w->hide(); - w->deleteLater(); - - Q_EMIT tabRemoved(); - } - - checkTabStatus(); -} - -void TabWidget::removeOtherTabs(int index) -{ - QWidget *actW = stacked_->widget(index); - - while (bar_->count() > 0) { - if (stacked_->widget(0) != actW) { - removeTab(0); - } else - break; - } - - while (bar_->count() > 1) { - if (stacked_->widget(1) != actW) { - removeTab(1); - } - } - - checkTabStatus(); -} - -void TabWidget::currentTabChanged(int index) -{ - if (stacked_->count() == bar_->count()) { - stacked_->setCurrentIndex(index); - Q_EMIT currentIndexChanged(index); - - checkTabStatus(); - } -} - -void TabWidget::tabMoved(int from, int to) -{ - QWidget *w = stacked_->widget(from); - stacked_->removeWidget(w); - stacked_->insertWidget(to, w); - - //bar_->setCurrentIndex(to); - currentTabChanged(to); -} - -void TabWidget::setTabText(int index, QString txt) -{ - if (index >= 0 && index < bar_->count()) { - bar_->setTabText(index, txt); - } -} - -void TabWidget::setTabToolTip(int index, QString txt) -{ - if (index >= 0 && index < bar_->count()) { - bar_->setTabToolTip(index, txt); - } -} - -void TabWidget::setTabWht(int index, QString txt) -{ - if (index >= 0 && index < bar_->count()) { - bar_->setTabWhatsThis(index, txt); - } -} - -void TabWidget::setTabData(int index, QPixmap pix) -{ - if (index >= 0 && index < bar_->count()) { - bar_->setTabData(index,QIcon(pix)); - } -} - -void TabWidget::setTabIcon(int index, QPixmap pix) -{ - if (index >= 0 && index < bar_->count()) - { - QLabel *lab=static_cast(bar_->tabButton(index,QTabBar::RightSide)); - if(!lab) - { - lab=new QLabel(); - lab->setAlignment(Qt::AlignCenter); - } - else - { - bar_->setTabButton(index,QTabBar::RightSide,0); - } - lab->setPixmap(pix); - lab->setFixedSize(pix.size()); - bar_->setTabButton(index,QTabBar::RightSide,lab); - -#if 0 - QSize maxSize=maxIconSize(); - - if(maxSize.width() < pix.width()) - maxSize.setWidth(pix.width()); - - if(maxSize.height() < pix.height()) - maxSize.setHeight(pix.height()); - - if(maxSize != bar_->iconSize()) - bar_->setIconSize(maxSize); - - bar_->setTabIcon(index, QIcon(pix)); -#endif - - } -} - -#if 0 -QSize TabWidget::maxIconSize() const -{ - QSize maxSize(0,0); - for(int i=0; i < bar_->count(); i++) - { - if(bar_->tabIcon(i).availableSizes().count() > 0) - { - QSize avs=bar_->tabIcon(i).availableSizes().front(); - if(maxSize.width() < avs.width()) - maxSize.setWidth(avs.width()); - - if(maxSize.height() < avs.height()) - maxSize.setHeight(avs.height()); - } - } - return maxSize; -} -#endif - -void TabWidget::checkTabStatus() -{ - if (bar_->count() > 1) - { - bar_->show(); - //bar_->setTabsClosable(true); - addTb_->show(); - tabListTb_->show(); - } - else - { - bar_->hide(); - //bar_->setTabsClosable(false); - addTb_->hide(); - tabListTb_->hide(); - } - - /* - for (int i = 0; i < bar_->count(); i++) - { - if (QWidget *w = bar_->tabButton(i, QTabBar::RightSide)) - { - if (i == bar_->currentIndex()) - w->show(); - else - w->hide(); - } - }*/ -} - -void TabWidget::slotTabList() -{ - QMenu* menu=new QMenu(tabListTb_); - - for(int i=0; i < bar_->count(); i++) - { - QAction *ac=new QAction(menu); - ac->setText(bar_->tabWhatsThis(i)); - ac->setIcon(bar_->tabData(i).value()); - ac->setData(i); - if(i==bar_->currentIndex()) - { - QFont font; - font.setBold(true); - ac->setFont(font); - } - - menu->addAction(ac); - } - - if(QAction *ac=menu->exec(QCursor::pos())) - { - int index=ac->data().toInt(); - if(index >=0 && index < count()) - { - setCurrentIndex(index); - } - } - - menu->clear(); - menu->deleteLater(); -} diff -Nru ecflow-4.9.0/Viewer/src/TabWidget.hpp ecflow-4.11.1/Viewer/src/TabWidget.hpp --- ecflow-4.9.0/Viewer/src/TabWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TabWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,84 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TABWIDGET_HPP_ -#define TABWIDGET_HPP_ - -#include -#include - -class QMenu; -class QStackedWidget; -class QTabBar; -class QToolButton; -class QVBoxLayout; - -class IconTabBar : public QTabBar -{ -public: - IconTabBar(QWidget* parent=0) : QTabBar(parent) {} -protected: - void paintEvent(QPaintEvent *e); -}; - - -class TabWidget : public QWidget -{ - Q_OBJECT - -public: - explicit TabWidget(QWidget *parent=0); - - int currentIndex() const; - int indexOfWidget(QWidget*) const; - QWidget *widget(int) const; - QWidget *currentWidget() const; - void checkTabStatus(); - void addTab(QWidget *,QPixmap,QString); - void setTabText(int,QString); - void setTabIcon(int,QPixmap); - void setTabToolTip(int,QString); - void setTabWht(int,QString); - void setTabData(int,QPixmap); - int count() const; - void clear(); - bool beingCleared() const {return beingCleared_;} - -public Q_SLOTS: - void removeTab(int); - void removeOtherTabs(int); - void setCurrentIndex(int); - -private Q_SLOTS: - void slotContextMenu(const QPoint&); - void currentTabChanged(int index); - void tabMoved(int from,int to); - void slotTabList(); - -Q_SIGNALS: - void currentIndexChanged(int); - void newTabRequested(); - void tabRemoved(); - -protected: - //virtual MvQContextItemSet* cmSet()=0; - virtual void tabBarCommand(QString,int)=0; - -private: -#if 0 - QSize maxIconSize() const; -#endif - QTabBar *bar_; - QStackedWidget *stacked_; - QToolButton* addTb_; - QToolButton* tabListTb_; - bool beingCleared_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextEditSearchLine.cpp ecflow-4.11.1/Viewer/src/TextEditSearchLine.cpp --- ecflow-4.9.0/Viewer/src/TextEditSearchLine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextEditSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,249 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - - -#include "TextEditSearchLine.hpp" - -#include "AbstractTextEditSearchInterface.hpp" - -#include -#include -#include -#include -#include -#include - -//#include "UserMessage.hpp" - -TextEditSearchLine::TextEditSearchLine(QWidget *parent) : - AbstractSearchLine(parent), - interface_(0), - lastFindSuccessful_(false) -{ - connect(matchModeCb_,SIGNAL(currentIndexChanged(int)), - this, SLOT(matchModeChanged(int))); -} - -TextEditSearchLine::~TextEditSearchLine() -{ - -} - -void TextEditSearchLine::setSearchInterface(AbstractTextEditSearchInterface *e) -{ - interface_=e; -} - -bool TextEditSearchLine::findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, QTextCursor::MoveOperation move, int iteration) -{ - QTextDocument::FindFlags flags = findFlags() | extraFlags; - lastFindSuccessful_ = interface_->findString(str,highlightAll,flags,move,iteration,matchModeCb_->currentMatchMode()); - return lastFindSuccessful_; -} - -void TextEditSearchLine::highlightMatches(QString txt) -{ - if(interface_) - { - interface_->enableHighlights(); - if(interface_->highlightsNeedSearch() && !txt.isEmpty()) - { - findString(txt, true, 0, QTextCursor::StartOfWord, 0); // highlight all matches - } - } -} - -void TextEditSearchLine::slotHighlight() -{ - //UserMessage::message(UserMessage::DBG, false," highlight: " + searchLine_->text().toStdString()); - - highlightAllTimer_.stop(); - - if (highlightAll()) - highlightMatches(searchLine_->text()); -} - -//This slot is called as we type in the search string -void TextEditSearchLine::slotFind(QString txt) -{ - if(!interface_) - return; - - //In confirmSearch mode we do not start the search - if(confirmSearch_) - { - toDefaultState(); - return; - } - - if(txt.isEmpty()) - { - highlightAllTimer_.stop(); - toDefaultState(); - return; - } - - highlightAllTimer_.stop(); - bool found = findString(txt, false, 0, QTextCursor::StartOfWord, 0); // find the next match - lastFindSuccessful_ = found; - - if (!isEmpty()) // there is a search term supplied by the user - { - // don't highlight the matches immediately - this can be expensive for large files, - // and we don't want to highlight each time the user types a new character; wait - // a moment and then start the highlight - highlightAllTimer_.setInterval(500); - highlightAllTimer_.disconnect(); - connect(&highlightAllTimer_, SIGNAL(timeout()), this, SLOT(slotHighlight())); - highlightAllTimer_.start(); - } - else - { - clearHighlights(); - } - - updateButtons(found); -} - -void TextEditSearchLine::slotFindNext() -{ - if(!interface_) - return; - - lastFindSuccessful_ = findString(searchLine_->text(), false, 0, QTextCursor::NoMove, 0); - updateButtons(lastFindSuccessful_); -} - -void TextEditSearchLine::slotFindPrev() -{ - if(!interface_) - return; - - lastFindSuccessful_ = findString(searchLine_->text(), false, QTextDocument::FindBackward, QTextCursor::NoMove, 0); - updateButtons(lastFindSuccessful_); -} - -QTextDocument::FindFlags TextEditSearchLine::findFlags() -{ - QTextDocument::FindFlags flags; - - if(caseSensitive()) - { - flags = flags | QTextDocument::FindCaseSensitively; - } - - if(wholeWords()) - { - flags = flags | QTextDocument::FindWholeWords; - } - - return flags; -} - - -// EditorSearchLine::refreshSearch -// performed when the user changes search parameters such as case sensitivity - we want to -// re-do the search from the current point, but if the current selection still matches then -// we'd like it to be found first. - -void TextEditSearchLine::refreshSearch() -{ - if(!interface_) - return; - - // if there's something selected already then move the cursor to the start of the line and search again - interface_->refreshSearch(); - - slotFindNext(); - slotHighlight(); -} - -void TextEditSearchLine::disableHighlights() -{ - if(interface_) - interface_->disableHighlights(); -} - - -void TextEditSearchLine::clearHighlights() -{ - if(interface_) - interface_->clearHighlights(); -} - -void TextEditSearchLine::matchModeChanged(int notUsed) -{ - if(matchModeCb_->currentMatchMode() == StringMatchMode::ContainsMatch) - actionWholeWords_->setEnabled(true); - else - actionWholeWords_->setEnabled(false); - - refreshSearch(); -} - - -void TextEditSearchLine::on_actionCaseSensitive__toggled(bool b) -{ - AbstractSearchLine::on_actionCaseSensitive__toggled(b); - - refreshSearch(); -} - - -void TextEditSearchLine::on_actionWholeWords__toggled(bool b) -{ - AbstractSearchLine::on_actionWholeWords__toggled(b); - - refreshSearch(); -} - -void TextEditSearchLine::on_actionHighlightAll__toggled(bool b) -{ - AbstractSearchLine::on_actionHighlightAll__toggled(b); - - if (b) // user switched on the highlights - slotHighlight(); - else // user switched off the highlights - disableHighlights(); - - if(interface_ && interface_->highlightsNeedSearch()) - refreshSearch(); -} - -void TextEditSearchLine::slotClose() -{ - AbstractSearchLine::slotClose(); - clearHighlights(); -} - -// Called when we load a new node's information into the panel, or -// when we move to the panel from another one. -// If the search box is open, then search for the first matching item; -// if not found, go to the last line. -// If the search box is not open, search for a pre-configured list of -// keywords. If none are found, and the user has clicked on the 'reload' -// button then we just go to the last line of the output -void TextEditSearchLine::searchOnReload(bool userClickedReload) -{ - if (isVisible() && !isEmpty()) - { - slotFindNext(); - slotHighlight(); - if(!lastFindSuccessful()) - interface_->gotoLastLine(); - } - else if(interface_) - { - // search for a highlight any of the pre-defined keywords so that - // the (probably) most important piece of information is highlighted - interface_->automaticSearchForKeywords(userClickedReload); - } -} - diff -Nru ecflow-4.9.0/Viewer/src/TextEditSearchLine.hpp ecflow-4.11.1/Viewer/src/TextEditSearchLine.hpp --- ecflow-4.9.0/Viewer/src/TextEditSearchLine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextEditSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ -#define VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ - -#include -#include - -#include "AbstractSearchLine.hpp" - -class AbstractTextEditSearchInterface; - -class TextEditSearchLine : public AbstractSearchLine -{ - Q_OBJECT - -public: - explicit TextEditSearchLine(QWidget *parent); - ~TextEditSearchLine(); - void setSearchInterface(AbstractTextEditSearchInterface*); - void searchOnReload(bool userClickedReload); - bool hasInterface() const {return interface_ != 0;} - -public Q_SLOTS: - void slotFind(QString); - void slotFindNext(); - void slotFindPrev(); - void slotFindNext(bool) {slotFindNext();} - void slotFindPrev(bool) {slotFindPrev();} - void matchModeChanged(int newIndex); - void on_actionCaseSensitive__toggled(bool); - void on_actionWholeWords__toggled(bool); - void on_actionHighlightAll__toggled(bool); - void slotClose(); - void slotHighlight(); - -protected: - QTextDocument::FindFlags findFlags(); - bool findString (QString str, bool highlightAll, QTextDocument::FindFlags extraFlags, QTextCursor::MoveOperation move, int iteration); - void refreshSearch(); - void highlightMatches(QString txt); - void clearHighlights(); - void disableHighlights(); - bool lastFindSuccessful() {return lastFindSuccessful_;} - - AbstractTextEditSearchInterface* interface_; - QTimer highlightAllTimer_; - QColor highlightColour_; - bool lastFindSuccessful_; -}; - -#endif /* VIEWER_SRC_TEXTEDITSEARCHLINE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TextFilterAddDialog.ui ecflow-4.11.1/Viewer/src/TextFilterAddDialog.ui --- ecflow-4.9.0/Viewer/src/TextFilterAddDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,118 +0,0 @@ - - - TextFilterAddDialog - - - - 0 - 0 - 378 - 176 - - - - Dialog - - - - - - - - - - - - - - Rege&xp: - - - label - - - - - - - Match mode: - - - - - - - - - Case sensitive - - - - - - - Add to context menu - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - buttonBox - accepted() - TextFilterAddDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - TextFilterAddDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TextFilterHandler.cpp ecflow-4.11.1/Viewer/src/TextFilterHandler.cpp --- ecflow-4.9.0/Viewer/src/TextFilterHandler.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterHandler.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,242 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TextFilterHandler.hpp" - -#include -#include - -#include "SessionHandler.hpp" -#include "VSettings.hpp" - -TextFilterHandler* TextFilterHandler::instance_=0; - -//============================================== -// -// TextFilterItem -// -//============================================== - -void TextFilterItem::save(VSettings *vs) const -{ - vs->put("filter", filter_); - vs->putAsBool("matched", matched_); - vs->putAsBool("caseSensitive",caseSensitive_); - vs->putAsBool("contextMenu",contextMenu_); -} - -TextFilterItem TextFilterItem::make(VSettings* vs) -{ - std::string emptyDefault=""; - std::string filter = vs->get("filter", emptyDefault); - bool matched = vs->getAsBool("matched",true); - bool caseSensitive = vs->getAsBool("filter",false); - bool contextMenu = vs->getAsBool("contextMenu",false); - return TextFilterItem(filter,matched,caseSensitive,contextMenu); -} - -bool TextFilterItem::operator ==(const TextFilterItem& o) const -{ - return filter_ == o.filter_ && - matched_ == o.matched_ && caseSensitive_ == o.caseSensitive_; -} - -//============================================== -// -// TextFilterHandler -// -//============================================== - -TextFilterHandler* TextFilterHandler::Instance() -{ - if(!instance_) - instance_=new TextFilterHandler(); - - return instance_; -} - -TextFilterHandler::TextFilterHandler() : - maxLatestNum_(5) -{ - readSettings(); -} - -int TextFilterHandler::indexOf(const std::string& filter,bool matched,bool caseSensitive) const -{ - if(filter.empty()) - return -1; - - TextFilterItem item(filter,matched,caseSensitive); - for(size_t i=0; i < items_.size(); i++) - { - if(items_[i] == item) - return i; - } - - return -1; -} - -bool TextFilterHandler::contains(const std::string& filter,bool matched,bool caseSensitive) const -{ - return indexOf(filter,matched,caseSensitive) != -1; -} - -bool TextFilterHandler::containsExceptOne(int index,const std::string& filter,bool matched,bool caseSensitive) const -{ - if(filter.empty()) - return false; - - TextFilterItem item(filter,matched,caseSensitive); - for(int i=0; i < static_cast(items_.size()); i++) - { - if(i!= index && items_[i] == item) - return true; - } - return false; -} - -bool TextFilterHandler::add(const TextFilterItem& item) -{ - if(item.filter().empty()) - return false; - - items_.push_back(item); - writeSettings(); - return true; -} - - -bool TextFilterHandler::add(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu) -{ - TextFilterItem item(filter,matched,caseSensitive,contextMenu); - return add(item); -} - -void TextFilterHandler::addLatest(const TextFilterItem& item) -{ - if(item.filter().empty()) - return; - - //Remove if exists - std::vector::iterator it=std::find(latest_.begin(),latest_.end(),item) ; - if(it != latest_.end()) - latest_.erase(it); - - //trim size - while(static_cast(latest_.size()) >= maxLatestNum_) - { - latest_.pop_back(); - } - - //add item to front - latest_.insert(latest_.begin(),item); - - writeSettings(); -} - -void TextFilterHandler::addLatest(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu) -{ - TextFilterItem item(filter,matched,caseSensitive,contextMenu); - addLatest(item); -} - -void TextFilterHandler::remove(int index) -{ - if(index < 0 || index >= static_cast(items_.size())) - return; - - items_.erase(items_.begin()+index); - writeSettings(); -} - -void TextFilterHandler::update(int index,const TextFilterItem& item) -{ - if(index < 0 || index >= static_cast(items_.size())) - return; - - items_[index]=item; - writeSettings(); -} - -void TextFilterHandler::allFilters(std::set& v) -{ - v.clear(); - for(std::size_t i = 0; i < items_.size() ; i++) - { - v.insert(items_[i].filter()); - } - for(std::size_t i = 0; i < latest_.size() ; i++) - { - v.insert(latest_[i].filter()); - } -} - -std::string TextFilterHandler::settingsFile() -{ - SessionItem* cs=SessionHandler::instance()->current(); - return cs->textFilterFile(); -} - -void TextFilterHandler::writeSettings() -{ - std::string dummyFileName="dummy"; - std::string settingsFilePath = settingsFile(); - VSettings vs(settingsFilePath); - - std::vector vsItems; - for(std::size_t i = 0; i < items_.size() ; i++) - { - VSettings vsThisItem(dummyFileName); - items_[i].save(&vsThisItem); - vsItems.push_back(vsThisItem); - } - vs.put("saved",vsItems); - - vsItems.clear(); - for(std::size_t i = 0; i < latest_.size() ; i++) - { - VSettings vsThisItem(dummyFileName); - latest_[i].save(&vsThisItem); - vsItems.push_back(vsThisItem); - } - vs.put("latest",vsItems); - - vs.write(); -} - -void TextFilterHandler::readSettings() -{ - std::string settingsFilePath = settingsFile(); - VSettings vs(settingsFilePath); - - bool ok = vs.read(false); // false means we don't abort if the file is not there - - if(ok) - { - std::vector vsItems; - vs.get("saved",vsItems); - for (std::size_t i = 0; i < vsItems.size(); i++) - { - add(TextFilterItem::make(&vsItems[i])); - } - - vsItems.clear(); - vs.get("latest",vsItems); - for (std::size_t i = 0; i < vsItems.size(); i++) - { - addLatest(TextFilterItem::make(&vsItems[i])); - } - } - //If there is no settings file at all we automatically add this filter - else if(!vs.fileExists()) - { - add(TextFilterItem("^\\+\\s",false,false)); - } -} diff -Nru ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.cpp ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.cpp --- ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,305 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TextFilterHandlerDialog.hpp" - -#include "TextFilterHandler.hpp" -#include "SessionHandler.hpp" - -#include -#include -#include -#include - -//====================================== -// -// TextFilterAddDialog -// -//====================================== - -TextFilterAddDialog::TextFilterAddDialog(QWidget *parent) : - QDialog(parent) -{ - setupUi(this); - - setWindowTitle(tr("Add item")); - - //match - matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_match.svg")),tr("match"),0); - matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_no_match.svg")),tr("no match"),1); -} - -TextFilterItem TextFilterAddDialog::item() -{ - return TextFilterItem(filterLe_->text().toStdString(),(matchCb_->currentIndex() == 0), - caseCb_->isChecked(),menuCb_->isChecked()); -} - -void TextFilterAddDialog::init(const TextFilterItem& item) -{ - filterLe_->setText(QString::fromStdString(item.filter())); - matchCb_->setCurrentIndex(item.matched()?0:1); - caseCb_->setChecked(item.caseSensitive()); - menuCb_->setChecked(item.contextMenu()); -} - -void TextFilterAddDialog::accept() -{ - TextFilterItem it=item(); - if(TextFilterHandler::Instance()->contains(it.filter(),it.matched(),it.caseSensitive())) - { - QMessageBox::critical(0,tr("Save text filter"), "Cannot save text filter! A text filter with the same regexp: " + - QString::fromStdString(it.filter()) + - " and settings already exists!"); - return; - } - - TextFilterHandler::Instance()->add(it); - QDialog::accept(); -} - - -//====================================== -// -// TextFilterEditDialog -// -//====================================== - -TextFilterEditDialog::TextFilterEditDialog(QWidget *parent) : - TextFilterAddDialog(parent), itemIndex_(-1) -{ - setWindowTitle(tr("Edit item")); -} - -void TextFilterEditDialog::init(int itemIndex,const TextFilterItem& item) -{ - itemIndex_=itemIndex; - TextFilterAddDialog::init(item); -} - -void TextFilterEditDialog::accept() -{ - if(itemIndex_ >= 0) - { - TextFilterItem it=item(); - - if(TextFilterHandler::Instance()->items()[itemIndex_] == it && - TextFilterHandler::Instance()->items()[itemIndex_].contextMenu() == - it.contextMenu()) - { - QDialog::reject(); - return; - } - - if(TextFilterHandler::Instance()->containsExceptOne(itemIndex_,it.filter(),it.matched(),it.caseSensitive())) - { - QMessageBox::critical(0,tr("Save text filter"), "Cannot save text filter! A text filter with the same regexp: " + - QString::fromStdString(it.filter())+ - " and settings already exists!"); - return; - } - - TextFilterHandler::Instance()->update(itemIndex_,it); - } - - QDialog::accept(); -} - -//====================================== -// -// TextFilterHandlerDialog -// -//====================================== - -TextFilterHandlerDialog::TextFilterHandlerDialog(QWidget *parent) : QDialog(parent), applyIndex_(-1) -{ - setupUi(this); - - QAction *sep1=new QAction(this); - sep1->setSeparator(true); - - QAction *sep2=new QAction(this); - sep2->setSeparator(true); - - QAction *sep3=new QAction(this); - sep3->setSeparator(true); - - table_->addAction(actionAdd_); - table_->addAction(sep1); - table_->addAction(actionDuplicate_); - table_->addAction(actionEdit_); - table_->addAction(sep2); - table_->addAction(actionApply_); - table_->addAction(sep3); - table_->addAction(actionRemove_); - - //Add actions for the toolbuttons - addTb_->setDefaultAction(actionAdd_); - editTb_->setDefaultAction(actionEdit_); - removeTb_->setDefaultAction(actionRemove_); - duplicateTb_->setDefaultAction(actionDuplicate_); - applyTb_->setDefaultAction(actionApply_); - - //Init the table - reloadTable(); - - //init - readSettings(); -} - -TextFilterHandlerDialog::~TextFilterHandlerDialog() -{ - writeSettings(); -} - -void TextFilterHandlerDialog::reloadTable() -{ - //The itemlist is fairly small. For simplicity we do not use a model/view solution here just - //a tablewidget. We reload the whole table widget whenerver there is a change in the list. - - table_->clear(); //it removes the headers as well - - QStringList headers; - headers << "Match mode" << "Case sensitive" << "Context menu" << "Regexp (grep)"; - table_->setHorizontalHeaderLabels(headers); - - const std::vector& items=TextFilterHandler::Instance()->items(); - table_->setRowCount(items.size()); - for(size_t i=0; i < items.size(); i++) - { - QString filterTxt=QString::fromStdString(items[i].filter()); - //Replace whitespace with Open Box U+2423 just for better interpretation - filterTxt.replace(QChar(' '),QChar(9251)); - - QTableWidgetItem *filterItem = new QTableWidgetItem(filterTxt); - QTableWidgetItem *matchedItem = new QTableWidgetItem((items[i].matched())?"match":"no match"); - QTableWidgetItem *caseItem = new QTableWidgetItem((items[i].caseSensitive())?"yes":"no"); - QTableWidgetItem *contextItem = new QTableWidgetItem((items[i].contextMenu())?"yes":"no"); - - table_->setItem(i, 0, matchedItem); - table_->setItem(i, 1, caseItem); - table_->setItem(i, 2, contextItem); - table_->setItem(i, 3, filterItem); - } - - if(table_->rowCount() > 0) - table_->setCurrentCell(0,0); - - updateStatus(); -} - -void TextFilterHandlerDialog::editItem() -{ - int r=table_->currentRow(); - if(r >=0 ) - { - TextFilterEditDialog diag(this); - diag.init(r,TextFilterHandler::Instance()->items()[r]); - if(diag.exec() == QDialog::Accepted) - reloadTable(); - } -} - - -void TextFilterHandlerDialog::on_actionEdit__triggered() -{ - editItem(); -} - -void TextFilterHandlerDialog::on_actionDuplicate__triggered() -{ - int r=table_->currentRow(); - if(r >=0 ) - { - TextFilterAddDialog diag(this); - diag.init(TextFilterHandler::Instance()->items()[r]); - if(diag.exec() == QDialog::Accepted) - reloadTable(); - } -} - -void TextFilterHandlerDialog::on_actionRemove__triggered() -{ - int r=table_->currentRow(); - if(r >= 0) - { - TextFilterHandler::Instance()->remove(r); - reloadTable(); - } -} - -void TextFilterHandlerDialog::on_actionAdd__triggered() -{ - TextFilterAddDialog diag(this); - if(diag.exec() == QDialog::Accepted) - reloadTable(); -} - -void TextFilterHandlerDialog::on_table__doubleClicked(const QModelIndex& index) -{ - editItem(); -} - -void TextFilterHandlerDialog::on_actionApply__triggered() -{ - int r=table_->currentRow(); - { - applyIndex_=table_->currentRow(); - close(); - } -} - -void TextFilterHandlerDialog::updateStatus() -{ - bool hasSelected=table_->currentRow() >=0; - - actionEdit_->setEnabled(hasSelected); - actionDuplicate_->setEnabled(hasSelected); - actionRemove_->setEnabled(hasSelected); - actionApply_->setEnabled(hasSelected); -} - -QString TextFilterHandlerDialog::settingsFile() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - return QString::fromStdString(cs->qtSettingsFile("TextFilterHandlerDialog")); -} - -void TextFilterHandlerDialog::writeSettings() -{ - Q_ASSERT(settingsFile().isEmpty() == false); - QSettings settings(settingsFile(),QSettings::NativeFormat); - - //We have to clear it not to remember all the previous windows - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void TextFilterHandlerDialog::readSettings() -{ - Q_ASSERT(settingsFile().isEmpty() == false); - QSettings settings(settingsFile(),QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(520,330)); - } - - settings.endGroup(); -} - diff -Nru ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.hpp ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.hpp --- ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,82 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TEXTFILTERHANDLERDIALOG_HPP -#define TEXTFILTERHANDLERDIALOG_HPP - -#include - -#include "ui_TextFilterAddDialog.h" -#include "ui_TextFilterHandlerDialog.h" - -class TextFilterItem; - -class TextFilterAddDialog : public QDialog, private Ui::TextFilterAddDialog -{ -Q_OBJECT - -public: - explicit TextFilterAddDialog(QWidget* parent=0); - void init(const TextFilterItem& item); - -public Q_SLOTS: - void accept(); - -protected: - TextFilterItem item(); -}; - -class TextFilterEditDialog : public TextFilterAddDialog -{ -Q_OBJECT - -public: - explicit TextFilterEditDialog(QWidget* parent=0); - void init(int itemIndex,const TextFilterItem& item); - -public Q_SLOTS: - void accept(); - -protected: - int itemIndex_; -}; - -class TextFilterHandlerDialog : public QDialog, private Ui::TextFilterHandlerDialog -{ - Q_OBJECT - -public: - explicit TextFilterHandlerDialog(QWidget *parent = 0); - ~TextFilterHandlerDialog(); - - void setItemToSaveAs(QString name,QString filter,bool matched,bool caseSensitive); - int applyIndex() const {return applyIndex_;} - -protected Q_SLOTS: - void on_actionAdd__triggered(); - void on_actionEdit__triggered(); - void on_actionDuplicate__triggered(); - void on_actionRemove__triggered(); - void on_actionApply__triggered(); - void on_table__doubleClicked(const QModelIndex& index); - -private: - void reloadTable(); - bool addItem(); - void editItem(); - void updateStatus(); - QString settingsFile(); - void writeSettings(); - void readSettings(); - - int applyIndex_; -}; - -#endif // TEXTFILTERHANDLERDIALOG_HPP - diff -Nru ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.ui ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.ui --- ecflow-4.9.0/Viewer/src/TextFilterHandlerDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterHandlerDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,287 +0,0 @@ - - - TextFilterHandlerDialog - - - - 0 - 0 - 697 - 525 - - - - Text Filter Manager - - - - - - - - - - Qt::ActionsContextMenu - - - QAbstractItemView::NoEditTriggers - - - true - - - QAbstractItemView::SingleSelection - - - QAbstractItemView::SelectRows - - - 4 - - - true - - - false - - - false - - - - Match mode - - - - - Case sensitive - - - - - Context menu - - - - - Regexp - - - - - - - - - - - - - - - - 0 - 0 - - - - Add - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - &Duplicate - - - - - - - - 0 - 0 - - - - &Edit - - - Qt::ToolButtonTextBesideIcon - - - - - - - - 0 - 0 - - - - Apply - - - - - - - Qt::Horizontal - - - - - - - - 0 - 0 - - - - &Remove - - - Qt::ToolButtonTextBesideIcon - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - - &Duplicate - - - Duplicate item - - - Ctrl+D - - - - - - :/viewer/configure.svg:/viewer/configure.svg - - - &Edit - - - Edit item - - - Ctrl+E - - - - - - :/viewer/close.svg:/viewer/close.svg - - - &Remove - - - Remove item - - - Del - - - - - - :/viewer/images/add.svg:/viewer/images/add.svg - - - &Add - - - Add item - - - Ctrl+N - - - - - A&pply - - - - - - - - - buttonBox - accepted() - TextFilterHandlerDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - TextFilterHandlerDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TextFilterHandler.hpp ecflow-4.11.1/Viewer/src/TextFilterHandler.hpp --- ecflow-4.9.0/Viewer/src/TextFilterHandler.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterHandler.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TEXTFILTERHANDLER_HPP -#define TEXTFILTERHANDLER_HPP - -#include -#include -#include - -class VSettings; - -class TextFilterItem -{ -public: - TextFilterItem(const std::string& filter,bool matched=true,bool caseSensitive=false,bool contextMenu=true) : - filter_(filter), matched_(matched), caseSensitive_(caseSensitive), contextMenu_(contextMenu) {} - - const std::string& filter() const {return filter_;} - bool caseSensitive() const {return caseSensitive_;} - bool matched() const {return matched_;} - bool contextMenu() const {return contextMenu_;} - void setContextMenu(bool cm) {contextMenu_=cm;} - void save(VSettings *vs) const; - - - static TextFilterItem make(VSettings* vs); - bool operator ==(const TextFilterItem& o) const; - -public: - std::string filter_; - bool matched_; - bool caseSensitive_; - bool contextMenu_; -}; - -class TextFilterHandler -{ -public: - static TextFilterHandler* Instance(); - - bool contains(const std::string& filter,bool matched,bool caseSensitive) const; - bool containsExceptOne(int index,const std::string& filter,bool matched,bool caseSensitive) const; - bool add(const TextFilterItem&); - bool add(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu); - void addLatest(const TextFilterItem&); - void addLatest(const std::string& filter,bool matched,bool caseSensitive,bool contextMenu); - const std::vector& items() const {return items_;} - const std::vector& latestItems() const {return latest_;} - void update(int,const TextFilterItem&); - void remove(int); - void allFilters(std::set&); - int indexOf(const std::string& filter,bool matched,bool caseSensitive) const; - -protected: - TextFilterHandler(); - - std::string settingsFile(); - void readSettings() ; - void writeSettings(); - - static TextFilterHandler* instance_; - const int maxLatestNum_; - std::vector items_; - std::vector latest_; -}; - - -#endif // TEXTFILTERHANDLER_HPP diff -Nru ecflow-4.9.0/Viewer/src/TextFilterWidget.cpp ecflow-4.11.1/Viewer/src/TextFilterWidget.cpp --- ecflow-4.9.0/Viewer/src/TextFilterWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,488 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TextFilterWidget.hpp" -#include "TextFilterHandlerDialog.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "TextFormat.hpp" -#include "ViewerUtil.hpp" - -TextFilterWidget::TextFilterWidget(QWidget *parent) : - QWidget(parent), - status_(EditStatus), - statusTb_(0), - optionTb_(0) -{ - setupUi(this); - - setProperty("textFilter","1"); - - //match - matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_match.svg")),tr("match"),0); - matchCb_->addItem(QIcon(QPixmap(":/viewer/filter_no_match.svg")),tr("no match"),1); - matchCb_->setItemData(0,tr("Show only the lines matching the filter experssion"),Qt::ToolTipRole); - matchCb_->setItemData(1,tr("Show only the lines not matching the filter experssion"),Qt::ToolTipRole); - - //Editor -#if QT_VERSION >= QT_VERSION_CHECK(4, 7, 0) - le_->setPlaceholderText(tr(" Regexp (grep)")); -#endif - -#if QT_VERSION >= QT_VERSION_CHECK(5, 2, 0) - le_->setClearButtonEnabled(true); -#endif - - oriBrush_=QBrush(le_->palette().brush(QPalette::Base)); - redBrush_=ViewerUtil::lineEditRedBg(); - greenBrush_=ViewerUtil::lineEditGreenBg(); - - completer_=new QCompleter(this); - le_->setCompleter(completer_); - - QIcon icon; - icon.addPixmap(QPixmap(":/viewer/close_grey.svg"),QIcon::Normal); - icon.addPixmap(QPixmap(":/viewer/close_red.svg"),QIcon::Active); - closeTb_->setIcon(icon); - - refreshCompleter(); - - //Initially it is hidden - hide(); -} - -bool TextFilterWidget::isActive() const -{ - return !le_->text().isEmpty(); -} - -bool TextFilterWidget::isCaseSensitive() const -{ - return caseCb_->isChecked(); -} - -bool TextFilterWidget::isMatched() const -{ - return matchCb_->currentIndex() == 0; -} - -QString TextFilterWidget::filterText() const -{ - return le_->text(); -} - -void TextFilterWidget::init(const TextFilterItem& item) -{ - matchCb_->setCurrentIndex(item.matched()?0:1); - caseCb_->setChecked(item.caseSensitive()); - le_->setText(QString::fromStdString(item.filter())); -} - -void TextFilterWidget::setExternalButtons(QToolButton* statusTb,QToolButton* optionTb) -{ - Q_ASSERT(statusTb); - Q_ASSERT(optionTb); - - statusTb_=statusTb; - optionTb_=optionTb; - - statusTb_->setChecked(false); - - connect(statusTb_,SIGNAL(toggled(bool)), - this,SLOT(slotStatusTb(bool))); - - connect(optionTb_,SIGNAL(clicked()), - this,SLOT(slotOptionTb())); -} - -void TextFilterWidget::setEnabledExternalButtons(bool b) -{ - if(statusTb_) statusTb_->setEnabled(b); - if(optionTb_) optionTb_->setEnabled(b); -} - -void TextFilterWidget::refreshCompleter() -{ - QStringList lst; - std::set vals; - TextFilterHandler::Instance()->allFilters(vals); - - for(std::set::const_iterator it=vals.begin(); it != vals.end(); ++it) - { - lst << QString::fromStdString(*it); - } - le_->setCompleter(new QCompleter(lst,le_)); -} - -void TextFilterWidget::slotFilterEditor() -{ - -} - -void TextFilterWidget::buildMenu(QToolButton *tb) -{ - QMenu* menu=new QMenu(tb); - - QAction *manageAc=new QAction(menu); - manageAc->setText(tr("Manage filters ...")); - manageAc->setIcon(QPixmap(":/viewer/configure.svg")); - menu->addAction(manageAc); - - QAction *saveAc=0; - if(isVisible()) - { - saveAc=new QAction(menu); - saveAc->setText(tr("Save filter")); - saveAc->setIcon(QPixmap(":/viewer/filesaveas.svg")); - if(!isActive() || isCurrentSaved()) saveAc->setEnabled(false); - menu->addAction(saveAc); - } - - QAction *sep=new QAction(menu); - sep->setSeparator(true); - menu->addAction(sep); - - QAction *clearAc=new QAction(menu); - clearAc->setText(tr("Clear filter")); - if(!isActive()) clearAc->setEnabled(false); - menu->addAction(clearAc); - - QAction *sep1=new QAction(menu); - sep1->setSeparator(true); - menu->addAction(sep1); - - addMenuSection(menu,TextFilterHandler::Instance()->items(),tr("Saved"),"s"); - addMenuSection(menu,TextFilterHandler::Instance()->latestItems(),tr("Recent"),"r"); - - if(QAction *ac=menu->exec(QCursor::pos())) - { - //Start manage filters dialogue - if(ac == manageAc) - { - TextFilterHandlerDialog diag; - diag.exec(); - int pos=diag.applyIndex(); - if(pos >=0 && TextFilterHandler::Instance()->latestItems().size()) - { - TextFilterItem item=TextFilterHandler::Instance()->items()[pos]; - if(!item.filter().empty()) - { - init(item); - Q_EMIT runRequested(QString::fromStdString(item.filter()),item.matched(),item.caseSensitive()); - } - } - else - { - refreshCompleter(); - } - } - //Save current filter - else if(ac == saveAc) - { - std::string filter=filterText().toStdString(); - bool matchMode=isMatched(); - bool caseSensitive=isCaseSensitive(); - - int pos=TextFilterHandler::Instance()->indexOf(filter,matchMode,caseSensitive); - if(pos != -1) - { - TextFilterItem it=TextFilterHandler::Instance()->items()[pos]; - - //Enable context menu for already saved items - if(!it.contextMenu()) - { - it.setContextMenu(true); - TextFilterHandler::Instance()->update(pos,it); - } - return; - } - - TextFilterHandler::Instance()->add(filter,matchMode,caseSensitive,true); - refreshCompleter(); - } - //Clear current filter - else if(ac == clearAc) - { - le_->clear(); - setStatus(EditStatus); - refreshCompleter(); - } - //Load a filter - else - { - QStringList id=ac->data().toString().split("_"); - if(id.count() == 2) - { - std::size_t pos=id[1].toInt(); - TextFilterItem item("",""); - if(id[0] == "s") - { - if(pos >=0 && TextFilterHandler::Instance()->items().size()) - { - item=TextFilterHandler::Instance()->items()[pos]; - } - } - - else if(id[0] == "r") - { - if(pos >=0 && TextFilterHandler::Instance()->latestItems().size()) - { - item=TextFilterHandler::Instance()->latestItems()[pos]; - } - } - - if(!item.filter().empty()) - { - init(item); - Q_EMIT runRequested(QString::fromStdString(item.filter()),item.matched(),item.caseSensitive()); - } - } - } - - } - - menu->clear(); - menu->deleteLater(); -} - -void TextFilterWidget::addMenuSection(QMenu* menu,const std::vector& items,QString title,QString data) -{ - if(items.empty()) - return; - - QAction *sep1=new QAction(menu); - sep1->setSeparator(true); - menu->addAction(sep1); - - if(!title.isEmpty()) - { - QAction* acTitle = new QAction(menu); - acTitle->setText(title); - QFont f=acTitle->font(); - f.setBold(true); - acTitle->setFont(f); - menu->addAction(acTitle); - } - - for(std::size_t i=0 ; i < items.size(); i++) - { - if(data != "s" || items[i].contextMenu()) - { - QAction* ac=new QAction(this); - - QString txt=QString::fromStdString(items[i].filter()); - //Replace whitespace with Open Box U+2423 just for better interpretation - txt.replace(QChar(' '),QChar(9251)); - txt+=" (" + QString(items[i].caseSensitive()?"cs":"ci") + ")"; - - ac->setText(txt); - ac->setData(data + "_" + QString::number(i)); //set an id for the action - - if(items[i].matched()) - ac->setIcon(QPixmap(":/viewer/filter_match.svg")); - else - ac->setIcon(QPixmap(":/viewer/filter_no_match.svg")); - - menu->addAction(ac); - } - } -} - -void TextFilterWidget::runIt() -{ - QString t=le_->text(); - if(!t.isEmpty()) - Q_EMIT runRequested(t,isMatched(),isCaseSensitive()); -} - -void TextFilterWidget::slotStatusTb(bool b) -{ - if(b) - { - show(); - setEditFocus(); - } - else - { - hide(); - adjustToolTip(); - } -} - -void TextFilterWidget::closeIt() -{ - //clear - le_->clear(); - setStatus(EditStatus); - refreshCompleter(); - - //hide - statusTb_->setChecked(false); - Q_EMIT closeRequested(); //this will clear the filter -} - - -void TextFilterWidget::on_closeTb__clicked() -{ - closeIt(); -} - -void TextFilterWidget::on_le__returnPressed() -{ - runIt(); -} - -void TextFilterWidget::on_le__textChanged() -{ - if(status_ != EditStatus) - setStatus(EditStatus); - - if(!isActive()) - Q_EMIT clearRequested(); -} - -void TextFilterWidget::on_matchCb__currentIndexChanged(int) -{ - runIt(); -} - -void TextFilterWidget::on_caseCb__stateChanged(int) -{ - runIt(); -} - -void TextFilterWidget::slotOptionTb() -{ - Q_ASSERT(optionTb_); - buildMenu(optionTb_); -} - -void TextFilterWidget::setEditFocus() -{ - le_->setFocus(); -} - -void TextFilterWidget::adjustToolTip() -{ - if(!statusTb_) - return; - - QString filterDesc; - if(status_ == FoundStatus || status_ == NotFoundStatus) - { - if(!isVisible()) - filterDesc+=tr("Toggle to show text filter bar
      ----------------------------------------
      "); - else - filterDesc+=tr("Toggle to hide text filter bar. The filter remains active (if defined).
      ----------------------------------------
      "); - - filterDesc+=tr("Current filter:") + - "
       regexp: " + filterText() + - "
       mode: " + (isMatched()?"match":"no match") + ", " + - + (isCaseSensitive()?"case sensitive":"case insensitive") + - "

      "; - } - - switch(status_) - { - case EditStatus: - statusTb_->setToolTip(tr(isVisible()?"Toggle to hide text filter bar. The filter remains active (if defined).":"Toggle to show text filter bar")); - break; - case FoundStatus: - statusTb_->setToolTip(filterDesc + tr("There ") + - Viewer::formatText("are lines",QColor(100,220,120)) + - tr(" matching the filter in the output file")); - break; - case NotFoundStatus: - statusTb_->setToolTip(filterDesc + tr("There ") + - Viewer::formatText("are no lines",QColor(255,95,95)) + - tr(" matching the filter in the output file")); - break; - default: - break; - } -} - -void TextFilterWidget::setStatus(FilterStatus status,bool force) -{ - if(force || status_ != status) - { - status_=status; - - QBrush br=oriBrush_; - QPalette p=le_->palette(); - switch(status_) - { - case EditStatus: - br=oriBrush_; - if(statusTb_) - { - statusTb_->setIcon(QPixmap(":/viewer/filter_decor.svg")); - } - break; - case FoundStatus: - br=greenBrush_; - if(statusTb_) - { - statusTb_->setIcon(QPixmap(":/viewer/filter_decor_green.svg")); - } - addCurrentToLatest(); - break; - case NotFoundStatus: - br=redBrush_; - if(statusTb_) - { - statusTb_->setIcon(QPixmap(":/viewer/filter_decor_red.svg")); - } - break; - default: - break; - } - - p.setBrush(QPalette::Base,br); - le_->setPalette(p); - - adjustToolTip(); - } -} - -void TextFilterWidget::addCurrentToLatest() -{ - TextFilterHandler::Instance()->addLatest(filterText().toStdString(), - isMatched(),isCaseSensitive(),true); -} - -bool TextFilterWidget::isCurrentSaved() const -{ - return TextFilterHandler::Instance()->contains(filterText().toStdString(), - isMatched(),isCaseSensitive()); -} - -void TextFilterWidget::paintEvent(QPaintEvent *) -{ - QStyleOption opt; - opt.init(this); - QPainter p(this); - style()->drawPrimitive(QStyle::PE_Widget, &opt, &p, this); -} - -void TextFilterWidget::showEvent(QShowEvent *) -{ - adjustToolTip(); -} - diff -Nru ecflow-4.9.0/Viewer/src/TextFilterWidget.hpp ecflow-4.11.1/Viewer/src/TextFilterWidget.hpp --- ecflow-4.9.0/Viewer/src/TextFilterWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -//============================================================================ -// Copyright 2009-2018 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TEXTFILTERWIDGET_HPP -#define TEXTFILTERWIDGET_HPP - -#include "ui_TextFilterWidget.h" - -#include -#include -#include - -#include "TextFilterHandler.hpp" - -class TextFilterCompleterModel; - -class TextFilterWidget : public QWidget, private Ui::TextFilterWidget -{ -Q_OBJECT - -public: - explicit TextFilterWidget(QWidget *parent=0); - ~TextFilterWidget() {} - - enum FilterStatus {EditStatus,FoundStatus,NotFoundStatus}; - void setStatus(FilterStatus,bool force=false); - - void setEditFocus(); - void buildMenu(QToolButton *tb); - void setExternalButtons(QToolButton* statusTb,QToolButton* optionTb); - void setEnabledExternalButtons(bool); - QString filterText() const; - bool isActive() const; - bool isCaseSensitive() const; - bool isMatched() const; - void closeIt(); - -public Q_SLOTS: - void slotFilterEditor(); - void on_le__textChanged(); - void on_le__returnPressed(); - void on_closeTb__clicked(); - void on_matchCb__currentIndexChanged(int); - void on_caseCb__stateChanged(int); - void slotOptionTb(); - void slotStatusTb(bool); - -Q_SIGNALS: - void runRequested(QString,bool,bool); - void clearRequested(); - void closeRequested(); - void hideRequested(); - void statusChanged(FilterStatus); - -protected: - void paintEvent(QPaintEvent *); - void showEvent(QShowEvent *); - -private: - void init(const TextFilterItem& item); - void runIt(); - void refreshCompleter(); - void addCurrentToLatest(); - bool isCurrentSaved() const; - void adjustToolTip(); - void addMenuSection(QMenu* menu,const std::vector& items, - QString title,QString data); - - FilterStatus status_; - QBrush oriBrush_; - QBrush redBrush_; - QBrush greenBrush_; - QCompleter* completer_; - TextFilterCompleterModel* completerModel_; - QToolButton* statusTb_; - QToolButton* optionTb_; -}; - -#endif // TEXTFILTERWIDGET_HPP diff -Nru ecflow-4.9.0/Viewer/src/TextFilterWidget.ui ecflow-4.11.1/Viewer/src/TextFilterWidget.ui --- ecflow-4.9.0/Viewer/src/TextFilterWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFilterWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ - - - TextFilterWidget - - - - 0 - 0 - 400 - 300 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Qt::Horizontal - - - - 2 - 20 - - - - - - - - Filter: - - - - - - - - - - - - - Qt::Horizontal - - - - 6 - 20 - - - - - - - - Case sensitive - - - - - - - Close text filter bar and <b>clear</b> filter - - - ... - - - true - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TextFormat.cpp ecflow-4.11.1/Viewer/src/TextFormat.cpp --- ecflow-4.9.0/Viewer/src/TextFormat.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFormat.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TextFormat.hpp" - -#include -#include - -static QColor shortCutCol(147,165,202); - -namespace Viewer -{ - -QString formatShortCut(QString text) -{ - return "" + text + ""; -} - - -QString formatShortCut(QAction* ac) -{ - Q_ASSERT(ac); - - return " (" + - ac->shortcut().toString() + ")"; -} - -void addShortCutToToolTip(QList alst) -{ - Q_FOREACH(QAction *ac,alst) - { - if(!ac->shortcut().isEmpty()) - { - QString tt=ac->toolTip(); - if(!tt.isEmpty()) - { - tt+=" " + formatShortCut(ac); - ac->setToolTip(tt); - } - } - } -} - -QString formatBoldText(QString txt,QColor col) -{ - return "" +txt + ""; -} - -QString formatText(QString txt,QColor col) -{ - return "" +txt + ""; -} -} //namespace Viewer diff -Nru ecflow-4.9.0/Viewer/src/TextFormat.hpp ecflow-4.11.1/Viewer/src/TextFormat.hpp --- ecflow-4.9.0/Viewer/src/TextFormat.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextFormat.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TEXTFORMAT_HPP -#define TEXTFORMAT_HPP - -#include -#include -#include - -class QAction; - -namespace Viewer -{ - QString formatShortCut(QString); - QString formatShortCut(QAction*); - void addShortCutToToolTip(QList); - QString formatBoldText(QString,QColor); - QString formatText(QString,QColor); -} //namespace Viewer - -#endif // TEXTFORMAT_HPP diff -Nru ecflow-4.9.0/Viewer/src/TextPager/syntaxhighlighter.cpp ecflow-4.11.1/Viewer/src/TextPager/syntaxhighlighter.cpp --- ecflow-4.9.0/Viewer/src/TextPager/syntaxhighlighter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/syntaxhighlighter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "syntaxhighlighter.hpp" - -#include "TextPagerEdit.hpp" -#include "TextPagerDocument.hpp" -#include "TextPagerLayout_p.hpp" -#include "TextPagerDocument_p.hpp" - -SyntaxHighlighter::SyntaxHighlighter(QObject *parent) - : QObject(parent), d(new Private) -{ -} - -SyntaxHighlighter::SyntaxHighlighter(TextPagerEdit *parent) - : QObject(parent), d(new Private) -{ - if (parent) { - parent->addSyntaxHighlighter(this); - } -} - -SyntaxHighlighter::~SyntaxHighlighter() -{ - delete d; -} - -void SyntaxHighlighter::setTextEdit(TextPagerEdit *doc) -{ - Q_ASSERT(doc); - doc->addSyntaxHighlighter(this); -} -TextPagerEdit *SyntaxHighlighter::textEdit() const -{ - return d->textEdit; -} - - -TextPagerDocument * SyntaxHighlighter::document() const -{ - return d->textEdit ? d->textEdit->document() : 0; -} - -void SyntaxHighlighter::rehighlight() -{ - if (d->textEdit) { - Q_ASSERT(d->textLayout); - d->textLayout->layoutDirty = true; - d->textEdit->viewport()->update(); - } -} - -void SyntaxHighlighter::setFormat(int start, int count, const QTextCharFormat &format) -{ - ASSUME(d->textEdit); - Q_ASSERT(start >= 0); - Q_ASSERT(start + count <= d->currentBlock.size()); - d->formatRanges.append(QTextLayout::FormatRange()); - QTextLayout::FormatRange &range = d->formatRanges.last(); - range.start = start; - range.length = count; - range.format = format; -} - -void SyntaxHighlighter::setFormat(int start, int count, const QColor &color) -{ - QTextCharFormat format; - format.setForeground(color); - setFormat(start, count, format); -} - -void SyntaxHighlighter::setFormat(int start, int count, const QFont &font) -{ - QTextCharFormat format; - format.setFont(font); - setFormat(start, count, format); -} - -QTextCharFormat SyntaxHighlighter::format(int pos) const -{ - QTextCharFormat ret; - Q_FOREACH(const QTextLayout::FormatRange &range, d->formatRanges) { - if (range.start <= pos && range.start + range.length > pos) { - ret.merge(range.format); - } else if (range.start > pos) { - break; - } - } - return ret; -} - - -int SyntaxHighlighter::previousBlockState() const -{ - return d->previousBlockState; -} - -int SyntaxHighlighter::currentBlockState() const -{ - return d->currentBlockState; -} - -void SyntaxHighlighter::setCurrentBlockState(int s) -{ - d->previousBlockState = d->currentBlockState; - d->currentBlockState = s; // ### These don't entirely follow QSyntaxHighlighter's behavior -} - -int SyntaxHighlighter::currentBlockPosition() const -{ - return d->currentBlockPosition; -} - -QTextBlockFormat SyntaxHighlighter::blockFormat() const -{ - return d->blockFormat; -} - -void SyntaxHighlighter::setBlockFormat(const QTextBlockFormat &format) -{ - d->blockFormat = format; -} diff -Nru ecflow-4.9.0/Viewer/src/TextPager/syntaxhighlighter.hpp ecflow-4.11.1/Viewer/src/TextPager/syntaxhighlighter.hpp --- ecflow-4.9.0/Viewer/src/TextPager/syntaxhighlighter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/syntaxhighlighter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 - -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef SYNTAXHIGHLIGHTER_HPP__ -#define SYNTAXHIGHLIGHTER_HPP__ - -#include -#include -#include -#include -#include -#include -#include - -class TextPagerEdit; -class TextPagerLayout; -class TextPagerDocument; -class SyntaxHighlighter : public QObject -{ - Q_OBJECT -public: - SyntaxHighlighter(QObject *parent = 0); - SyntaxHighlighter(TextPagerEdit *parent); - ~SyntaxHighlighter(); - void setTextEdit(TextPagerEdit *doc); - TextPagerEdit *textEdit() const; - TextPagerDocument *document() const; - virtual void highlightBlock(const QString &text) = 0; - QString currentBlock() const { return d->currentBlock; } - void setFormat(int start, int count, const QTextCharFormat &format); - void setFormat(int start, int count, const QColor &color); - inline void setColor(int start, int count, const QColor &color) - { setFormat(start, count, color); } - inline void setBackground(int start, int count, const QBrush &brush) - { QTextCharFormat format; format.setBackground(brush); setFormat(start, count, format); } - inline void setBackgroundColor(int start, int count, const QColor &color) - { setBackground(start, count, color); } - void setFormat(int start, int count, const QFont &font); - inline void setFont(int start, int count, const QFont &font) - { setFormat(start, count, font); } - QTextBlockFormat blockFormat() const; - void setBlockFormat(const QTextBlockFormat &format); - QTextCharFormat format(int pos) const; - int previousBlockState() const; - int currentBlockState() const; - void setCurrentBlockState(int s); - int currentBlockPosition() const; -public Q_SLOTS: - void rehighlight(); -private: - struct Private { - Private() : textEdit(0), textLayout(0), previousBlockState(0), currentBlockState(0), - currentBlockPosition(-1) {} - TextPagerEdit *textEdit; - TextPagerLayout *textLayout; - int previousBlockState, currentBlockState, currentBlockPosition; - QList formatRanges; - QTextBlockFormat blockFormat; - QString currentBlock; - } *d; - - friend class TextPagerEdit; - friend class TextPagerLayout; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,781 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "TextPagerCursor.hpp" -#include "TextPagerCursor_p.hpp" -#include "TextPagerDocument.hpp" -#include "TextPagerDocument_p.hpp" -#include "TextPagerEdit_p.hpp" -#include "TextPagerLayout_p.hpp" -#include "TextPagerEdit.hpp" - -class SelectionChangedEmitter -{ -public: - SelectionChangedEmitter(TextPagerEdit *t) - : selectionStart(-1), selectionEnd(-1), textEdit(t) - { - if (textEdit) { - selectionStart = textEdit->textCursor().selectionStart(); - selectionEnd = textEdit->textCursor().selectionEnd(); - } - } - - ~SelectionChangedEmitter() - { - if (textEdit) { - const TextPagerCursor &cursor = textEdit->textCursor(); - if (((selectionStart != selectionEnd) != cursor.hasSelection()) - || (selectionStart != selectionEnd - && (selectionStart != cursor.selectionStart() - || selectionEnd != cursor.selectionEnd()))) { - QMetaObject::invokeMethod(textEdit, "selectionChanged"); - } - } - } -private: - int selectionStart, selectionEnd; - TextPagerEdit *textEdit; -}; - -TextPagerCursor::TextPagerCursor() - : d(0), textEdit(0) -{ -} - -TextPagerCursor::TextPagerCursor(const TextPagerDocument *document, int pos, int anc) - : d(0), textEdit(0) -{ - if (document) { - const int documentSize = document->d->documentSize; - if (pos < 0 || pos > documentSize || anc < -1 || anc > documentSize) { -#ifndef LAZYTEXTEDIT_AUTOTEST - qWarning("Invalid cursor data %d %d - %d\n", - pos, anc, documentSize); - Q_ASSERT(0); -#endif - return; - } - d = new TextCursorSharedPrivate; - d->document = const_cast(document); - d->position = pos; - d->anchor = anc == -1 ? pos : anc; - d->document->d->textCursors.insert(d); - } -} - -TextPagerCursor::TextPagerCursor(const TextPagerEdit *edit, int pos, int anc) - : d(0), textEdit(0) -{ - if (edit) { - TextPagerDocument *document = edit->document(); - const int documentSize = document->d->documentSize; - if (pos < 0 || pos > documentSize || anc < -1 || anc > documentSize) { -#ifndef LAZYTEXTEDIT_AUTOTEST - qWarning("Invalid cursor data %d %d - %d\n", - pos, anc, documentSize); - Q_ASSERT(0); -#endif - return; - } - d = new TextCursorSharedPrivate; - d->document = const_cast(document); - d->position = pos; - d->anchor = anc == -1 ? pos : anc; - d->document->d->textCursors.insert(d); - } -} - -TextPagerCursor::TextPagerCursor(const TextPagerCursor &cursor) - : d(cursor.d), textEdit(0) -{ - ref(); -} - -TextPagerCursor &TextPagerCursor::operator=(const TextPagerCursor &other) -{ - deref(); - d = other.d; - textEdit = 0; - ref(); - return *this; -} - -TextPagerCursor::~TextPagerCursor() -{ - deref(); -} - -bool TextPagerCursor::isNull() const -{ - return !d || !d->document; -} - -TextPagerDocument *TextPagerCursor::document() const -{ - return d ? d->document : 0; -} - -void TextPagerCursor::setSelection(int pos, int length) // can be negative -{ - setPosition(pos + length); - if (length != 0) - setPosition(pos, KeepAnchor); -} - -void TextPagerCursor::setPosition(int pos, MoveMode mode) -{ - Q_ASSERT(!isNull()); - d->overrideColumn = -1; - if (pos < 0 || pos > d->document->documentSize()) { - clearSelection(); - return; - } else if (pos == d->position && (mode == KeepAnchor || d->anchor == d->position)) { - return; - } - - SelectionChangedEmitter emitter(textEdit); - detach(); - cursorChanged(false); - -#if 0 - Link *link = d->document->links(pos, 1).value(0, 0); - if (link && link->position < pos && link->position + link->size > pos) { // inside a link - pos = (pos > d->position ? link->position + link->size : link->position); - } -#endif - - if (mode ==TextPagerCursor::MoveAnchor) { - clearSelection(); - } - - d->position = pos; - if (mode == MoveAnchor) { - d->anchor = pos; - } - cursorChanged(true); -} - -int TextPagerCursor::position() const -{ - return isNull() ? -1 : d->position; -} - -int TextPagerCursor::anchor() const -{ - return isNull() ? -1 : d->anchor; -} - -bool TextPagerCursor::movePosition(TextPagerCursor::MoveOperation op,TextPagerCursor::MoveMode mode, int n) -{ - if (!d || !d->document || n <= 0) - return false; - - switch (op) { - case Start: - case StartOfLine: - case End: - case EndOfLine: - n = 1; - break; - default: - break; - } - - if (n > 1) { - while (n > 0 && movePosition(op, mode, 1)) - --n; - return n == 0; - } - detach(); - - switch (op) { - case NoMove: - return true; - - case PreviousCharacter: - case NextCharacter: - return movePosition(op ==TextPagerCursor::PreviousCharacter - ?TextPagerCursor::Left :TextPagerCursor::Right, mode); - - case End: - case Start: - setPosition(op ==TextPagerCursor::Start ? 0 : d->document->documentSize(), mode); - break; - - case Up: - case Down: { - TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 1); // should I use 1? - Q_ASSERT(textLayout); - int index; - bool currentIsLast; - const QTextLine currentLine = textLayout->lineForPosition(d->position, 0, - &index, - ¤tIsLast); - Q_ASSERT(textLayout->lines.size() <= 1 || (index != -1 && currentLine.isValid())); - if (!currentLine.isValid()) - return false; - const int col = columnNumber(); - int targetLinePos; - if (op == Up) { - targetLinePos = d->position - col - 1; -// qDebug() << "I was at column" << col << "and position" -// << d->position << "so naturally I can find the previous line around" -// << (d->position - col - 1); - } else { - targetLinePos = d->position + currentLine.textLength() - col - + (currentIsLast ? 1 : 0); -// qDebug() << "currentLine.textLength" << currentLine.textLength() << "col" << col -// << "currentIsLast" << currentIsLast; - // ### probably need to add only if last line in layout - } - if (targetLinePos < 0) { - if (d->position == 0) { - return false; - } else { - setPosition(0, mode); - return true; - } - } else if (targetLinePos >= d->document->documentSize()) { - if (d->position == d->document->documentSize()) { - return false; - } else { - setPosition(d->document->documentSize(), mode); - return true; - } - } - int offsetInLine; - - const QTextLine targetLine = textLayout->lineForPosition(targetLinePos, &offsetInLine); - if (!targetLine.isValid()) - return false; - targetLinePos -= offsetInLine; // targetLinePos should now be at col 0 - -// qDebug() << "finding targetLine at" << targetLinePos -// << d->document->read(targetLinePos, 7) -// << "d->position" << d->position -// << "col" << col -// << "offsetInLine" << offsetInLine; - - int gotoCol = qMax(d->overrideColumn, col); - if (gotoCol > targetLine.textLength()) { - d->overrideColumn = qMax(d->overrideColumn, gotoCol); - gotoCol = targetLine.textLength(); - } - const int overrideColumn = d->overrideColumn; - setPosition(targetLinePos + gotoCol, mode); - d->overrideColumn = overrideColumn; - break; } - - case EndOfLine: - case StartOfLine: { - int offset; - bool lastLine; - TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 1); - QTextLine line = textLayout->lineForPosition(position(), &offset, - 0, &lastLine); - if (!line.isValid()) - return false; - if (op ==TextPagerCursor::StartOfLine) { - setPosition(position() - offset, mode); - } else { - int pos = position() - offset + line.textLength(); - if (!lastLine) - --pos; - setPosition(pos, mode); - } - break; } - - case StartOfBlock: { - TextDocumentIterator it(d->document->d, d->position); - const QLatin1Char newline('\n'); - while (it.hasPrevious() && it.previous() != newline) ; - if (it.hasPrevious()) - it.next(); - setPosition(it.position(), mode); - break; } - case EndOfBlock: { - TextDocumentIterator it(d->document->d, d->position); - const QLatin1Char newline('\n'); - while (it.current() != newline && it.hasNext() && it.next() != newline) ; - setPosition(it.position(), mode); - break; } - - case StartOfWord: - case PreviousWord: - case WordLeft: { - TextDocumentIterator it(d->document->d, d->position); - - while (it.hasPrevious()) { - const QChar ch = it.previous(); - if (d->document->isWordCharacter(ch, it.position())) - break; - } - while (it.hasPrevious()) { - const QChar ch = it.previous(); - if (!d->document->isWordCharacter(ch, it.position())) - break; - } - if (it.hasPrevious()) - it.next(); - setPosition(it.position(), mode); - d->overrideColumn = -1; - break; } - - case NextWord: - case WordRight: - case EndOfWord: { - TextDocumentIterator it(d->document->d, d->position); - while (it.hasNext()) { - const QChar ch = it.next(); - if (d->document->isWordCharacter(ch, it.position())) - break; - } - while (it.hasNext()) { - const QChar ch = it.next(); - if (!d->document->isWordCharacter(ch, it.position())) - break; - } - setPosition(it.position(), mode); - d->overrideColumn = -1; - break; } - - case PreviousBlock: - movePosition(TextPagerCursor::StartOfBlock, mode); - movePosition(TextPagerCursor::Left, mode); - movePosition(TextPagerCursor::StartOfBlock, mode); - break; - - case NextBlock: - movePosition(TextPagerCursor::EndOfBlock, mode); - movePosition(TextPagerCursor::Right, mode); - return true; - - case Left: - case Right: - d->overrideColumn = -1; - setPosition(qBound(0, position() + (op ==TextPagerCursor::Left ? -1 : 1), - d->document->documentSize()), mode); - break; - }; - - return true; -} - -void TextPagerCursor::select(SelectionType selection) -{ - if (!d || !d->document) - return; - - clearSelection(); - - switch (selection) { - case LineUnderCursor: - movePosition(StartOfLine); - movePosition(EndOfLine, KeepAnchor); - break; - case WordUnderCursor: - movePosition(StartOfWord); - movePosition(EndOfWord, KeepAnchor); - break; - case BlockUnderCursor: - movePosition(StartOfBlock); - // also select the paragraph separator - if (movePosition(PreviousBlock)) { - movePosition(EndOfBlock); - movePosition(NextBlock, KeepAnchor); - } - movePosition(EndOfBlock, KeepAnchor); - break; - } -} - -bool TextPagerCursor::hasSelection() const -{ - return !isNull() && d->anchor != d->position; -} - -void TextPagerCursor::clearSelection() -{ - Q_ASSERT(!isNull()); - if (hasSelection()) { - detach(); - SelectionChangedEmitter emitter(textEdit); - d->anchor = d->position; - } -} - -int TextPagerCursor::selectionStart() const -{ - return qMin(d->anchor, d->position); -} - -int TextPagerCursor::selectionEnd() const -{ - return qMax(d->anchor, d->position); -} - -int TextPagerCursor::selectionSize() const -{ - return selectionEnd() - selectionStart(); -} - - -QString TextPagerCursor::selectedText() const -{ - if (isNull() || d->anchor == d->position) - return QString(); - - const int min = qMin(d->anchor, d->position); - const int max = qMax(d->anchor, d->position); - return d->document->read(min, max - min); -} - -bool TextPagerCursor::atBlockStart() const -{ - Q_ASSERT(!isNull()); - return atStart() || d->document->read(d->position - 1, 1).at(0) == '\n'; -} - -bool TextPagerCursor::atBlockEnd() const -{ - Q_ASSERT(!isNull()); - return atEnd() || d->document->read(d->position, 1).at(0) == '\n'; // ### is this right? -} - -bool TextPagerCursor::atStart() const -{ - Q_ASSERT(!isNull()); - return d->position == 0; -} - -bool TextPagerCursor::atEnd() const -{ - Q_ASSERT(!isNull()); - return d->position == d->document->documentSize(); -} - - -bool TextPagerCursor::operator!=(const TextPagerCursor &rhs) const -{ - return !(*this == rhs); -} - -bool TextPagerCursor::operator<(const TextPagerCursor &rhs) const -{ - if (!d) - return true; - - if (!rhs.d) - return false; - - Q_ASSERT_X(d->document == rhs.d->document, "TextCursor::operator<", - "cannot compare cursors attached to different documents"); - - return d->position < rhs.d->position; -} - -bool TextPagerCursor::operator<=(const TextPagerCursor &rhs) const -{ - return *this < rhs || *this == rhs; -} - -bool TextPagerCursor::operator==(const TextPagerCursor &rhs) const -{ - if (isCopyOf(rhs)) - return true; - - if (!d || !rhs.d) - return false; - - return (d->position == rhs.d->position - && d->anchor == rhs.d->anchor - && d->document == rhs.d->document); -} - -bool TextPagerCursor::operator>=(const TextPagerCursor &rhs) const -{ - return *this > rhs || *this == rhs; -} - -bool TextPagerCursor::operator>(const TextPagerCursor &rhs) const -{ - if (!d) - return false; - - if (!rhs.d) - return true; - - Q_ASSERT_X(d->document == rhs.d->document, "TextCursor::operator>=", - "cannot compare cursors attached to different documents"); - - return d->position > rhs.d->position; -} - -bool TextPagerCursor::isCopyOf(const TextPagerCursor &other) const -{ - return d == other.d; -} - -int TextPagerCursor::columnNumber() const -{ - Q_ASSERT(d && d->document); - TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 0); - int col; - textLayout->lineForPosition(d->position, &col); - return col; -} - -int TextPagerCursor::lineNumber() const -{ - Q_ASSERT(d && d->document); - return d->document->lineNumber(position()); -} - -void TextPagerCursor::detach() -{ - Q_ASSERT(d); - int refint = d->ref.fetchAndAddRelaxed(0); // required to get past a bug in Qt 5.2.1 (see Ubuntu 14.04) - if (refint > 1) { - d->ref.deref(); - TextCursorSharedPrivate *p = new TextCursorSharedPrivate; - p->position = d->position; - p->overrideColumn = d->overrideColumn; - p->anchor = d->anchor; - p->document = d->document; - d->document->d->textCursors.insert(p); - d = p; - } -} - -bool TextPagerCursor::ref() -{ - return d && d->ref.ref(); -} - -bool TextPagerCursor::deref() -{ - TextCursorSharedPrivate *dd = d; - d = 0; - if (dd && !dd->ref.deref()) { - if (dd->document) { - const bool removed = dd->document->d->textCursors.remove(dd); - Q_ASSERT(removed); - Q_UNUSED(removed) - } - delete dd; - return false; - } - return true; -} - -void TextPagerCursor::cursorChanged(bool ensureVisible) -{ - if (textEdit) { - if (textEdit->d->cursorBlinkTimer.isActive()) - textEdit->d->cursorVisible = true; - if (ensureVisible) { - Q_EMIT textEdit->cursorPositionChanged(d->position); - textEdit->ensureCursorVisible(); - } - const QRect r = textEdit->cursorBlockRect(*this) & textEdit->viewport()->rect(); - if (!r.isNull()) { - textEdit->viewport()->update(r); - } - } -} - -bool TextPagerCursor::cursorMoveKeyEvent(QKeyEvent *e) -{ - MoveMode mode = MoveAnchor; - MoveOperation op = NoMove; - - if (e == QKeySequence::MoveToNextChar) { - op = Right; - } else if (e == QKeySequence::MoveToPreviousChar) { - op = Left; - } else if (e == QKeySequence::SelectNextChar) { - op = Right; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectPreviousChar) { - op = Left; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectNextWord) { - op = WordRight; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectPreviousWord) { - op = WordLeft; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectStartOfLine) { - op = StartOfLine; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectEndOfLine) { - op = EndOfLine; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectStartOfBlock) { - op = StartOfBlock; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectEndOfBlock) { - op = EndOfBlock; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectStartOfDocument) { - op = Start; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectEndOfDocument) { - op = End; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectPreviousLine) { - op = Up; - mode = KeepAnchor; - } else if (e == QKeySequence::SelectNextLine) { - op = Down; - mode = KeepAnchor; -#if 0 - { - QTextBlock block = cursor.block(); - QTextLine line = currentTextLine(cursor); - if (!block.next().isValid() - && line.isValid() - && line.lineNumber() == block.layout()->lineCount() - 1) - op = End; - } -#endif - } else if (e == QKeySequence::MoveToNextWord) { - op = WordRight; - } else if (e == QKeySequence::MoveToPreviousWord) { - op = WordLeft; - } else if (e == QKeySequence::MoveToEndOfBlock) { - op = EndOfBlock; - } else if (e == QKeySequence::MoveToStartOfBlock) { - op = StartOfBlock; - } else if (e == QKeySequence::MoveToNextLine) { - op = Down; - } else if (e == QKeySequence::MoveToPreviousLine) { - op = Up; - } else if (e == QKeySequence::MoveToStartOfLine) { - op = StartOfLine; - } else if (e == QKeySequence::MoveToEndOfLine) { - op = EndOfLine; - } else if (e == QKeySequence::MoveToStartOfDocument) { - op = Start; - } else if (e == QKeySequence::MoveToEndOfDocument) { - op = End; - } else if (e == QKeySequence::MoveToNextPage || e == QKeySequence::MoveToPreviousPage - || e == QKeySequence::SelectNextPage || e == QKeySequence::SelectPreviousPage) { - const MoveMode mode = (e == QKeySequence::MoveToNextPage - || e == QKeySequence::MoveToPreviousPage - ? MoveAnchor : KeepAnchor); - const MoveOperation operation = (e == QKeySequence::MoveToNextPage - || e == QKeySequence::SelectNextPage - ? Down : Up); - int visibleLines = 10; - if (textEdit) - visibleLines = textEdit->d->visibleLines; - for (int i=0; iviewport()->width() : d->viewportWidth; -} - -void TextPagerCursor::setViewportWidth(int width) -{ - if (textEdit) { - qWarning("It makes no sense to set the viewportWidth of a text cursor that belongs to a text edit. The actual viewportWidth will be used"); - return; - } - d->viewportWidth = width; -} - -QChar TextPagerCursor::cursorCharacter() const -{ - Q_ASSERT(d && d->document); - return d->document->readCharacter(d->anchor); -} - -QString TextPagerCursor::cursorLine() const -{ - Q_ASSERT(d && d->document); - int layoutIndex = -1; - TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 2); // should I use 1? - - QTextLine line = textLayout->lineForPosition(position(), 0, &layoutIndex); - Q_ASSERT(line.isValid()); // ### could this be legitimate? - Q_ASSERT(textLayout); - return textLayout->textLayouts.at(layoutIndex)->text() - .mid(line.textStart(), line.textLength()); - // ### there is a bug here. It doesn't seem to use the right - // ### viewportWidth even though it is the textEdit's cursor -} - -int TextPagerCursor::lineHeight() const -{ - int res = -1; - Q_ASSERT(d && d->document); - int layoutIndex = -1; - TextPagerLayout *textLayout = TextLayoutCacheManager::requestLayout(*this, 2); - QTextLine line = textLayout->lineForPosition(position(), 0, &layoutIndex); - if (line.isValid()) - res = line.height(); - return res; -} - - -QString TextPagerCursor::wordUnderCursor() const -{ - Q_ASSERT(!isNull()); - return d->document->d->wordAt(d->position); -} - -QString TextPagerCursor::paragraphUnderCursor() const -{ - Q_ASSERT(!isNull()); - return d->document->d->paragraphAt(d->position); -} - -QDebug operator<<(QDebug dbg, const TextPagerCursor &cursor) -{ - QString ret = QString::fromLatin1("TextCursor("); - if (cursor.isNull()) { - ret.append(QLatin1String("null)")); - dbg.maybeSpace() << ret; - } else { - if (!cursor.hasSelection()) { - dbg << "anchor/position:" << cursor.anchor() << "character:" << cursor.cursorCharacter(); - } else { - dbg << "anchor:" << cursor.anchor() << "position:" << cursor.position() - << "selectionSize:" << cursor.selectionSize(); - QString selectedText; - if (cursor.selectionSize() > 10) { - selectedText = cursor.document()->read(cursor.selectionStart(), 7); - selectedText.append("..."); - } else { - selectedText = cursor.selectedText(); - } - dbg << "selectedText:" << selectedText; - } - } - return dbg.space(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERCURSOR_HPP__ -#define TEXTPAGERCURSOR_HPP__ - -#include -#include -#include -#include - -class TextPagerEdit; -class TextPagerLayout; -class TextPagerDocument; -class TextCursorSharedPrivate; -class TextPagerCursor -{ -public: - TextPagerCursor(); - explicit TextPagerCursor(const TextPagerDocument *document, int pos = 0, int anchor = -1); - explicit TextPagerCursor(const TextPagerEdit *document, int pos = 0, int anchor = -1); - TextPagerCursor(const TextPagerCursor &cursor); - TextPagerCursor &operator=(const TextPagerCursor &other); - ~TextPagerCursor(); - - TextPagerDocument *document() const; - bool isNull() const; - inline bool isValid() const { return !isNull(); } - - enum MoveMode { - MoveAnchor, - KeepAnchor - }; - - void setPosition(int pos, MoveMode mode = MoveAnchor); - int position() const; - - void setSelection(int pos, int length); // can be negative - - int viewportWidth() const; - void setViewportWidth(int width); - - int anchor() const; - - QChar cursorCharacter() const; - QString cursorLine() const; - int lineHeight() const; - - QString wordUnderCursor() const; - QString paragraphUnderCursor() const; - - enum MoveOperation { - NoMove, - Start, - Up, - StartOfLine, - StartOfBlock, - StartOfWord, - PreviousBlock, - PreviousCharacter, - PreviousWord, - Left, - WordLeft, - End, - Down, - EndOfLine, - EndOfWord, - EndOfBlock, - NextBlock, - NextCharacter, - NextWord, - Right, - WordRight - }; - - bool movePosition(MoveOperation op, MoveMode = MoveAnchor, int n = 1); - - enum SelectionType { - WordUnderCursor, - LineUnderCursor, - BlockUnderCursor - }; - - void select(SelectionType selection); - - bool hasSelection() const; - - void clearSelection(); - int selectionStart() const; - int selectionEnd() const; - int selectionSize() const; - inline int selectionLength() const { return selectionSize(); } - - QString selectedText() const; - - bool atBlockStart() const; - bool atBlockEnd() const; - bool atStart() const; - bool atEnd() const; - - bool operator!=(const TextPagerCursor &rhs) const; - bool operator<(const TextPagerCursor &rhs) const; - bool operator<=(const TextPagerCursor &rhs) const; - bool operator==(const TextPagerCursor &rhs) const; - bool operator>=(const TextPagerCursor &rhs) const; - bool operator>(const TextPagerCursor &rhs) const; - - bool isCopyOf(const TextPagerCursor &other) const; - - int columnNumber() const; - int lineNumber() const; -private: - bool cursorMoveKeyEvent(QKeyEvent *e); - void cursorChanged(bool ensureCursorVisible); - void detach(); - bool ref(); - bool deref(); - - TextCursorSharedPrivate *d; - TextPagerEdit *textEdit; - friend class TextPagerEdit; - friend class TextLayoutCacheManager; - friend class TextPagerDocument; - friend class TextDocumentPrivate; -}; - -QDebug operator<<(QDebug dbg, const TextPagerCursor &cursor); - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor_p.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor_p.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerCursor_p.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerCursor_p.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,215 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERCURSOR_P_HPP__ -#define TEXTPAGERCURSOR_P_HPP__ - -#include -#include -#include -#include -#include -#include -#include -#include "TextPagerCursor_p.hpp" -#include "TextPagerEdit.hpp" -#include "TextPagerEdit_p.hpp" - -static inline bool match(const TextPagerCursor &cursor, const TextPagerLayout *layout, int lines) -{ - Q_ASSERT(cursor.document() == layout->document); - if (cursor.viewportWidth() != -1 && cursor.viewportWidth() != layout->viewport) { - return false; - } - if (layout->viewportPosition > cursor.position() - || layout->layoutEnd < cursor.position()) { - return false; - } - int index = -1; - if (!layout->layoutForPosition(cursor.position(), 0, &index)) { - // ### need an interface for this if I am going to have a mode - // ### that doesn't break lines - return false; - } - - if (index < lines && layout->viewportPosition > 0) { - return false; // need more margin before the cursor - } else if (layout->textLayouts.size() - index - 1 < lines && layout->layoutEnd < layout->document->documentSize()) { - return false; // need more margin after cursor - } - return true; -} - -class TextLayoutCacheManager : public QObject -{ - Q_OBJECT -public: - static TextLayoutCacheManager *instance() - { - static TextLayoutCacheManager *inst = new TextLayoutCacheManager(QCoreApplication::instance()); - return inst; - } - // ### this class doesn't react to TextSections added or removed. I - // ### don't think it needs to since it's only being used for - // ### cursor movement which shouldn't be impacted by these things - - static TextPagerLayout *requestLayout(const TextPagerCursor &cursor, int margin) - { - Q_ASSERT(cursor.document()); - if (cursor.textEdit && match(cursor, cursor.textEdit->d, margin)) { - return cursor.textEdit->d; - } - - TextPagerDocument *doc = cursor.document(); - Q_ASSERT(doc); - QList &layouts = instance()->cache[doc]; - Q_ASSERT(cursor.document()); - Q_FOREACH(TextPagerLayout *l, layouts) { - if (match(cursor, l, margin)) { - return l; - } - } - if (layouts.size() < instance()->maxLayouts) { - if (layouts.isEmpty()) { - connect(cursor.document(), SIGNAL(charactersAdded(int, int)), - instance(), SLOT(onCharactersAddedOrRemoved(int))); - connect(cursor.document(), SIGNAL(charactersRemoved(int, int)), - instance(), SLOT(onCharactersAddedOrRemoved(int))); - connect(cursor.document(), SIGNAL(destroyed(QObject*)), - instance(), SLOT(onDocumentDestroyed(QObject*))); - } - layouts.append(new TextPagerLayout(doc)); - } - TextPagerLayout *l = layouts.last(); - l->viewport = cursor.viewportWidth(); - if (l->viewport == -1) - l->viewport = INT_MAX - 1024; // prevent overflow in comparisons. - if (cursor.textEdit) { - l->textEdit = cursor.textEdit; - l->suppressTextEditUpdates = true; - l->lineBreaking = cursor.textEdit->lineBreaking(); - l->sections = cursor.textEdit->d->sections; - l->font = cursor.textEdit->font(); - l->syntaxHighlighters = cursor.textEdit->syntaxHighlighters(); -// l->extraSelections = cursor.textEdit->extraSelections(); - // ### can the extra selections impact layout? If so they - // ### need to be in the actual textLayout shouldn't need - // ### to care about the actual selection - } - int startPos = (cursor.position() == 0 - ? 0 - : qMax(0, doc->find(QLatin1Char('\n'), cursor.position() - 1, TextPagerDocument::FindBackward).anchor())); - // We start at the beginning of the current line - int linesAbove = margin; - if (startPos > 0) { - while (linesAbove > 0) { - const TextPagerCursor c = doc->find(QLatin1Char('\n'), startPos - 1, TextPagerDocument::FindBackward); - if (c.isNull()) { - startPos = 0; - break; - } - startPos = c.anchor(); - ASSUME(c.anchor() == 0 || c.cursorCharacter() == QLatin1Char('\n')); - ASSUME(c.anchor() == 0 || doc->readCharacter(c.anchor()) == QLatin1Char('\n')); - ASSUME(c.cursorCharacter() == doc->readCharacter(c.anchor())); - - --linesAbove; - } - } - - int linesBelow = margin; - int endPos = cursor.position(); - if (endPos < doc->documentSize()) { - while (linesBelow > 0) { - const TextPagerCursor c = doc->find(QLatin1Char('\n'), endPos + 1); - if (c.isNull()) { - endPos = doc->documentSize(); - break; - } - endPos = c.anchor(); - --linesBelow; - } - } - if (startPos > 0) - ++startPos; // in this case startPos points to the newline before it - l->viewportPosition = startPos; - l->layoutDirty = true; - ASSUME(l->viewportPosition == 0 || doc->readCharacter(l->viewportPosition - 1) == QLatin1Char('\n')); - l->relayoutByPosition(endPos - startPos + 100); // ### fudged a couple of lines likely - ASSUME(l->viewportPosition < l->layoutEnd - || (l->viewportPosition == l->layoutEnd && l->viewportPosition == doc->documentSize())); - ASSUME(l->textLayouts.size() > margin * 2 || l->viewportPosition == 0 || l->layoutEnd == doc->documentSize()); - return l; - } -private Q_SLOTS: - void onDocumentDestroyed(QObject *o) - { - qDeleteAll(cache.take(static_cast(o))); - } - - void onCharactersAddedOrRemoved(int pos) - { - QList &layouts = cache[qobject_cast(sender())]; - ASSUME(!layouts.isEmpty()); - for (int i=layouts.size() - 1; i>=0; --i) { - TextPagerLayout *l = layouts.at(i); - if (pos <= l->layoutEnd) { - delete l; - layouts.removeAt(i); - } - } - if (layouts.isEmpty()) { - disconnect(sender(), 0, this, 0); - cache.remove(qobject_cast(sender())); - } - } -private: - TextLayoutCacheManager(QObject *parent) - : QObject(parent) - { - maxLayouts = qMax(1, qgetenv("TEXTCURSOR_MAX_CACHED_TEXTLAYOUTS").toInt()); - } - - int maxLayouts; - QHash > cache; -}; - -class TextPagerLayout; -struct TextCursorSharedPrivate -{ -public: - TextCursorSharedPrivate() : ref(1), position(-1), anchor(-1), - overrideColumn(-1), viewportWidth(-1), - document(0) - {} - - ~TextCursorSharedPrivate() - { - int refint = ref.fetchAndAddRelaxed(0); // required to get past a bug in Qt 5.2.1 (see Ubuntu 14.04) - ASSUME(refint == 0); - } - - void invalidate() - { - - } - - mutable QAtomicInt ref; - int position, anchor, overrideColumn, viewportWidth; - - TextPagerDocument *document; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1789 +0,0 @@ -#include "TextPagerDocument.hpp" -#include "TextPagerCursor.hpp" -#include "TextPagerCursor_p.hpp" -#include "TextPagerDocument_p.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -//#define TEXTDOCUMENT_FIND_DEBUG -#define TEXTDOCUMENT_USE_FILE_LOCK - - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - #include -#endif - -// #define DEBUG_CACHE_HITS - -#ifndef TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE -#define TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE 1000 -#endif - -#ifndef TEXTDOCUMENT_MAX_INTERVAL -#define TEXTDOCUMENT_MAX_INTERVAL 1000 -#endif - -#ifdef TEXTDOCUMENT_FIND_SLEEP -static void findSleep(const TextPagerDocument *document) -{ - Q_ASSERT(document); - const int duration = document->property("TEXTDOCUMENT_FIND_SLEEP").toInt(); - if (duration > 0) { - usleep(duration * 1000); - } -} -#endif - -TextPagerDocument::TextPagerDocument(QObject *parent) - : QObject(parent), d(new TextDocumentPrivate(this)) -{ -} - -TextPagerDocument::~TextPagerDocument() -{ - { - QWriteLocker locker(d->readWriteLock); - Q_FOREACH(TextCursorSharedPrivate *cursor, d->textCursors) { - cursor->document = 0; - } - Chunk *c = d->first; - while (c) { - if (!(d->options & KeepTemporaryFiles) && !c->swap.isEmpty()) - QFile::remove(c->swap); - Chunk *tmp = c; - c = c->next; - delete tmp; - } - Q_FOREACH(TextPagerSection *section, d->sections) { - section->d.document = 0; - section->d.textEdit = 0; - delete section; - } - if (d->ownDevice) - delete d->device.data(); - } - delete d->readWriteLock; - delete d; -} - -bool TextPagerDocument::load(QIODevice *device, DeviceMode mode, QTextCodec *codec) -{ - QWriteLocker locker(d->readWriteLock); - Q_ASSERT(device); - if (!device->isReadable()) - return false; - - Options options = d->options; - if (options & ConvertCarriageReturns && mode == Sparse) { - qWarning("TextPagerDocument::load() ConvertCarriageReturns is incompatible with Sparse"); - options &= ~ConvertCarriageReturns; - } - if ((options & (AutoDetectCarriageReturns|ConvertCarriageReturns)) == (AutoDetectCarriageReturns|ConvertCarriageReturns)) - options &= ~AutoDetectCarriageReturns; - - Q_FOREACH(TextPagerSection *section, d->sections) { - Q_EMIT sectionRemoved(section); - section->d.document = 0; - delete section; - } - d->sections.clear(); - - if (d->documentSize > 0) { - Q_EMIT charactersRemoved(0, d->documentSize); - } - - Chunk *c = d->first; - while (c) { - Chunk *tmp = c; - c = c->next; - delete tmp; - } - - d->textCodec = codec; - d->documentSize = device->size(); - if (d->documentSize <= d->chunkSize && mode == Sparse && !(options & NoImplicitLoadAll)) - mode = LoadAll; -#if 0 - if (codec && mode == Sparse) { - - qWarning("Sparse mode doesn't really work with unicode data yet. I am working on it.\n--\nAnders"); - } -#endif - d->first = d->last = 0; - - if (d->device) { - if (d->ownDevice && d->device.data() != device) // this is done when saving to the same file - delete d->device.data(); - } - - d->ownDevice = false; - d->device = device; - d->deviceMode = mode; -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - d->cachedChunk = 0; - d->cachedChunkPos = -1; - d->cachedChunkData.clear(); -#endif -#ifndef NO_TEXTDOCUMENT_READ_CACHE - d->cachePos = -1; - d->cache.clear(); -#endif - - switch (d->deviceMode) { - case LoadAll: { - device->seek(0); - QTextStream ts(device); - if (d->textCodec) - ts.setCodec(d->textCodec); - Chunk *current = 0; - d->documentSize = 0; // in case of unicode - do { - Chunk *c = new Chunk; - c->data = ts.read(d->chunkSize); - if (options & AutoDetectCarriageReturns) { - if (c->data.contains(QLatin1Char('\n'))) { - options |= ConvertCarriageReturns; - } - options &= ~AutoDetectCarriageReturns; - } - if (options & ConvertCarriageReturns) - c->data.remove(QLatin1Char('\r')); - d->documentSize += c->data.size(); - if (current) { - current->next = c; - c->previous = current; - } else { - d->first = c; - } - current = c; - } while (!ts.atEnd()); - - d->last = current; - break; } - - case Sparse: { - int index = 0; - Chunk *current = 0; - do { - Chunk *chunk = new Chunk; - chunk->from = index; - chunk->length = qMin(d->documentSize - index, d->chunkSize); - if (!current) { - d->first = chunk; - } else { - chunk->previous = current; - current->next = chunk; - } - current = chunk; - index += chunk->length; - } while (index < d->documentSize); - - d->last = current; - break; } - } -// if (d->first) -// d->first->firstLineIndex = 0; - Q_EMIT charactersAdded(0, d->documentSize); - Q_EMIT documentSizeChanged(d->documentSize); - Q_EMIT textChanged(); - - return true; -} - -bool TextPagerDocument::load(const QString &fileName, DeviceMode mode, QTextCodec *codec) -{ - if (mode == LoadAll) { - QFile from(fileName); - return from.open(QIODevice::ReadOnly) && load(&from, mode, codec); - } else { - QFile *file = new QFile(fileName); - if (file->open(QIODevice::ReadOnly) && load(file, mode, codec)) { - d->ownDevice = true; - return true; - } else { - delete file; - d->ownDevice = false; - return false; - } - } -} - -void TextPagerDocument::clear() -{ - setText(QString()); -} - -QString TextPagerDocument::read(int pos, int size) const -{ - QReadLocker locker(d->readWriteLock); - Q_ASSERT(size >= 0); - if (size == 0 || pos == d->documentSize) { - return QString(); - } - Q_ASSERT(pos < d->documentSize); - -#ifndef NO_TEXTDOCUMENT_READ_CACHE -#ifdef DEBUG_CACHE_HITS - static int hits = 0; - static int misses = 0; -#endif - if (d->cachePos != -1 && pos >= d->cachePos && d->cache.size() - (pos - d->cachePos) >= size) { -#ifdef DEBUG_CACHE_HITS - qWarning() << "read hits" << ++hits << "misses" << misses; -#endif - return d->cache.mid(pos - d->cachePos, size); - } -#ifdef DEBUG_CACHE_HITS - qWarning() << "read hits" << hits << "misses" << ++misses; -#endif -#endif - - QString ret(size, '\0'); - int written = 0; - int offset; - Chunk *c = d->chunkAt(pos, &offset); - Q_ASSERT(c); - int chunkPos = pos - offset; - - while (written < size && c) { - const int max = qMin(size - written, c->size() - offset); - const QString data = d->chunkData(c, chunkPos); - chunkPos += data.size(); - ret.replace(written, max, data.constData() + offset, max); - written += max; - offset = 0; - c = c->next; - } - - if (written < size) { - ret.truncate(written); - } - Q_ASSERT(!c || written == size); -#ifndef NO_TEXTDOCUMENT_READ_CACHE - d->cachePos = pos; - d->cache = ret; -#endif - return ret; -} - -QStringRef TextPagerDocument::readRef(int pos, int size) const -{ - QReadLocker locker(d->readWriteLock); - int offset; - Chunk *c = d->chunkAt(pos, &offset); - if (c && pos + offset + size <= c->size()) { - const QString string = d->chunkData(c, pos - offset); - return string.midRef(offset, size); - } - return QStringRef(); -} - -#if 0 -static bool isSameFile(const QIODevice *left, const QIODevice *right) -{ - if (left == right) - return true; - if (const QFile *lf = qobject_cast(left)) { - if (const QFile *rf = qobject_cast(right)) { - return QFileInfo(*lf) == QFileInfo(*rf); - } - } - return false; -} -#endif - -int TextPagerDocument::documentSize() const -{ - QReadLocker locker(d->readWriteLock); - return d->documentSize; -} - -int TextPagerDocument::chunkCount() const -{ - QReadLocker locker(d->readWriteLock); - Chunk *c = d->first; - int count = 0; - while (c) { - ++count; - c = c->next; - } - return count; -} - -int TextPagerDocument::instantiatedChunkCount() const -{ - QReadLocker locker(d->readWriteLock); - Chunk *c = d->first; - int count = 0; - while (c) { - if (!c->data.isEmpty()) - ++count; - c = c->next; - } - return count; -} - -int TextPagerDocument::swappedChunkCount() const -{ - QReadLocker locker(d->readWriteLock); - Chunk *c = d->first; - int count = 0; - while (c) { - if (!c->swap.isEmpty()) - ++count; - c = c->next; - } - return count; -} - -TextPagerDocument::DeviceMode TextPagerDocument::deviceMode() const -{ - QReadLocker locker(d->readWriteLock); - return d->deviceMode; -} - -QTextCodec * TextPagerDocument::textCodec() const -{ - QReadLocker locker(d->readWriteLock); - return d->textCodec; -} - -class FindScope -{ -public: - FindScope(TextDocumentPrivate::FindState *s) : state(s) { if (state) *state = TextDocumentPrivate::Finding; } - ~FindScope() { if (state) *state = TextDocumentPrivate::NotFinding; } - TextDocumentPrivate::FindState *state; -}; - -static void initFind(const TextPagerCursor &cursor, bool reverse, int *start, int *limit) -{ - if (cursor.hasSelection()) { - *start = cursor.selectionStart(); - *limit = cursor.selectionEnd(); - if (reverse) { - qSwap(*start, *limit); - } - } else { - *start = cursor.position(); - *limit = (reverse ? 0 : cursor.document()->documentSize()); - } - - -} - -static void initFindForLines(const TextPagerCursor &cursor, bool reverse, int *start, int *limit) -{ - *start = cursor.position(); - if(*limit == -1) - *limit = (reverse ? 0 : cursor.document()->documentSize()); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "initial position" << "pos:" << *start << "anchor:" << cursor.anchor(); -#endif - - //Change the search position according to the selection and search - //direction - if(cursor.anchor() >=0) - { - int ca=cursor.anchor(); - - if(!reverse) - { - if(*start < ca) - *start=ca; - } - else - { - if(*start > ca && ca >0) - *start=ca-1; - else if(*start >0) - *start=(*start)-1; - } - } - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "modified position" << "pos:" << *start << "anchor:" << cursor.anchor(); -#endif - -} - -TextPagerCursor TextPagerDocument::find(const QRegExp ®exp, const TextPagerCursor &cursor, FindMode flags,int limit) const -{ -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "---> TextPagerDocument::find" << "regexp" << regexp; -#endif - - if(documentSize() == 0) - return TextPagerCursor(); - - if(regexp.isEmpty()) - return TextPagerCursor(); - - QReadLocker locker(d->readWriteLock); - if (flags & FindWholeWords) { - qWarning("FindWholeWords doesn't work with regexps. Instead use an actual RegExp for this"); - } - if (flags & FindCaseSensitively) { - qWarning("FindCaseSensitively doesn't work with regexps. Instead use an QRegExp::caseSensitivity for this"); - } - /* if (flags & FindWrap && cursor.hasSelection()) { - qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); - flags &= ~FindWrap; - }*/ - - const bool reverse = flags & FindBackward; - int pos; - ::initFindForLines(cursor, reverse, &pos, &limit); - - if(reverse) { - if(pos < limit) - return TextPagerCursor(); - } else { - if(pos > limit) - return TextPagerCursor(); - } - - if (pos == d->documentSize) { - if (reverse) { - --pos; - } else if (!(flags & FindWrap)) { - return TextPagerCursor(); - } - } - - //const TextDocumentIterator::Direction direction = (reverse - // ? TextDocumentIterator::Left - // : TextDocumentIterator::Right); - TextDocumentIterator it(d, pos); - if (reverse) { - it.setMinBoundary(limit); - } else { - it.setMaxBoundary(limit); - } - const QLatin1Char newline('\n'); - bool ok = true; - //int progressInterval = 0; - const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); - QTime lastProgressTime; - if (flags & FindAllowInterrupt) { - //progressInterval = qMax(1, (reverse - // ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) - // : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); - //maxFindLength = (reverse ? pos : d->documentSize - pos); - lastProgressTime.start(); - } - - QString line; - int index; - int from; -#ifdef TEXTDOCUMENT_FIND_DEBUG - QTime lap; - lap.start(); -#endif - - //Loop over the lines in the document. Since we can have millions of lines it has to be - //extremely fast. - do { -#ifdef TEXTDOCUMENT_FIND_SLEEP - findSleep(this); -#endif - - //This clears the string without reallocation - line.resize(0); - - if(((reverse)?it.prevLine(line):it.nextLine(line)) == 0) - { - ok=false; - if(line.isEmpty()) - break; - } - - //We only search for the first occurrence. So the FindAll flag is ignored. - - //QRegExp::lastIndexIn() is 3 times slower than QRegExp::indexIn()!! So we always call - //indexIn() first the lastIndexIn() if we need the reverse order. - - if((index = regexp.indexIn(line, 0)) != -1) { - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << line; -#endif - - if(reverse) { - index = regexp.lastIndexIn(line, line.size()); - from = it.position(); //-line.size(); - - if(from + index < limit) { - ok = false; - } else { - const TextPagerCursor ret(this, from + index, from + index + regexp.matchedLength()); -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); - qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); -#endif - return ret; - } - } else { - - from = it.position()-line.size()+1; - - if(from + index + regexp.matchedLength() > limit) { - ok = false; - } else { - - //we need to remove the newline char from the end of the matching text - QString captured=regexp.capturedTexts().first(); - if(index + regexp.matchedLength() == line.size() && line.endsWith(newline)) - { - captured.chop(1); - } - - const TextPagerCursor ret(this, from + index + captured.size(), from + index); -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "current:" << it.current() << it.position() << line.size(); - qDebug() << "captured:" << index << captured; - qDebug() << "cursor:" << ret.selectedText(); -#endif - Q_ASSERT(ret.selectedText() == captured); -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); - qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); -#endif - return ret; - } - } - } - - - /*if (progressInterval != 0) { - const int progress = qAbs(it.position() - lastProgress); - if (progress >= progressInterval - || (lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { - const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); - Q_EMIT findProgress(progress * 100.0, it.position()); - if (d->findState == TextDocumentPrivate::AbortFind) { - return TextPagerCursor(); - } - lastProgress = it.position(); - lastProgressTime.restart(); - } - }*/ - - } while (ok); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); - qDebug() << "iterator position:" << it.position(); -#endif - - if (flags & FindWrap) { - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "---> end of doc reached. FindWrap will start."; -#endif - //Q_ASSERT(!cursor.hasSelection()); - if (reverse) { - if (cursor.position() + 1 < d->documentSize) { - return find(regexp, TextPagerCursor(this, d->documentSize), flags & ~FindWrap, cursor.position()); - } - } else if (cursor.position() > 0) { - return find(regexp, TextPagerCursor(this, 0), flags & ~FindWrap, cursor.position()); - } - } - - return TextPagerCursor(); -} - -TextPagerCursor TextPagerDocument::find(const QString &in, const TextPagerCursor &cursor, FindMode flags, int limit) const -{ -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "---> TextPagerDocument::find" << "string:" << in; -#endif - - if (in.isEmpty()) { - return TextPagerCursor(); - } - - QReadLocker locker(d->readWriteLock); - - const bool reverse = flags & FindBackward; - const bool caseSensitive = flags & FindCaseSensitively; - const bool wholeWords = flags & FindWholeWords; - - /* if (flags & FindWrap && cursor.hasSelection()) { - qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); - flags &= ~FindWrap; - } -*/ - const QLatin1Char newline('\n'); - int pos; - ::initFindForLines(cursor, reverse, &pos, &limit); - - //Sanity check!! - Q_ASSERT(pos >= 0 && pos <= d->documentSize); - Q_ASSERT(limit >= 0 && limit <= d->documentSize); - - if(reverse) { - if(pos < limit) - return TextPagerCursor(); - } else { - if(pos > limit) - return TextPagerCursor(); - } - - if (pos == d->documentSize) { - if (reverse) { - --pos; - } else if (!(flags & FindWrap)) { - return TextPagerCursor(); - } - } - - // ### what if one searches for a string with non-word characters in it and FindWholeWords? - //const TextDocumentIterator::Direction direction = (reverse ? TextDocumentIterator::Left : TextDocumentIterator::Right); - QString word = caseSensitive ? in : in.toLower(); - - TextDocumentIterator it(d, pos); - if (reverse) { - it.setMinBoundary(limit); - } else { - it.setMaxBoundary(limit); - } - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "current character:" << it.current(); -#endif - - if (!caseSensitive) - it.setConvertToLowerCase(true); - - bool ok = true; - //QChar ch = it.current(); - //int progressInterval = 0; - const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); - QTime lastProgressTime; - if (flags & FindAllowInterrupt) { - //progressInterval = qMax(1, (reverse - // ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) - // : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); - //maxFindLength = (reverse ? pos : d->documentSize - pos); - lastProgressTime.start(); - } - - QString line; - int index; - int from; -#ifdef TEXTDOCUMENT_FIND_DEBUG - QTime lap; - lap.start(); -#endif - - do { -#ifdef TEXTDOCUMENT_FIND_SLEEP - findSleep(this); -#endif - /* if (progressInterval != 0) { - const int progress = qAbs(it.position() - lastProgress); - if (progress >= progressInterval - || (progress % 10 == 0 && lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { - const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); - Q_EMIT findProgress(progress * 100.0, it.position()); - if (d->findState == TextDocumentPrivate::AbortFind) { - return TextPagerCursor(); - } - lastProgress = it.position(); - lastProgressTime.restart(); - } - }*/ - - //This clears the string without reallocation - line.resize(0); - - if(((reverse)?it.prevLine(line):it.nextLine(line)) == 0) - { - ok=false; - if(line.isEmpty()) - break; - } - - if(!caseSensitive) - line=line.toLower(); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - //qDebug() << line; -#endif - - if((index=line.indexOf(word)) != -1) { - - //Backward: - //The iterator is positioned at the linebreak character of the previous line, or at - //the start of the document - if(reverse) { - from = it.position(); - index=line.lastIndexOf(word); - } - //Forward: - //The iterator is positioned at the linebreak character at the end of the line or at - //the end of the document - else { - from = it.position()-line.size()+1; - } - - while(ok && index != -1) { - - const int startPos=from + index; - const int endPos=from + index + word.size(); - - if(!reverse && endPos > limit) { - ok = false; - break; - } - - bool found=true; - if(wholeWords) { - - if(TextDocumentIterator::Left != d->wordBoundariesAt(startPos)) { - found = false; - } - if(found) { - if(TextDocumentIterator::Right != d->wordBoundariesAt(endPos-1)) { - found = false; - } - } - } - - if(found) { - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << line; -#endif - //Backward - if(reverse) { - TextPagerCursor ret(this, startPos, endPos); -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); - qDebug() << "current:" << it.current() << it.position() << line.size(); - qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); -#endif - return ret; - //Forward - } else { - - TextPagerCursor ret(this, endPos,startPos); -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); - qDebug() << "current:" << it.current() << it.position() << line.size(); - qDebug() << "result" << "pos:" << ret.position() << "anchor:" << ret.anchor(); -#endif - return ret; - } - - //If it is not a wholeword we try to match every other match int he same line - } else { - - if(reverse) { - index=line.lastIndexOf(word,index-1); - } else { - index=line.indexOf(word,index+word.size()); - } - - } - } //while - - } - -#if 0 - bool found = ch == word.at(wordIndex); - if (found && wholeWords && (wordIndex == 0 || wordIndex == word.size() - 1)) { - Q_ASSERT(word.size() > 1); - const uint requiredBounds = ((wordIndex == 0) != reverse) - ? TextDocumentIterator::Left - : TextDocumentIterator::Right; - const uint bounds = d->wordBoundariesAt(it.position()); - if (requiredBounds & ~bounds) { - found = false; - } - } - if (found) { - if (++wordIndex == word.size()) { - const int pos = it.position() - (reverse ? 0 : word.size() - 1); - // the iterator reads one past the last matched character so we have to account for that here - const TextPagerCursor ret(this, pos + wordIndex, pos); - if (flags & FindAll) { - Q_EMIT entryFound(ret); - if (d->findState == TextDocumentPrivate::AbortFind) - return TextPagerCursor(); - wordIndex = 0; - } else { - return ret; - } - } - } else if (wordIndex != 0) { - wordIndex = 0; - continue; - } - ch = it.nextPrev(direction, ok); - -#endif - - } while (ok); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "total time" << lap.elapsed(); -#endif - - if (flags & FindWrap) { - //Q_ASSERT(!cursor.hasSelection()); - if (reverse) { - - if (cursor.position() + 1 < d->documentSize) { - return find(in, TextPagerCursor(this, d->documentSize), flags & ~FindWrap,cursor.position()); - } - } else if (cursor.position() > 0) { - return find(in, TextPagerCursor(this, 0), flags & ~FindWrap,cursor.position()); - } - } - - return TextPagerCursor(); -} - -TextPagerCursor TextPagerDocument::find(const QChar &chIn, const TextPagerCursor &cursor, FindMode flags) const -{ - QReadLocker locker(d->readWriteLock); - if (flags & FindWrap && cursor.hasSelection()) { - qWarning("It makes no sense to pass FindWrap and set a selection for the cursor. The entire selection will be searched"); - flags &= ~FindWrap; - } - - const bool reverse = flags & FindBackward; - int pos; - int limit; - ::initFind(cursor, reverse, &pos, &limit); - if (pos == d->documentSize) { - if (reverse) { - --pos; - } else if (!(flags & FindWrap)) { - return TextPagerCursor(); - } - } - Q_ASSERT(pos >= 0 && pos <= d->documentSize); - - const bool caseSensitive = flags & FindCaseSensitively; - const bool wholeWords = flags & FindWholeWords; - const QChar ch = (caseSensitive ? chIn : chIn.toLower()); - TextDocumentIterator it(d, pos); - if (reverse) { - it.setMinBoundary(limit); - } else { - it.setMaxBoundary(limit); - } - const TextDocumentIterator::Direction dir = (reverse - ? TextDocumentIterator::Left - : TextDocumentIterator::Right); - int lastProgress = pos; - const int initialPos = pos; - int maxFindLength = 0; - int progressInterval = 0; - const FindScope scope(flags & FindAllowInterrupt ? &d->findState : 0); - QTime lastProgressTime; - if (flags & FindAllowInterrupt) { - progressInterval = qMax(1, (reverse - ? (static_cast(pos) / static_cast(TEXTDOCUMENT_FIND_INTERVAL_PERCENTAGE)) - : (static_cast(d->documentSize) - static_cast(pos)) / 100.0)); - maxFindLength = (reverse ? pos : d->documentSize - pos); - lastProgressTime.start(); - } - - QChar c = it.current(); - bool ok = true; - do { -#ifdef TEXTDOCUMENT_FIND_SLEEP - findSleep(this); -#endif - if (((caseSensitive ? c : c.toLower()) == ch) - && (!wholeWords || (d->wordBoundariesAt(it.position()) == TextDocumentIterator::Both))) { - const TextPagerCursor ret(this, it.position() + 1, it.position()); - if (flags & FindAll) { - Q_EMIT entryFound(ret); - if (d->findState == TextDocumentPrivate::AbortFind) - return TextPagerCursor(); - } else { - return ret; - } - } - c = it.nextPrev(dir, ok); -// qDebug() << "progressInterval" << progressInterval << qAbs(it.position() - lastProgress) -// << lastProgressTime.elapsed() << TEXTDOCUMENT_MAX_INTERVAL; - if (progressInterval != 0) { - const int progress = qAbs(it.position() - lastProgress); - if (progress >= progressInterval - || (progress % 10 == 0 && lastProgressTime.elapsed() >= TEXTDOCUMENT_MAX_INTERVAL)) { - const qreal progress = qAbs(static_cast(it.position() - initialPos)) / static_cast(maxFindLength); - Q_EMIT findProgress(progress * 100.0, it.position()); - if (d->findState == TextDocumentPrivate::AbortFind) { - return TextPagerCursor(); - } - lastProgress = it.position(); - lastProgressTime.restart(); - } - } - } while (ok); - - if (flags & FindWrap) { - Q_ASSERT(!cursor.hasSelection()); - if (reverse) { - if (cursor.position() + 1 < d->documentSize) { - return find(ch, TextPagerCursor(this, cursor.position(), d->documentSize), flags & ~FindWrap); - } - } else if (cursor.position() > 0) { - return find(ch, TextPagerCursor(this, 0, cursor.position()), flags & ~FindWrap); - } - } - - return TextPagerCursor(); -} - -static inline int count(const QString &string, int from, int size, const QChar &ch) -{ - Q_ASSERT(from + size <= string.size()); - const ushort needle = ch.unicode(); - const ushort *haystack = string.utf16() + from; - int num = 0; - for (int i=0; ireadWriteLock); - Q_ASSERT(section); - Q_ASSERT(section->document() == this); - - QList::iterator first = qLowerBound(d->sections.begin(), d->sections.end(), section, compareTextSection); - Q_ASSERT(first != d->sections.end()); - const QList::iterator last = qUpperBound(d->sections.begin(), d->sections.end(), section, compareTextSection); - - while (first != last) { - if (*first == section) { - Q_EMIT sectionRemoved(section); - d->sections.erase(first); - break; - } - ++first; - } - - // Moved this to the end as the slots called by sectionRemoved (presently) rely - // on section->d.document to be valid. - section->d.textEdit = 0; - section->d.document = 0; -} - -QList TextPagerDocument::sections(int pos, int size, TextPagerSection::TextSectionOptions flags) const -{ - QReadLocker locker(d->readWriteLock); - return d->getSections(pos, size, flags, 0); -} - -void TextPagerDocument::insertTextSection(TextPagerSection *section) -{ - QWriteLocker locker(d->readWriteLock); - Q_ASSERT(!d->sections.contains(section)); - QList::iterator it = qLowerBound::iterator>(d->sections.begin(), d->sections.end(), - section, compareTextSection); - d->sections.insert(it, section); - Q_EMIT sectionAdded(section); -} - -TextPagerSection *TextPagerDocument::insertTextSection(int pos, int size, - const QTextCharFormat &format, const QVariant &data) -{ - QWriteLocker locker(d->readWriteLock); - Q_ASSERT(pos >= 0); - Q_ASSERT(size >= 0); - Q_ASSERT(pos < d->documentSize); - - TextPagerSection *l = new TextPagerSection(pos, size, this, format, data); - QList::iterator it = qLowerBound::iterator>(d->sections.begin(), d->sections.end(), l, compareTextSection); - d->sections.insert(it, l); - Q_EMIT sectionAdded(l); - return l; -} - -bool TextPagerDocument::abortSave() -{ - if (d->saveState == TextDocumentPrivate::Saving) { - d->saveState = TextDocumentPrivate::AbortSave; - return true; - } - return false; -} - -bool TextPagerDocument::abortFind() const -{ - if (d->findState == TextDocumentPrivate::Finding) { - d->findState = TextDocumentPrivate::AbortFind; - return true; - } - return false; -} - - -QChar TextPagerDocument::readCharacter(int pos) const -{ - QReadLocker locker(d->readWriteLock); - if (pos == d->documentSize) - return QChar(); - Q_ASSERT(pos >= 0 && pos < d->documentSize); -#ifndef NO_TEXTDOCUMENT_READ_CACHE -#ifdef DEBUG_CACHE_HITS - static int hits = 0; - static int misses = 0; -#endif - if (pos >= d->cachePos && pos < d->cachePos + d->cache.size()) { -#ifdef DEBUG_CACHE_HITS - qWarning() << "readCharacter hits" << ++hits << "misses" << misses; -#endif - return d->cache.at(pos - d->cachePos); - } -#ifdef DEBUG_CACHE_HITS - qWarning() << "readCharacter hits" << hits << "misses" << ++misses; -#endif -#endif - - int offset; - Chunk *c = d->chunkAt(pos, &offset); - return d->chunkData(c, pos - offset).at(offset); -} - -void TextPagerDocument::setText(const QString &text) -{ - // ### could optimize this to avoid detach and copy if text.size() <= chunkSize - // ### but is it worth it? - QBuffer buffer; - buffer.open(QIODevice::WriteOnly); - QTextStream ts(&buffer); - if (d->textCodec) - ts.setCodec(d->textCodec); - ts << text; - buffer.close(); - buffer.open(QIODevice::ReadOnly); - const bool ret = load(&buffer, LoadAll); - Q_ASSERT(ret); - Q_UNUSED(ret); -} - -int TextPagerDocument::chunkSize() const -{ - QReadLocker locker(d->readWriteLock); - return d->chunkSize; -} - -void TextPagerDocument::setChunkSize(int size) -{ - QWriteLocker locker(d->readWriteLock); - Q_ASSERT(d->chunkSize > 0); - d->chunkSize = size; -} - -int TextPagerDocument::currentMemoryUsage() const -{ - QReadLocker locker(d->readWriteLock); - Chunk *c = d->first; - int used = 0; - while (c) { - used += c->data.size() * sizeof(QChar); - c = c->next; - } - return used; -} - -bool TextPagerDocument::isModified() const -{ - // ### should it lock for read? - return d->modified; -} - -int TextPagerDocument::lineNumber(int position) const -{ - QReadLocker locker(d->readWriteLock); - d->hasChunksWithLineNumbers = true; // does this need to be a write lock? - int offset; - Chunk *c = d->chunkAt(position, &offset); - d->updateChunkLineNumbers(c, position - offset); - Q_ASSERT(c->firstLineIndex != -1); - Q_ASSERT(d->first->firstLineIndex != -1); - const int extra = (offset == 0 ? 0 : d->countNewLines(c, position - offset, offset)); - -//#ifdef QT_DEBUG -#if 0 - if (position <= 16000) { - const QString data = read(0, position); - // if we're on a newline it shouldn't count so we do read(0, position) - // not read(0, position + 1); - const int count = data.count(QLatin1Char('\n')); - if (count != c->firstLineIndex + extra) { - qDebug() << "TextPagerDocument::lineNumber returns" << (c->firstLineIndex + extra) - << "should have returned" << (count + 1) - << "for index" << position; - } - } -#endif - - return c->firstLineIndex + extra; -} - -int TextPagerDocument::columnNumber(int position) const -{ - TextPagerCursor cursor(this, position); - return cursor.isNull() ? -1 : cursor.columnNumber(); -} - -int TextPagerDocument::lineNumber(const TextPagerCursor &cursor) const -{ - return cursor.document() == this ? lineNumber(cursor.position()) : -1; -} - -int TextPagerDocument::columnNumber(const TextPagerCursor &cursor) const -{ - return cursor.document() == this ? cursor.columnNumber() : -1; -} - -TextPagerCursor TextPagerDocument::findLine(int lineNum, const TextPagerCursor &cursor) const -{ - if(lineNum <=0) - return TextPagerCursor(); - - lineNum--; - - Q_ASSERT(cursor.position() >=0); - - int current=lineNumber(cursor); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "findLine --> line:" << lineNum << "current:" << current; -#endif - - if(lineNum == current) - return cursor; - - int offset; - Chunk *c = d->chunkAt(cursor.position(), &offset); - - Q_ASSERT(c != NULL); - - int pos=cursor.position() - offset; //points to the chunks beginning - d->updateChunkLineNumbers(c, pos); - -#ifdef TEXTDOCUMENT_FIND_DEBUG - qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; -#endif - - if(lineNum < current) { - - while(c->firstLineIndex > lineNum && c->previous) { - pos-=c->size(); - c=c->previous; - d->updateChunkLineNumbers(c,pos); -#ifdef TEXTDOCUMENT_FIND_DEBUG - //qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; -#endif - } - - } else if(lineNum > current) { - - while(c->firstLineIndex < lineNum && c->next) { - pos+=c->size(); - c=c->next; - d->updateChunkLineNumbers(c,pos); -#ifdef TEXTDOCUMENT_FIND_DEBUG - //qDebug() << "chunk - first line:" << c->firstLineIndex << "pos:" << pos; -#endif - } - - Q_ASSERT(c != NULL && c->previous != NULL); - - c=c->previous; - pos-=c->size(); - } - -#ifdef TEXTDOCUMENT_FIND_DEBUG - if(c) qDebug() << "chunk found - first line:" << c->firstLineIndex << "pos:" << pos; - else qDebug() << "chunk not found"; -#endif - if(c && c->firstLineIndex != -1 && c->firstLineIndex <= lineNum) { - - current=c->firstLineIndex; - if(current == lineNum) - return TextPagerCursor(this,pos); - - QChar newline('\n'); - int index=0; - QString data=d->chunkData(c,-1); - while((index=data.indexOf(newline,index)) != -1) { -#ifdef TEXTDOCUMENT_FIND_DEBUG - //qDebug() << "chunk found - line:" << current << "index:" << index; -#endif - if(current == lineNum) { - TextPagerCursor ret(this,pos+index); - return ret; - } - index++; - current++; - } - } - - return TextPagerCursor(); -} - - -void TextPagerDocument::setOptions(Options opt) -{ - d->options = opt; - if ((d->options & Locking) != (d->readWriteLock != 0)) { - if (d->readWriteLock) { - delete d->readWriteLock; - } else { - d->readWriteLock = new QReadWriteLock(QReadWriteLock::Recursive); - } - } -} - -TextPagerDocument::Options TextPagerDocument::options() const -{ - return d->options; -} - -bool TextPagerDocument::isWordCharacter(const QChar &ch, int /*index*/) const -{ - // from qregexp. - return ch.isLetterOrNumber() || ch.isMark() || ch == QLatin1Char('_'); -} - -QString TextPagerDocument::swapFileName(Chunk *chunk) -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QReadLocker locker(d->readWriteLock); - QString file = QStandardPaths::standardLocations(QStandardPaths::TempLocation).first(); - file.reserve(file.size() + 24); - QTextStream ts(&file); - ts << QLatin1Char('/') << QLatin1String("lte_") << chunk << QLatin1Char('_') - << this << QLatin1Char('_') << QCoreApplication::applicationPid(); - return file; -#endif - return QString(); -} - -//=========================================================================== -// -// TextDocumentPrivate -// -//=========================================================================== - -Chunk *TextDocumentPrivate::chunkAt(int p, int *offset) const -{ - Q_ASSERT(p <= documentSize); - Q_ASSERT(p >= 0); - Q_ASSERT(first); - Q_ASSERT(last); - if (p == documentSize) { - if (offset) - *offset = last->size(); - return last; - } -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - Q_ASSERT(!cachedChunk || cachedChunkPos != -1); - if (cachedChunk && p >= cachedChunkPos && p < cachedChunkPos + cachedChunkData.size()) { - if (offset) - *offset = p - cachedChunkPos; - return cachedChunk; - } -#endif - int pos = p; - Chunk *c = first; - - Q_FOREVER { - const int size = c->size(); - if (pos < size) { - break; - } - pos -= size; - c = c->next; - Q_ASSERT(c); - } - - if (offset) - *offset = pos; - - Q_ASSERT(c); - return c; -} - - -/* Evil double meaning of pos here. If it's -1 we don't cache it. */ -QString TextDocumentPrivate::chunkData(const Chunk *chunk, int chunkPos) const -{ -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE -#ifdef DEBUG_CACHE_HITS - static int hits = 0; - static int misses = 0; -#endif - if (chunk == cachedChunk) { -#ifdef DEBUG_CACHE_HITS - qWarning() << "chunkData hits" << ++hits << "misses" << misses; -#endif - Q_ASSERT(cachedChunkData.size() == chunk->size()); - return cachedChunkData; - } else -#endif - if (chunk->from == -1) { - return chunk->data; - } else if (!device && chunk->swap.isEmpty()) { - // Can only happen if the device gets deleted behind our back when in Sparse mode - return QString().fill(QLatin1Char(' '), chunk->size()); - } else { - QIODevice *dev = device.data(); -#if 0 - QFile file; - if (!chunk->swap.isEmpty()) { - file.setFileName(chunk->swap); - if (!file.open(QIODevice::ReadOnly)) { - qWarning("TextDocumentPrivate::chunkData() Can't open file for reading '%s'", qPrintable(chunk->swap)); - return QString().fill(QLatin1Char(' '), chunk->size()); - } - dev = &file; - } -#endif - QTextStream ts(dev); - //if (textCodec) - // ts.setCodec(textCodec); -// if (!chunk->swap.isEmpty()) { -// qDebug() << "reading stuff from swap" << chunk << chunk->from << chunk->size() << chunk->swap; -// } - ts.seek(chunk->from); - -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - - if (chunkPos != -1) { - cachedChunkData = ts.read(chunk->length); - - Q_ASSERT(cachedChunkData.size() == chunk->size()); - - cachedChunk = const_cast(chunk); - cachedChunkPos = chunkPos; - - return cachedChunkData; -/*#ifdef QT_DEBUG - if (chunkPos != chunk->pos()) { - qWarning() << chunkPos << chunk->pos(); - } - Q_ASSERT(chunkPos == chunk->pos()); -#endif*/ - } else { - const QString data = ts.read(chunk->length); - return data; - } - -#else - const QString data = ts.read(chunk->length); - return data; -#endif - } - //Q_ASSERT(data.size() == chunk->size()); -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE -#ifdef DEBUG_CACHE_HITS - qWarning() << "chunkData hits" << hits << "misses" << ++misses; -#endif -#endif - -#if 0 - if (chunkPos != -1) { - cachedChunk = const_cast(chunk); - cachedChunkData = data; - cachedChunkPos = chunkPos; -#ifdef QT_DEBUG - if (chunkPos != chunk->pos()) { - qWarning() << chunkPos << chunk->pos(); - } - Q_ASSERT(chunkPos == chunk->pos()); -#endif - } - - //return data; - } -#endif - return QString(); -} - -int TextDocumentPrivate::chunkIndex(const Chunk *c) const -{ - int index = 0; - while (c->previous) { - ++index; - c = c->previous; - } - return index; -} - -void TextDocumentPrivate::instantiateChunk(Chunk *chunk) -{ - if (chunk->from == -1 && chunk->swap.isEmpty()) - return; - chunk->data = chunkData(chunk, -1); -// qDebug() << "instantiateChunk" << chunk << chunk->swap; - chunk->swap.clear(); -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - // Don't want to cache this chunk since it's going away. If it - // already was cached then sure, but otherwise don't - if (chunk == cachedChunk) { - cachedChunk = 0; - cachedChunkPos = -1; - cachedChunkData.clear(); - } -#endif - chunk->from = chunk->length = -1; -} - -void TextDocumentPrivate::removeChunk(Chunk *c) -{ - Q_ASSERT(c); - if (c == first) { - first = c->next; - } else { - c->previous->next = c->next; - } - if (c == last) { - last = c->previous; - } else { - c->next->previous = c->previous; - } -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - if (c == cachedChunk) { - cachedChunk = 0; - cachedChunkPos = -1; - cachedChunkData.clear(); - } -#endif - if (!first) { - Q_ASSERT(!last); - first = last = new Chunk; - } - - delete c; -} - -QString TextDocumentPrivate::wordAt(int position, int *start) const -{ - TextDocumentIterator from(this, position); - if (!q->isWordCharacter(from.current(), position)) { - if (start) - *start = -1; - return QString(); - } - - while (from.hasPrevious()) { - const QChar ch = from.previous(); - if (!q->isWordCharacter(ch, from.position())) { - // ### could just peek rather than going one too far - from.next(); - break; - } - } - TextDocumentIterator to(this, position); - while (to.hasNext()) { - const QChar ch = to.next(); - if (!q->isWordCharacter(ch, to.position())) - break; - } - - if (start) - *start = from.position(); - return q->read(from.position(), to.position() - from.position()); -} - -QString TextDocumentPrivate::paragraphAt(int position, int *start) const -{ - const QLatin1Char newline('\n'); - TextDocumentIterator from(this, position); - while (from.hasPrevious() && from.previous() != newline) - ; - TextDocumentIterator to(this, position); - while (to.hasNext() && to.next() != newline) - ; - if (start) - *start = from.position(); - return q->read(from.position(), to.position() - from.position()); -} - -uint TextDocumentPrivate::wordBoundariesAt(int pos) const -{ - Q_ASSERT(pos >= 0 && pos < documentSize); - uint ret = 0; - if (pos == 0 || !q->isWordCharacter(q->readCharacter(pos - 1), pos - 1)) { - ret |= TextDocumentIterator::Left; - } - if (pos + 1 == documentSize || !q->isWordCharacter(q->readCharacter(pos + 1), pos + 1)) { - ret |= TextDocumentIterator::Right; - } - return ret; -} - -void TextDocumentPrivate::updateChunkLineNumbers(Chunk *c, int chunkPos) const -{ - Q_ASSERT(c); - if (c->firstLineIndex == -1) { - Chunk *cc = c; - int pos = chunkPos; - while (cc->previous && cc->previous->firstLineIndex == -1) { - //pos -= cc->size(); - //Here chunkPos points to the position (the beginning) of the chunk so - //the line above was incorrect had to be changed like this: - pos -=cc->previous->size(); - cc = cc->previous; - } - // cc is at the first chunk that has firstLineIndex != -1 - Q_ASSERT(!cc->previous || cc->previous->firstLineIndex != -1); - Q_ASSERT(cc->firstLineIndex == 1 || cc->firstLineIndex == -1); - // special case for first chunk - do { - const int size = cc->size(); - if (!cc->previous) { - cc->firstLineIndex = 0; - } else { - const int prevSize = cc->previous->size(); - const int lineCount = countNewLines(cc->previous, pos - prevSize, prevSize); - Q_ASSERT(cc->previous->firstLineIndex != -1); - cc->firstLineIndex = cc->previous->firstLineIndex + lineCount; - } - pos += size; - cc = cc->next; - } while (cc && cc != c->next); - countNewLines(c, chunkPos, c->size()); - } - Q_ASSERT(c->firstLineIndex != -1); -} - -static inline QList dumpNewLines(const QString &string, int from, int size) -{ - QList ret; - for (int i=from; ifirstLineIndex << chunkPos << size -// << c->size(); - int ret = 0; -#ifndef TEXTDOCUMENT_LINENUMBER_CACHE - if (size == c->size()) { - if (c->lines == -1) { - c->lines = ::count(chunkData(c, chunkPos), 0, size, QLatin1Char('\n')); -// qDebug() << "counting" << c->lines << "in" << chunkIndex(c) -// << "Size" << size << "chunkPos" << chunkPos; - } - ret = c->lines; - } else { - ret = ::count(chunkData(c, chunkPos), 0, size, QLatin1Char('\n')); - } -#else -// qDebug() << size << ret << c->lineNumbers << chunkPos -// << dumpNewLines(chunkData(c, chunkPos), 0, c->size()); - static const int lineNumberCacheInterval = TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL; - if (c->lineNumbers.isEmpty()) { - const QString data = chunkData(c, chunkPos); - const int s = data.size(); - c->lineNumbers.fill(0, (s + lineNumberCacheInterval - 1) / lineNumberCacheInterval); -// qDebug() << data.size() << c->lineNumbers.size() << lineNumberCacheInterval; - - for (int i=0; ilineNumbers[i / lineNumberCacheInterval]; -// qDebug() << "found one at" << i << "put it in" << (i / lineNumberCacheInterval) -// << "chunkPos" << chunkPos; - if (i < size) - ++ret; - } - } - } else { - for (int i=0; ilineNumbers.size(); ++i) { - if (i * lineNumberCacheInterval > size) { - break; - } else if (c->lineNumbers.at(i) == 0) { - // nothing in this area - continue; - } else if ((i + 1) * lineNumberCacheInterval > size) { - ret += ::count(chunkData(c, chunkPos), i * lineNumberCacheInterval, - size - i * lineNumberCacheInterval, QChar('\n')); - // partly - break; - } else { - ret += c->lineNumbers.at(i); - } - } - } -#endif - return ret; -} - -void TextDocumentPrivate::swapOutChunk(Chunk *c) -{ - if (!c->swap.isEmpty()) - return; - Q_ASSERT(!c->data.isEmpty()); - c->from = 0; - c->length = c->data.size(); - c->swap = q->swapFileName(c); - QFile file(c->swap); - if (!file.open(QIODevice::WriteOnly)) { - qWarning("TextDocumentPrivate::chunkData() Can't open file for writing '%s'", qPrintable(c->swap)); - c->swap.clear(); - return; - } - QTextStream ts(&file); - if (textCodec) - ts.setCodec(textCodec); - ts << c->data; - c->data.clear(); -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - // ### do I want to do this? - if (cachedChunk == c) { - cachedChunk = 0; - cachedChunkPos = -1; - cachedChunkData.clear(); - } -#endif -} - -static inline bool match(int pos, int left, int size) -{ - return pos >= left && pos < left + size; -} - -static inline bool match(int pos, int size, const TextPagerSection *section, TextPagerSection::TextSectionOptions flags) -{ - const int sectionPos = section->position(); - const int sectionSize = section->size(); - - if (::match(sectionPos, pos, size) && ::match(sectionPos + sectionSize - 1, pos, size)) { - return true; - } else if (flags & TextPagerSection::IncludePartial) { - const int boundaries[] = { pos, pos + size - 1 }; - for (int i=0; i<2; ++i) { - if (::match(boundaries[i], sectionPos, sectionSize)) - return true; - } - } - return false; -} - -static inline void filter(QList §ions, const TextPagerEdit *filter) -{ - if (filter) { - for (int i=sections.size() - 1; i>=0; --i) { - if (!::matchSection(sections.at(i), filter)) - sections.removeAt(i); - } - } -} - -QList TextDocumentPrivate::getSections(int pos, int size, TextPagerSection::TextSectionOptions flags, const TextPagerEdit *filter) const -{ - if (size == -1) - size = documentSize - pos; - QList ret; - if (pos == 0 && size == documentSize) { - ret = sections; - ::filter(ret, filter); - return ret; - } - // binary search. TextPagerSections are sorted in order of position - if (sections.isEmpty()) { - return ret; - } - - const TextPagerSection tmp(pos, size, static_cast(0), QTextCharFormat(), QVariant()); - QList::const_iterator it = qLowerBound(sections.begin(), sections.end(), &tmp, compareTextSection); - if (flags & TextPagerSection::IncludePartial && it != sections.begin()) { - QList::const_iterator prev = it; - do { - if (::match(pos, size, *--prev, flags)) - ret.append(*prev); - } while (prev != sections.begin()); - } - while (it != sections.end()) { - if (::match(pos, size, *it, flags)) { - ret.append(*it); - } else { - break; - } - ++it; - } - ::filter(ret, filter); - return ret; -} - -void TextDocumentPrivate::textEditDestroyed(TextPagerEdit *edit) -{ - QMutableListIterator i(sections); - while (i.hasNext()) { - TextPagerSection *section = i.next(); - if (section->textEdit() == edit) { - section->d.document = 0; - // Make sure we also remove it from the list of sections so it - // isn't deleted in the TextDocument destructor too. - i.remove(); - delete section; - } - } -} - -void TextPagerDocument::lockForRead() -{ - Q_ASSERT(d->readWriteLock); - d->readWriteLock->lockForRead(); -} - -void TextPagerDocument::lockForWrite() -{ - Q_ASSERT(d->readWriteLock); - d->readWriteLock->lockForWrite(); -} - -bool TextPagerDocument::tryLockForRead() -{ - Q_ASSERT(d->readWriteLock); - return d->readWriteLock->tryLockForRead(); -} - -bool TextPagerDocument::tryLockForWrite() -{ - Q_ASSERT(d->readWriteLock); - return d->readWriteLock->tryLockForWrite(); -} - -void TextPagerDocument::unlock() -{ - Q_ASSERT(d->readWriteLock); - d->readWriteLock->unlock(); -} - diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERDOCUMENT_HPP__ -#define TEXTPAGERDOCUMENT_HPP__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "TextPagerCursor.hpp" -#include "TextPagerSection.hpp" - -class Chunk; -class TextDocumentPrivate; -class TextPagerDocument : public QObject -{ - Q_OBJECT - Q_PROPERTY(int documentSize READ documentSize) - Q_PROPERTY(int chunkCount READ chunkCount) - Q_PROPERTY(int instantiatedChunkCount READ instantiatedChunkCount) - Q_PROPERTY(int swappedChunkCount READ swappedChunkCount) - Q_PROPERTY(int chunkSize READ chunkSize WRITE setChunkSize) - Q_ENUMS(DeviceMode) - Q_FLAGS(Options) - Q_FLAGS(FindMode) - -public: - TextPagerDocument(QObject *parent = 0); - ~TextPagerDocument(); - - enum DeviceMode { - Sparse, - LoadAll - }; - - enum Option { - NoOptions = 0x0000, - SwapChunks = 0x0001, - KeepTemporaryFiles = 0x0002, - ConvertCarriageReturns = 0x0004, // incompatible with Sparse and must be set before loading - AutoDetectCarriageReturns = 0x0010, - NoImplicitLoadAll = 0x0020, - Locking = 0x0040, - DefaultOptions = AutoDetectCarriageReturns - }; - Q_DECLARE_FLAGS(Options, Option); - - Options options() const; - void setOptions(Options opt); - inline void setOption(Option opt, bool on = true) { setOptions(on ? (options() | opt) : (options() &= ~opt)); } - - inline bool load(QIODevice *device, DeviceMode mode, const QByteArray &codecName) - { return load(device, mode, QTextCodec::codecForName(codecName)); } - inline bool load(const QString &fileName, DeviceMode mode, const QByteArray &codecName) - { return load(fileName, mode, QTextCodec::codecForName(codecName)); } - bool load(QIODevice *device, DeviceMode mode = Sparse, QTextCodec *codec = 0); - bool load(const QString &fileName, DeviceMode mode = Sparse, QTextCodec *codec = 0); - - void clear(); - DeviceMode deviceMode() const; - - QTextCodec *textCodec() const; - - void setText(const QString &text); - QString read(int pos, int size) const; - QStringRef readRef(int pos, int size) const; - QChar readCharacter(int index) const; - - int documentSize() const; - int chunkCount() const; - int instantiatedChunkCount() const; - int swappedChunkCount() const; - - void lockForRead(); - void lockForWrite(); - bool tryLockForRead(); - bool tryLockForWrite(); - void unlock(); - - enum FindModeFlag { - FindNone = 0x00000, - FindBackward = 0x00001, - FindCaseSensitively = 0x00002, - FindWholeWords = 0x00004, - FindAllowInterrupt = 0x00008, - FindWrap = 0x00010, - FindAll = 0x00020 - }; - Q_DECLARE_FLAGS(FindMode, FindModeFlag); - - int chunkSize() const; - void setChunkSize(int pos); - - QIODevice *device() const; - - TextPagerCursor find(const QRegExp &rx, const TextPagerCursor &cursor, FindMode flags = 0,int limit = -1) const; - TextPagerCursor find(const QString &ba, const TextPagerCursor &cursor, FindMode flags = 0, int limit = -1) const; - TextPagerCursor find(const QChar &ch, const TextPagerCursor &cursor, FindMode flags = 0) const; - TextPagerCursor findLine(int lineNum, const TextPagerCursor &cursor) const; - - inline TextPagerCursor find(const QRegExp &rx, int pos = 0, FindMode flags = 0) const - { return find(rx, TextPagerCursor(this, pos), flags); } - inline TextPagerCursor find(const QString &ba, int pos = 0, FindMode flags = 0) const - { return find(ba, TextPagerCursor(this, pos), flags); } - inline TextPagerCursor find(const QChar &ch, int pos = 0, FindMode flags = 0) const - { return find(ch, TextPagerCursor(this, pos), flags); } - - - QList sections(int from = 0, int size = -1, TextPagerSection::TextSectionOptions opt = 0) const; - inline TextPagerSection *sectionAt(int pos) const { return sections(pos, 1, TextPagerSection::IncludePartial).value(0); } - TextPagerSection *insertTextSection(int pos, int size, const QTextCharFormat &format = QTextCharFormat(), - const QVariant &data = QVariant()); - void insertTextSection(TextPagerSection *section); - void takeTextSection(TextPagerSection *section); - int currentMemoryUsage() const; - - bool isModified() const; - - int lineNumber(int position) const; - int columnNumber(int position) const; - int lineNumber(const TextPagerCursor &cursor) const; - int columnNumber(const TextPagerCursor &cursor) const; - virtual bool isWordCharacter(const QChar &ch, int index) const; - -public Q_SLOTS: - bool abortSave(); - bool abortFind() const; - -Q_SIGNALS: - void entryFound(const TextPagerCursor &cursor) const; - void textChanged(); - void sectionAdded(TextPagerSection *section); - void sectionRemoved(TextPagerSection *removed); - void charactersAdded(int from, int count); - void charactersRemoved(int from, int count); - void saveProgress(qreal progress); - void findProgress(qreal progress, int position) const; - void documentSizeChanged(int size); - void modificationChanged(bool modified); - -protected: - virtual QString swapFileName(Chunk *chunk); - -private: - TextDocumentPrivate *d; - friend class TextPagerEdit; - friend class TextPagerCursor; - friend class TextDocumentPrivate; - friend class TextPagerLayout; - friend class TextPagerSection; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerDocument::FindMode); -Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerDocument::Options); - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument_p.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument_p.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerDocument_p.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerDocument_p.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,703 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERDOCUMENT_P_HPP__ -#define TEXTPAGERDOCUMENT_P_HPP__ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifndef ASSUME -#ifdef FATAL_ASSUMES -#define ASSUME(cond) Q_ASSERT(cond) -#elif defined Q_OS_SOLARIS -#define ASSUME(cond) if (!(cond)) qWarning("Failed assumption %s:%d %s", __FILE__, __LINE__, #cond); -#else -#define ASSUME(cond) if (!(cond)) qWarning("Failed assumption %s:%d (%s) %s", __FILE__, __LINE__, __FUNCTION__, #cond); -#endif -#endif - -#define Q_COMPARE_ASSERT(left, right) if (left != right) { qWarning() << left << right; Q_ASSERT(left == right); } - -#include "TextPagerDocument.hpp" -#ifdef NO_TEXTDOCUMENT_CACHE -#define NO_TEXTDOCUMENT_CHUNK_CACHE -#define NO_TEXTDOCUMENT_READ_CACHE -#endif - -#if defined TEXTDOCUMENT_LINENUMBER_CACHE && !defined TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL -#define TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL 100 -#endif - -#ifdef QT_DEBUG -#define _UI_TEXTPAGER_ITERATOR_DEBUG -#endif - -static inline bool matchSection(const TextPagerSection *section, const TextPagerEdit *textEdit) -{ - if (!textEdit) { - return true; - } else if (!section->textEdit()) { - return true; - } else { - return textEdit == section->textEdit(); - } -} - - -struct Chunk { - Chunk() : previous(0), next(0), from(-1), length(0), firstLineIndex(-1) -#ifndef TEXTDOCUMENT_LINENUMBER_CACHE - , lines(-1) -#endif - {} - - mutable QString data; - Chunk *previous, *next; - int size() const { return data.isEmpty() ? length : data.size(); } -#ifdef QT_DEBUG - int pos() const { int p = 0; Chunk *c = previous; while (c) { p += c->size(); c = c->previous; }; return p; } -#endif - mutable int from, length; // Not used when all is loaded - mutable int firstLineIndex; -#ifdef TEXTDOCUMENT_LINENUMBER_CACHE - mutable QVector lineNumbers; - // format is how many endlines in the area from (n * - // TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL) to - // ((n + 1) * TEXTDOCUMENT_LINENUMBER_CACHE_INTERVAL) -#else - mutable int lines; -#endif - QString swap; -}; - - -// should really use this stuff for all of this stuff - -static inline QPair intersection(int index1, int size1, int index2, int size2) -{ - QPair ret; - ret.first = qMax(index1, index2); - const int right = qMin(index1 + size1, index2 + size2); - ret.second = right - ret.first; - if (ret.second <= 0) - return qMakePair(-1, 0); - return ret; -} - -static inline bool compareTextSection(const TextPagerSection *left, const TextPagerSection *right) -{ - // don't make this compare document. Look at ::sections() - return left->position() < right->position(); -} - -class TextDocumentIterator; -struct DocumentCommand { - enum Type { - None, - Inserted, - Removed - }; - - DocumentCommand(Type t, int pos = -1, const QString &string = QString()) - : type(t), position(pos), text(string), joinStatus(NoJoin) - {} - - const Type type; - int position; - QString text; - - enum JoinStatus { - NoJoin, - Forward, - Backward - } joinStatus; -}; - -struct TextPagerSection; -struct TextCursorSharedPrivate; -struct TextDocumentPrivate : public QObject -{ - Q_OBJECT -public: - TextDocumentPrivate(TextPagerDocument *doc) - : q(doc), first(0), last(0), -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - cachedChunk(0), cachedChunkPos(-1), -#endif -#ifndef NO_TEXTDOCUMENT_READ_CACHE - cachePos(-1), -#endif - documentSize(0), - saveState(NotSaving), findState(NotFinding), ownDevice(false), modified(false), - deviceMode(TextPagerDocument::Sparse), chunkSize(64*1024), //chunkSize(1024*64), //chunkSize(16384), - //undoRedoStackCurrent(0), modifiedIndex(-1), undoRedoEnabled(true), ignoreUndoRedo(false), - //collapseInsertUndo(false), - hasChunksWithLineNumbers(false), textCodec(0), options(TextPagerDocument::DefaultOptions), - readWriteLock(0), cursorCommand(false) - { - first = last = new Chunk; - } - - TextPagerDocument *q; - QSet textCursors; - mutable Chunk *first, *last; - -#ifndef NO_TEXTDOCUMENT_CHUNK_CACHE - mutable Chunk *cachedChunk; - mutable int cachedChunkPos; - mutable QString cachedChunkData; // last uninstantiated chunk's read from file -#endif -#ifndef NO_TEXTDOCUMENT_READ_CACHE - mutable int cachePos; - mutable QString cache; // results of last read(). Could span chunks -#endif - - int documentSize; - enum SaveState { NotSaving, Saving, AbortSave } saveState; - enum FindState { NotFinding, Finding, AbortFind } mutable findState; - QList sections; - QPointer device; - bool ownDevice, modified; - TextPagerDocument::DeviceMode deviceMode; - int chunkSize; - -#if 0 - QList undoRedoStack; - int undoRedoStackCurrent, modifiedIndex; - bool undoRedoEnabled, ignoreUndoRedo, collapseInsertUndo; -#endif - - bool hasChunksWithLineNumbers; - QTextCodec *textCodec; - TextPagerDocument::Options options; - QReadWriteLock *readWriteLock; - bool cursorCommand; - -#ifdef QT_DEBUG - mutable QSet iterators; -#endif - -#if 0 - void joinLastTwoCommands(); -#endif - - void removeChunk(Chunk *c); - QString chunkData(const Chunk *chunk, int pos) const; - int chunkIndex(const Chunk *c) const; - - // evil API. pos < 0 means don't cache - - void updateChunkLineNumbers(Chunk *c, int pos) const; - int countNewLines(Chunk *c, int chunkPos, int index) const; - - void instantiateChunk(Chunk *chunk); - Chunk *chunkAt(int pos, int *offset) const; - -#if 0 - void clearRedo(); - void undoRedo(bool undo); -#endif - - QString wordAt(int position, int *start = 0) const; - QString paragraphAt(int position, int *start = 0) const; - - uint wordBoundariesAt(int pos) const; - - friend class TextPagerDocument; - void swapOutChunk(Chunk *c); - QList getSections(int from, int size, TextPagerSection::TextSectionOptions opt, const TextPagerEdit *filter) const; - inline TextPagerSection *sectionAt(int pos, const TextPagerEdit *filter) const { return getSections(pos, 1, TextPagerSection::IncludePartial, filter).value(0); } - void textEditDestroyed(TextPagerEdit *edit); -Q_SIGNALS: - void sectionFormatChanged(TextPagerSection *section); - void sectionCursorChanged(TextPagerSection *section); - -#if 0 - void undoRedoCommandInserted(DocumentCommand *cmd); - void undoRedoCommandRemoved(DocumentCommand *cmd); - void undoRedoCommandTriggered(DocumentCommand *cmd, bool undo); - void undoRedoCommandFinished(DocumentCommand *cmd); -#endif - -private: - friend class TextPagerSection; -}; - -// should not be kept as a member. Invalidated on inserts/removes -class TextDocumentIterator -{ -public: - TextDocumentIterator(const TextDocumentPrivate *d, int p) - : doc(d), pos(p), min(0), max(-1), convert(false), newline('\n') - { - Q_ASSERT(doc); - - end_=end(); - -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - chunk = doc->chunkAt(p, &offset); - Q_ASSERT(chunk); - const int chunkPos = p - offset; - chunkData = doc->chunkData(chunk, chunkPos); - //chunkLines = chunkData.split('\n'); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); -#ifdef QT_DEBUG - if (p != d->documentSize) { - if (doc->q->readCharacter(p) != chunkData.at(offset)) { - qDebug() << "got" << chunkData.at(offset) << "at" << offset << "in" << chunk << "of size" << chunkData.size() - << "expected" << doc->q->readCharacter(p) << "at document pos" << pos; - } - Q_ASSERT(chunkData.at(offset) == doc->q->readCharacter(p)); - } else { - Q_ASSERT(chunkData.size() == offset); - } -#endif -#endif - -#ifdef QT_DEBUG - doc->iterators.insert(this); -#endif - } -#ifdef QT_DEBUG - ~TextDocumentIterator() - { - Q_ASSERT(doc->iterators.remove(this)); - } -#endif - - int end() const - { - return max != -1 ? max : doc->documentSize; - } - - void setMinBoundary(int bound) - { - min = bound; - Q_ASSERT(pos >= min); - } - - void setMaxBoundary(int bound) - { - max = bound; - Q_ASSERT(pos <= max); - - //We suppose that the document size does not change - end_=end(); - } - - - inline bool hasNext() const - { - return pos < end(); - } - - inline bool hasPrevious() const - { - return pos > min; - } - - inline int position() const - { - return pos; - } - - inline QChar current() const - { - Q_ASSERT(doc); -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(chunk); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); - if (pos == end()) - return QChar(); - -//Calling readCharacter is very expensive during find!!! So we try not to call it!!! -#if 0 -#ifdef QT_DEBUG - if (doc->q->readCharacter(pos) != chunkData.at(offset)) { - qDebug() << "got" << chunkData.at(offset) << "at" << offset << "in" << chunk << "of size" << chunkData.size() - << "expected" << doc->q->readCharacter(pos) << "at document pos" << pos; - } -#endif -#endif -#if 0 - ASSUME(doc->q->readCharacter(pos) == chunkData.at(offset)); -#endif - return convert ? chunkData.at(offset).toLower() : chunkData.at(offset); - //return convert ? chunkData[offset].toLower() : chunkData[offset]; -#else - return convert ? doc->q->readCharacter(pos).toLower() : doc->q->readCharacter(pos); -#endif - } - - inline QChar next() - { - Q_ASSERT(doc); - ++pos; -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(chunk); - if (++offset >= chunkData.size() && chunk->next) { // special case for offset == chunkData.size() and !chunk->next - offset = 0; - chunk = chunk->next; - Q_ASSERT(chunk); - chunkData = doc->chunkData(chunk, pos); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); - } -#endif - return current(); - } - - inline QChar previous() - { - Q_ASSERT(doc); - Q_ASSERT(hasPrevious()); - --pos; - Q_ASSERT(pos >= min); - Q_ASSERT(pos <= end()); -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(chunk); - if (--offset < 0) { - chunk = chunk->previous; - Q_ASSERT(chunk); - chunkData = doc->chunkData(chunk, pos - chunk->size() + 1); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); - offset = chunkData.size() - 1; - } -#endif - return current(); - } - - enum Direction { None = 0x0, Left = 0x1, Right = 0x2, Both = Left|Right }; - inline QChar nextPrev(Direction dir, bool &ok) - { - if (dir == Left) { - if (!hasPrevious()) { - ok = false; - return QChar(); - } - ok = true; - return previous(); - } else { - if (!hasNext()) { - ok = false; - return QChar(); - } - ok = true; - return next(); - } - } - -// Gets the previous line preceding the current iterator position. It is only -// used for TextPagerDocument::find and was added to speed up the original find -// algorithm. it is only implemented for cached textdocument iterators! - - inline unsigned char prevLine(QString& str) - { -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(doc); - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << "prevLine --->" << pos << offset; //<< chunkData; -#endif - - //If we are at the start - if(pos <= min) - return 0; - - //Get the QString in the chunk as a const pointer. It is faster to iterate through it than - //calling QString::at() - const QChar *data = chunkData.constData(); - data+=offset; - - Q_ASSERT(*data == chunkData.at(offset)); - - //The current character is probably a newline (the - //result of the previous call) so we need to take a step back - if(*data == newline) - { - --pos; - --offset; - - //See if we need the previous chunk - if(offset < 0) { - //The previous chunk must exist - bool b=loadPrevChunk(); - Q_ASSERT( b == true); - data = chunkData.constData(); - data+=offset; - } else { - --data; - } - - if(pos == min) - return 0; - } - - //Mark the line end position within the chunk - int to=offset; - - //We will go backwards until we find a newline - while(*data != newline) - { - //Q_ASSERT(*data == chunkData.at(offset)); -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << pos << offset << chunkData.at(offset) << to; -#endif - if(pos <= min) { - if(to != offset) { - if(str.size() == 0) { - str=chunkData.mid(offset,to-offset+1); - } else { - str.prepend(chunkData.mid(offset,to-offset+1)); - } - } - return 0; - } - - --pos; - - //If we need the previous chunk - if(--offset < 0) { - - //Copy the line portion from this chunk to the result - if(str.size() == 0) - str=chunkData.mid(0,to); - else - str.prepend(chunkData.mid(0,to)); - - //Get previous chunk - bool b=loadPrevChunk(); - Q_ASSERT(b==true); - - //Initialise the search positions - data = chunkData.constData(); - data+=offset; - to=offset; - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << "change" << pos << offset << *data << chunkData.at(offset); -#endif - } else { - --data; - } - } - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << pos << offset << to; -#endif - - //offset is either a newline charter or 0 (the start of the document) - - if(to != offset) { - if(str.size() == 0) { - str=chunkData.mid(offset,to-offset+1); - } else { - str.prepend(chunkData.mid(offset,to-offset+1)); - } - } -#endif - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << "line:" << str; -#endif - - return 1; - } - - // Gets the next line following- the current iterator position. It is only - // used for TextPagerDocument::find and was added to speed up the original find - // algorithm. it is only implemented for cached textdocument iterators! - - inline unsigned char nextLine(QString& str) - { -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(doc); - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << "nextLine --->" << pos << offset << chunkData.size() << chunkData.at(offset); -#endif - int posEnd=end(); - if(pos >= posEnd) - return 0; - - //Get the QString in the chunk as a const pointer. It is faster to iterate through it than - //calling QString::at() - const QChar *data = chunkData.constData(); - data+=offset; - - Q_ASSERT(*data == chunkData.at(offset)); - - //The current character is probably a newline (the - //result of the previous call) so we need to take a step forward - if(*data == newline) - { - ++pos; - ++offset; - - //See if we need the next chunk - if(offset >= chunkData.size()) { - //Has next chunk - if(loadNextChunk()) { - data = chunkData.constData(); - Q_ASSERT(offset == 0); - } else { - pos--; - offset--; - return 0; - } - - } else { - ++data; - } - - if(pos >= posEnd) - return 0; - - } - - //Mark the line start position within the chunk - int from=offset; - - //We will go forward until we find a newline - while(*data != newline) - { -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << pos << offset << chunkData.at(offset); -#endif - if(pos >= posEnd) - { - if(str.size() == 0) - str=chunkData.mid(from,offset-from+1); - else - str.append(chunkData.mid(from,offset-from+1)); - - return 0; - } - - ++pos; - - //If we need the next chunk - if(++offset >= chunkData.size()) { - - //Copy the line portion from this chunk to the result - if(str.size() == 0) - str=chunkData.mid(from,offset-from); - else - str.append(chunkData.mid(from,offset-from)); - - //Get next chunk - if(loadNextChunk()) { - //Initialise the search positions - data = chunkData.constData(); - from=0; - } else { - pos--; - offset--; - return 0; - } - } - else { - ++data; - } - } - - if(from != offset) { - if(str.size() == 0) - { - str=chunkData.mid(from,offset-from+1); - } - else - str.append(chunkData.mid(from,offset-from+1)); - } - -#endif - -#ifdef _UI_TEXTPAGER_ITERATOR_DEBUG - //qDebug() << "line:" << str; -#endif - - return 1; - } - - - inline bool loadPrevChunk() - { -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(chunk); - if (chunk->previous) { - chunk = chunk->previous; - Q_ASSERT(chunk); - chunkData = doc->chunkData(chunk, pos - chunk->size() + 1); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); - offset = chunkData.size() - 1; - return true; - } -#endif - return false; - } - - inline bool loadNextChunk() - { -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - Q_ASSERT(chunk); - if (chunk->next) { - chunk = chunk->next; - Q_ASSERT(chunk); - chunkData = doc->chunkData(chunk, pos); - Q_COMPARE_ASSERT(chunkData.size(), chunk->size()); - offset = 0; - return true; - } -#endif - return false; - } - - - inline bool convertToLowerCase() const - { - return convert; - } - - inline void setConvertToLowerCase(bool on) - { - convert = on; - } - -private: - const TextDocumentPrivate *doc; - int pos; - int min, max; - int end_; // to speed things up -#ifndef NO_TEXTDOCUMENTITERATOR_CACHE - int offset; - QString chunkData; - QStringList chunkLines; - Chunk *chunk; -#endif - bool convert; - const QChar newline; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1938 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include "TextPagerEdit.hpp" -#include "TextPagerEdit_p.hpp" -#include "TextPagerCursor_p.hpp" -#include "TextPagerDocument_p.hpp" -#include "TextPagerSearchHighlighter.hpp" -#include "UiLog.hpp" -#include "VConfig.hpp" - -#include - -//#define _UI_TEXTPAGER_DEBUG -//#define DEBUG_TEXTPAGER_LASTPAGESIZE -//#define DEBUG_TEXTPAGER - -#ifdef DEBUG_TEXTPAGER -bool doLog = false; -QString logFileName; -#endif - -/*! - Constructs an empty TextPagerEdit with parent \a parent. -*/ - -TextPagerEdit::TextPagerEdit(QWidget *parent) : - QAbstractScrollArea(parent), - d(new TextEditPrivate(this)), - useSearchHighlight_(false), - showLineNum_(false), - lineNumArea_(0), - fontProp_(NULL) -{ - viewport()->setCursor(Qt::IBeamCursor); - - setProperty("pager","1"); - -#ifdef DEBUG_TEXTPAGER - if (logFileName.isEmpty()) - logFileName = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmsszzz.log"); -#endif - - connect(verticalScrollBar(), SIGNAL(valueChanged(int)), d, SLOT(onScrollBarValueChanged(int))); - connect(verticalScrollBar(), SIGNAL(actionTriggered(int)), d, SLOT(onScrollBarActionTriggered(int))); - connect(verticalScrollBar(), SIGNAL(sliderReleased()), d, SLOT(updateScrollBar())); - - setDocument(new TextPagerDocument(this)); - setViewportMargins(0, 0, 0, 0); - struct { - QString text; - const char *member; - QKeySequence::StandardKey key; - } shortcuts[] = { - { tr("Copy"), SLOT(copy()), QKeySequence::Copy }, - { tr("Select All"), SLOT(selectAll()), QKeySequence::SelectAll }, - { QString(), 0, QKeySequence::UnknownKey } }; - for (int i=0; shortcuts[i].member; ++i) { - d->actions[i] = new QAction(shortcuts[i].text, this); - d->actions[i]->setShortcutContext(Qt::WidgetShortcut); - d->actions[i]->setShortcut(QKeySequence(shortcuts[i].key)); - d->actions[i]->setShortcutContext(Qt::WidgetShortcut); - connect(d->actions[i], SIGNAL(triggered(bool)), this, shortcuts[i].member); - addAction(d->actions[i]); - } - //connect(this, SIGNAL(undoAvailableChanged(bool)), d->actions[UndoAction], SLOT(setEnabled(bool))); - //connect(this, SIGNAL(redoAvailableChanged(bool)), d->actions[RedoAction], SLOT(setEnabled(bool))); - //d->actions[UndoAction]->setEnabled(false); - //d->actions[RedoAction]->setEnabled(false); - setContextMenuPolicy(Qt::ActionsContextMenu); - //setCursorVisible(true); // starts blinking - connect(this, SIGNAL(selectionChanged()), d, SLOT(onSelectionChanged())); - - - setReadOnly(true); - - searchHighlight_=new TextPagerSearchHighlighter(this); -} - - -/*! - returns the current cursor position -*/ - -int TextPagerEdit::cursorPosition() const -{ - return d->textCursor.position(); -} - -/*! - ensures that \a cursor is visible with \a linesMargin lines of - margin on each side (if possible) -*/ - -void TextPagerEdit::ensureCursorVisible(const TextPagerCursor &cursor, int linesMargin) -{ - linesMargin = qMin(d->visibleLines, linesMargin); // ### could this be a problem down at the bottom of the document? - Q_ASSERT(cursor.document() == document()); - TextPagerCursor above = cursor; - for (int i=0; iviewportPosition) { - d->updateViewportPosition(above.position(), TextPagerLayout::Backward); - return; - } - - TextPagerCursor below = cursor; - for (int i=0; i d->lastVisibleCharacter) { - for (int i=0; ivisibleLines; ++i) { - below.movePosition(TextPagerCursor::Up); - d->updateViewportPosition(below.position(), TextPagerLayout::Forward); - } - } -} - -/*! - returns the QAction * for \a type. -*/ - -QAction *TextPagerEdit::action(ActionType type) const -{ - return d->actions[type]; -} - -/*! - Called when the textEdit is deleted -*/ - -TextPagerEdit::~TextPagerEdit() -{ - if (d->document) { - Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, d->syntaxHighlighters) { - if (syntaxHighlighter->parent() == this) - disconnect(syntaxHighlighter, 0, d, 0); - syntaxHighlighter->d->textEdit = 0; - syntaxHighlighter->d->textLayout = 0; - } - disconnect(d->document, 0, this, 0); - disconnect(d->document, 0, d, 0); - disconnect(d->document->d, 0, this, 0); - disconnect(d->document->d, 0, d, 0); - - if (d->document->parent() == this) { - delete d->document; - d->document = 0; - } else { - d->document->d->textEditDestroyed(this); - } - // to make sure we don't do anything drastic on shutdown - } - delete d; - - if(fontProp_) - fontProp_->removeObserver(this); -} - -/*! - returns the text edit's document. document() always returns a - valid TextPagerDocument * - \sa setDocument -*/ - - -TextPagerDocument *TextPagerEdit::document() const -{ - return d->document; -} - -/*! - sets the text edit's document to \a doc. The previous document - will be deleted if it's parented to the text edit. - - If \a doc is 0 a default TextPagerDocument will be set. - \sa document -*/ - -void TextPagerEdit::setDocument(TextPagerDocument *doc) -{ - if (doc == d->document) - return; - - if (d->document) { - disconnect(d->document, 0, this, 0); - disconnect(d->document, 0, d, 0); - disconnect(d->document->d, 0, this, 0); - disconnect(d->document->d, 0, d, 0); - if (d->document->parent() == this) - delete d->document; - } - if (!doc) - doc = new TextPagerDocument(this); - - d->sections.clear(); - d->buffer.clear(); - d->sectionsDirty = true; - d->document = doc; - d->sectionPressed = 0; - d->layoutDirty = true; - qDeleteAll(d->unusedTextLayouts); - d->unusedTextLayouts.clear(); - qDeleteAll(d->textLayouts); - d->textLayouts.clear(); - viewport()->setCursor(Qt::IBeamCursor); - viewport()->setMouseTracking(true); - d->sectionCount = 0; - - d->textCursor = TextPagerCursor(doc); - d->textCursor.textEdit = this; - - /*connect(d->document->d, SIGNAL(undoRedoCommandInserted(DocumentCommand *)), - d, SLOT(onDocumentCommandInserted(DocumentCommand *))); - connect(d->document, SIGNAL(sectionAdded(TextPagerSection *)), - d, SLOT(onTextSectionAdded(TextPagerSection *))); - connect(d->document, SIGNAL(sectionRemoved(TextPagerSection *)), - d, SLOT(onTextSectionRemoved(TextPagerSection *))); - connect(d->document->d, SIGNAL(undoRedoCommandRemoved(DocumentCommand *)), - d, SLOT(onDocumentCommandRemoved(DocumentCommand *))); - connect(d->document->d, SIGNAL(undoRedoCommandTriggered(DocumentCommand *, bool)), - d, SLOT(onDocumentCommandTriggered(DocumentCommand *, bool)));*/ - - connect(d->document, SIGNAL(charactersAdded(int, int)), - d, SLOT(onCharactersAddedOrRemoved(int, int))); - connect(d->document, SIGNAL(charactersRemoved(int, int)), - d, SLOT(onCharactersAddedOrRemoved(int, int))); - - /*connect(d->document, SIGNAL(textChanged()), this, SIGNAL(textChanged())); - connect(d->document, SIGNAL(undoAvailableChanged(bool)), - this, SIGNAL(undoAvailableChanged(bool))); - connect(d->document, SIGNAL(redoAvailableChanged(bool)), - this, SIGNAL(redoAvailableChanged(bool)));*/ - - connect(d->document, SIGNAL(documentSizeChanged(int)), d, SLOT(onDocumentSizeChanged(int))); - connect(d->document, SIGNAL(destroyed(QObject*)), d, SLOT(onDocumentDestroyed())); - connect(d->document->d, SIGNAL(sectionFormatChanged(TextPagerSection *)), - d, SLOT(onTextSectionFormatChanged(TextPagerSection *))); - connect(d->document->d, SIGNAL(sectionCursorChanged(TextPagerSection *)), - d, SLOT(onTextSectionCursorChanged(TextPagerSection *))); - - d->onDocumentSizeChanged(d->document->documentSize()); - - viewport()->update(); -} - -/*! - returns the textCursor's width (in pixels) - \sa setCursorWidth -*/ - -int TextPagerEdit::cursorWidth() const -{ - return d->cursorWidth; -} - -/*! - sets the cursorWidth of the text edit to \a cw pixels. - \a cw must be a valid integer larger than 0. - - \sa setCursorVisible -*/ - -void TextPagerEdit::setCursorWidth(int cw) -{ - Q_ASSERT(d->cursorWidth > 0); - d->cursorWidth = cw; - viewport()->update(); -} - -/*! - Loads the contenst of \a dev into the text edit's document. - Equivalent to calling document()->load(\a dev, \a mode); - - \sa setCursorVisible -*/ - -#if 0 -bool TextPagerEdit::load(QIODevice *dev, TextPagerDocument::DeviceMode mode, QTextCodec *codec) -{ -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile f(logFileName); - f.open(QIODevice::WriteOnly); - QDataStream ds(&f); - if (QFile *ff = qobject_cast(dev)) { - ds << ff->fileName(); - } else { - ds << QString::fromLatin1(dev->metaObject()->className()); - } - } -#endif - //we have to check to font here because the initial setting in setFontProperty doe not have any effect - updateFont(); - return d->document->load(dev, mode, codec); -} -#endif - -bool TextPagerEdit::load(const QString &file, TextPagerDocument::DeviceMode mode, QTextCodec *codec) -{ -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile f(logFileName); - f.open(QIODevice::WriteOnly); - QDataStream ds(&f); - ds << file; - } -#endif - - UiLog().dbg() << "TextPagerEdit::load fileName" << file; - //we have to check to font here because the initial setting in setFontProperty doe not have any effect - updateFont(); - bool ret=d->document->load(file, mode, codec); - UiLog().dbg() << " cursor: " << textCursor().position(); - return ret; -} - -void TextPagerEdit::setText(const QString &txt) -{ - //we have to check to font here because the initial setting in setFontProperty doe not have any effect - updateFont(); - d->document->setText(txt); -} - -enum SelectionAddStatus { - Invalid, - Before, - After, - Success -}; - - - - - -static inline SelectionAddStatus addSelection(int layoutStart, int layoutLength, - const TextPagerCursor &cursor, QTextLayout::FormatRange *format) -{ - Q_ASSERT(format); - if (!cursor.hasSelection()) - return Invalid; - if (cursor.selectionEnd() < layoutStart) - return Before; - if (cursor.selectionStart() > layoutStart + layoutLength) - return After; - - format->start = qMax(0, cursor.selectionStart() - layoutStart); - format->length = qMin(layoutLength - format->start, - cursor.selectionEnd() - layoutStart - format->start); - return Success; -} - -void TextPagerEdit::paintEvent(QPaintEvent *e) -{ - d->updateScrollBarPosition(); - d->relayout(); - if (d->updateScrollBarPageStepPending) { - d->updateScrollBarPageStepPending = false; - d->updateScrollBarPageStep(); - } - - QPainter p(viewport()); - - const QRect er = e->rect(); - p.translate(-horizontalScrollBar()->value(), 0); - p.setFont(font()); - QVector selections; - selections.reserve(d->extraSelections.size() + 1); - int textLayoutOffset = d->viewportPosition; - - const QTextLayout *cursorLayout = d->cursorVisible ? d->layoutForPosition(d->textCursor.position()) : 0; - int extraSelectionIndex = 0; - QTextLayout::FormatRange selectionRange; - selectionRange.start = -1; - Q_FOREACH(QTextLayout *l, d->textLayouts) { - const int textSize = l->text().size(); - const QRect r = l->boundingRect().toRect(); - if (r.intersects(er)) { - const QBrush background = d->blockFormats.value(l).background(); - if (background.style() != Qt::NoBrush) { - p.fillRect(r, background); - } - if (::addSelection(textLayoutOffset, textSize, d->textCursor, &selectionRange) == Success) { - selectionRange.format.setBackground(palette().highlight()); - selectionRange.format.setForeground(palette().highlightedText()); - } - int lowestIncompleteSelection = -1; - while (extraSelectionIndex < d->extraSelections.size()) { - QTextLayout::FormatRange range; - const SelectionAddStatus s = ::addSelection(textLayoutOffset, textSize, - d->extraSelections.at(extraSelectionIndex). - cursor, &range); - if (s == Success) { - range.format = d->extraSelections.at(extraSelectionIndex).format; - selections.append(range); - - const TextPagerCursor &cursor = d->extraSelections.at(extraSelectionIndex).cursor; - int lastPos = cursor.position() + cursor.selectionSize(); - if (lastPos > textLayoutOffset+textSize && lowestIncompleteSelection < 0) { - lowestIncompleteSelection = extraSelectionIndex; - } - } else if (s == After) { - break; - } - ++extraSelectionIndex; - } - if (lowestIncompleteSelection > -1) { - extraSelectionIndex = lowestIncompleteSelection; - } - - // is this the current line? - /*if(cursorLayout == l) - { - p.fillRect(r, QColor(216,228,239)); // highlight the current line - }*/ - - - if (selectionRange.start != -1) { - // The last range in the vector has priority, that - // should probably be the real selection - selections.append(selectionRange); - selectionRange.start = -1; - } - - l->draw(&p, QPoint(0, 0), selections); - if (!selections.isEmpty()) - selections.clear(); - if (cursorLayout == l) { - cursorLayout->drawCursor(&p, QPoint(0, 0), d->textCursor.position() - textLayoutOffset, - d->cursorWidth); - } - } else if (r.top() > er.bottom()) { - break; - } - textLayoutOffset += l->text().size() + 1; - } - - - //paintLineNumberArea(e); -} - -void TextPagerEdit::scrollContentsBy(int dx, int dy) -{ - Q_UNUSED(dx); - Q_UNUSED(dy); -// viewport()->update(); - viewport()->scroll(dx, dy); // seems to jitter more -} - -int TextPagerEdit::viewportPosition() const -{ - return d->viewportPosition; -} - -void TextPagerEdit::mousePressEvent(QMouseEvent *e) -{ - d->inMouseEvent = true; - if (e->button() == Qt::LeftButton) { -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); - } -#endif - if (d->tripleClickTimer.isActive()) { - d->tripleClickTimer.stop(); - d->textCursor.movePosition(TextPagerCursor::StartOfBlock); - d->textCursor.movePosition(TextPagerCursor::EndOfBlock, TextPagerCursor::KeepAnchor); - } else { - const bool shift = e->modifiers() & Qt::ShiftModifier; - if (!shift) { - clearSelection(); - } - int pos = textPositionAt(e->pos()); - if (pos == -1) - pos = d->document->documentSize() - 1; - d->sectionPressed = d->document->d->sectionAt(pos, this); - setCursorPosition(pos, shift ? TextPagerCursor::KeepAnchor : TextPagerCursor::MoveAnchor); - } - - e->accept(); - - //The cursor changed so wee need to update the selected line number - if(lineNumArea_ && showLineNum_) - lineNumArea_->update(); - } - else { - QAbstractScrollArea::mousePressEvent(e); - } -} - -void TextPagerEdit::mouseDoubleClickEvent(QMouseEvent *e) -{ - if (e->button() == Qt::LeftButton) { -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); - } -#endif - const int pos = textPositionAt(e->pos()); - if (pos == d->textCursor.position()) { - d->tripleClickTimer.start(qApp->doubleClickInterval(), d); - if (d->document->isWordCharacter(d->textCursor.cursorCharacter(), - d->textCursor.position())) { - // ### this is not quite right - d->textCursor.movePosition(TextPagerCursor::StartOfWord); - d->textCursor.movePosition(TextPagerCursor::EndOfWord, TextPagerCursor::KeepAnchor); - return; - } - } - mousePressEvent(e); - } -} - -void TextPagerEdit::mouseMoveEvent(QMouseEvent *e) -{ - if (e->buttons() == Qt::NoButton) { - d->updateCursorPosition(e->pos()); - } else if (e->buttons() == Qt::LeftButton && d->document->documentSize()) { -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); - } -#endif - const QRect r = viewport()->rect(); - d->lastMouseMove = e->pos(); - e->accept(); - if (e->y() < r.top()) { - if (d->atBeginning()) { - d->updateHorizontalPosition(); - d->autoScrollTimer.stop(); - return; - } - } else if (e->y() > r.bottom()) { - if (d->atEnd()) { - d->updateHorizontalPosition(); - d->autoScrollTimer.stop(); - return; - } - } else { - int pos = textPositionAt(QPoint(qBound(0, d->lastMouseMove.x(), r.right()), d->lastMouseMove.y())); - if (pos == -1) - pos = d->document->documentSize(); - d->autoScrollTimer.stop(); - setCursorPosition(pos, TextPagerCursor::KeepAnchor); - - //The cursor changed so wee need to update the selected line number - if(lineNumArea_ && showLineNum_) - lineNumArea_->update(); - - return; - } - const int distance = qMax(r.top() - d->lastMouseMove.y(), d->lastMouseMove.y() - r.bottom()); - Q_ASSERT(distance != 0); - enum { MinimumTimeOut = 3 }; - int timeout = qMax(MinimumTimeOut, 100 - distance); - enum { Margin = 3 }; - if (qApp->desktop()->screenGeometry(this).bottom() - mapToGlobal(d->lastMouseMove).y() <= Margin) { - timeout = MinimumTimeOut; - } - d->autoScrollLines = 1 + ((100 - timeout) / 30); - if (d->autoScrollTimer.isActive()) { - d->pendingTimeOut = timeout; - } else { - d->pendingTimeOut = -1; - d->autoScrollTimer.start(timeout, d); - } - } else { - QAbstractScrollArea::mouseMoveEvent(e); - } -} - -void TextPagerEdit::mouseReleaseEvent(QMouseEvent *e) -{ - d->inMouseEvent = false; - if (e->button() == Qt::LeftButton) { -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->pos() << e->button() << e->buttons() << e->modifiers(); - } -#endif - d->autoScrollTimer.stop(); - d->pendingTimeOut = -1; - e->accept(); - if (d->sectionPressed && sectionAt(e->pos()) == d->sectionPressed) { - Q_EMIT sectionClicked(d->sectionPressed, e->pos()); - } - d->sectionPressed = 0; - } else { - QAbstractScrollArea::mouseReleaseEvent(e); - } -} - -void TextPagerEdit::resizeEvent(QResizeEvent *e) -{ -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->size(); - } -#endif - QAbstractScrollArea::resizeEvent(e); - if(e->oldSize().height() != e->size().height()) - d->adjustVerticalScrollBar(); - d->updateScrollBarPageStepPending = true; - d->layoutDirty = true; -} - -int TextPagerEdit::textPositionAt(const QPoint &pos) const -{ - if (!viewport()->rect().contains(pos)) - return -1; - - QPoint realPos=pos; - realPos+=QPoint(horizontalScrollBar()->value(),0); - - //Adjust the horizontal position - /* if(pos.x() > horizontalScrollBar()->value()+viewport()->rect().width()) - { - int hval=realCrect.left()-r.width()/2; - horizontalScrollBar()->setValue((hval < horizontalScrollBar()->maximum())? - hval: - horizontalScrollBar()->maximum()); - } - else if(realCrect.right() < horizontalScrollBar()->value()) - { - int hval=realCrect.left()-r.width()/2; - horizontalScrollBar()->setValue((hval > horizontalScrollBar()->minimum())? - hval: - horizontalScrollBar()->minimum()); - } -}*/ - - - - - - - return d->textPositionAt(realPos); -} - -bool TextPagerEdit::readOnly() const -{ - return d->readOnly; -} - -void TextPagerEdit::setReadOnly(bool rr) -{ - d->readOnly = rr; - - setCursorVisible(!rr); - - /* //d->actions[PasteAction]->setEnabled(!rr); - //d->actions[CutAction]->setEnabled(!rr); - //d->actions[PasteAction]->setVisible(!rr); - //d->actions[CutAction]->setVisible(!rr); - - const bool redoWasAvailable = isRedoAvailable(); - const bool undoWasAvailable = isUndoAvailable(); - - // d->actions[UndoAction]->setEnabled(!rr); - // d->actions[RedoAction]->setEnabled(!rr); - // d->actions[UndoAction]->setVisible(!rr); - // d->actions[RedoAction]->setVisible(!rr); - - - if (undoWasAvailable != isUndoAvailable()) - Q_EMIT undoAvailableChanged(!undoWasAvailable); - if (redoWasAvailable != isRedoAvailable()) - Q_EMIT redoAvailableChanged(!redoWasAvailable); - */ -} - -bool TextPagerEdit::lineBreaking() const -{ - return d->lineBreaking; -} - -void TextPagerEdit::setLineBreaking(bool lineBreaking) -{ - if (d->lineBreaking != lineBreaking) { - d->lineBreaking = lineBreaking; - d->layoutDirty = true; - viewport()->update(); - } -} - - -int TextPagerEdit::maximumSizeCopy() const -{ - return d->maximumSizeCopy; -} - -void TextPagerEdit::setMaximumSizeCopy(int max) -{ - d->maximumSizeCopy = qMax(0, max); - d->updateCopyAndCutEnabled(); -} - -QRect TextPagerEdit::cursorBlockRect(const TextPagerCursor &textCursor) const -{ - if (const QTextLayout *l = d->layoutForPosition(textCursor.position())) { - return l->boundingRect().toRect(); - } - return QRect(); -} - -QRect TextPagerEdit::cursorRect(const TextPagerCursor &textCursor) const -{ - int offset = -1; - if (d->layoutForPosition(textCursor.position(), &offset)) { - ASSUME(offset != -1); - QTextLine line = d->lineForPosition(textCursor.position()); - - //if line is empty line.cursorToX() crashes. A qt bug? - if(line.isValid()) { - qreal x = line.cursorToX(offset); - return QRect(x, line.y(), d->cursorWidth, line.height()); - } - } - return QRect(); -} - -int TextPagerEdit::lineNumber(int position) const -{ - return d->document->lineNumber(position); -} - -int TextPagerEdit::columnNumber(int position) const -{ - TextPagerCursor cursor(this, position); - return cursor.isNull() ? -1 : cursor.columnNumber(); -} - -int TextPagerEdit::lineNumber(const TextPagerCursor &cursor) const -{ - return cursor.document() == d->document - ? d->document->lineNumber(cursor.position()) : -1; -} - -int TextPagerEdit::columnNumber(const TextPagerCursor &cursor) const -{ - return cursor.document() == d->document - ? cursor.columnNumber() : -1; -} - -void TextPagerEdit::wheelEvent(QWheelEvent *e) -{ - if (e->orientation() == Qt::Vertical) { - d->scrollLines(3 * (e->delta() > 0 ? -1 : 1)); - } else { - QAbstractScrollArea::wheelEvent(e); - } - - if(e->modifiers() & Qt::ControlModifier) - { - const int delta = e->delta(); - if (delta < 0) - zoomOut(); - else if (delta > 0) - zoomIn(); - return; - } -} - -void TextPagerEdit::changeEvent(QEvent *e) -{ - if (e->type() == QEvent::FontChange) { - d->font = font(); - Q_FOREACH(QTextLayout *l, d->textLayouts) { - l->setFont(d->font); - } - Q_FOREACH(QTextLayout *l, d->unusedTextLayouts) { - l->setFont(d->font); - } - - d->adjustVerticalScrollBar(); - - if(lineNumArea_ && showLineNum_) - lineNumArea_->updateWidth(); - - d->layoutDirty = true; - viewport()->update(); - } -} - -void TextPagerEdit::keyPressEvent(QKeyEvent *e) -{ -#ifdef DEBUG_TEXTPAGER - if (doLog) { - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->key() << int(e->modifiers()) << e->text() << e->isAutoRepeat() << e->count(); - } -#endif - - Q_ASSERT(d->textCursor.textEdit == this); - if (d->readOnly) { - d->cursorMoveKeyEventReadOnly(e); - return; - } -} - -void TextPagerEdit::keyReleaseEvent(QKeyEvent *e) -{ -#ifdef DEBUG_TEXTPAGER - if (doLog) { // ### does it make any sense to replay these? Probably not - QFile file(logFileName); - file.open(QIODevice::Append); - QDataStream ds(&file); - ds << int(e->type()) << e->key() << int(e->modifiers()) << e->text() << e->isAutoRepeat() << e->count(); - } -#endif - QAbstractScrollArea::keyReleaseEvent(e); -} - -void TextPagerEdit::setCursorPosition(int pos, TextPagerCursor::MoveMode mode) -{ - d->textCursor.setPosition(pos, mode); -} - -bool TextPagerEdit::moveCursorPosition(TextPagerCursor::MoveOperation op, TextPagerCursor::MoveMode mode, int n) -{ - return d->textCursor.movePosition(op, mode, n); -} - -void TextPagerEdit::copy(QClipboard::Mode mode) -{ - if (d->textCursor.selectionSize() <= d->maximumSizeCopy) { - QApplication::clipboard()->setText(selectedText(), mode); - } -} - -bool TextPagerEdit::cursorVisible() const -{ - return d->cursorBlinkTimer.isActive(); -} - -void TextPagerEdit::setCursorVisible(bool cc) -{ - if (cc == d->cursorBlinkTimer.isActive()) - return; - - d->cursorVisible = cc; - if (cc) { - d->cursorBlinkTimer.start(QApplication::cursorFlashTime(), d); - } else { - d->cursorBlinkTimer.stop(); - } - const QRect r = cursorRect(d->textCursor) & viewport()->rect(); - if (!r.isNull()) { - viewport()->update(r); - } -} - -void TextPagerEdit::clearSelection() -{ - if (!d->textCursor.hasSelection()) - return; - - d->textCursor.clearSelection(); -} - -QString TextPagerEdit::selectedText() const -{ - return d->textCursor.selectedText(); -} - -bool TextPagerEdit::hasSelection() const -{ - return d->textCursor.hasSelection(); -} - -void TextPagerEdit::selectAll() -{ - TextPagerCursor cursor(d->document); - Q_ASSERT(cursor.position() == 0); - cursor.movePosition(TextPagerCursor::End, TextPagerCursor::KeepAnchor); - setTextCursor(cursor); - Q_EMIT selectionChanged(); -} - -QString TextPagerEdit::read(int pos, int size) const -{ - return d->document->read(pos, size); -} - -QChar TextPagerEdit::readCharacter(int index) const -{ - return d->document->readCharacter(index); -} - -void TextEditPrivate::onDocumentSizeChanged(int size) -{ - adjustVerticalScrollBar(); - - /*int s=findLastPageSize(); - - qDebug() << "lat page position" << s; - - if(s != -1) - textEdit->verticalScrollBar()->setRange(0, s); - else - textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); - - - //textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); -// qDebug() << findLastPageSize(); - maxViewportPosition = textEdit->verticalScrollBar()->maximum(); - updateScrollBarPageStepPending = true;*/ -} - - -void TextEditPrivate::adjustVerticalScrollBar() -{ - int s=findLastPageSize(); - - int size=(document != 0)?(document->documentSize()):0; - -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << "last page position" << s; -#endif - if(s == 0) { - textEdit->verticalScrollBar()->hide(); - textEdit->verticalScrollBar()->setEnabled(false); - } else { - textEdit->verticalScrollBar()->setEnabled(true); - textEdit->verticalScrollBar()->show(); - - if(s != -1) - textEdit->verticalScrollBar()->setRange(0, s); - else - textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); - } - - //textEdit->verticalScrollBar()->setRange(0, qMax(0, size)); -// qDebug() << findLastPageSize(); - maxViewportPosition = textEdit->verticalScrollBar()->maximum(); - updateScrollBarPageStepPending = true; -} - -void TextEditPrivate::updateCopyAndCutEnabled() -{ - const bool wasEnabled = actions[TextPagerEdit::CopyAction]->isEnabled(); - const bool enable = qAbs(textCursor.position() - textCursor.anchor()) <= maximumSizeCopy; - actions[TextPagerEdit::CopyAction]->setEnabled(enable); - //actions[TextPagerEdit::CutAction]->setEnabled(enable); - if (wasEnabled != enable) { - Q_EMIT textEdit->copyAvailable(enable); - } -} - -void TextPagerEdit::takeSyntaxHighlighter(SyntaxHighlighter *highlighter) -{ - Q_ASSERT(highlighter); - const bool found = d->syntaxHighlighters.removeOne(highlighter); - Q_ASSERT(found); - Q_UNUSED(found); - highlighter->d->textEdit = 0; - highlighter->d->textLayout = 0; - disconnect(highlighter, 0, d, 0); -} - -void TextPagerEdit::removeSyntaxHighlighter(SyntaxHighlighter *highlighter) -{ - takeSyntaxHighlighter(highlighter); - delete highlighter; -} - -void TextPagerEdit::addSyntaxHighlighter(SyntaxHighlighter *highlighter) -{ - Q_ASSERT(highlighter); - if (highlighter->textEdit() != this) { - if (highlighter->textEdit()) { - qWarning("A SyntaxHighlighter can only be added to 1 TextPagerEdit. If this is a " - "use case you care about (having a syntaxHighlighter added to multiple " - "text edits) I could fix it. Anders"); - return; - } - d->syntaxHighlighters.append(highlighter); - connect(highlighter, SIGNAL(destroyed(QObject*)), d, SLOT(onSyntaxHighlighterDestroyed(QObject*))); - highlighter->d->textEdit = this; - highlighter->d->textLayout = d; - d->layoutDirty = true; - viewport()->update(); - } -} - -void TextPagerEdit::clearSyntaxHighlighters() -{ - Q_FOREACH(SyntaxHighlighter *highlighter, d->syntaxHighlighters) { - if (highlighter->parent() == this) { - removeSyntaxHighlighter(highlighter); - } else { - takeSyntaxHighlighter(highlighter); - } - } - Q_ASSERT(d->syntaxHighlighters.isEmpty()); -} - - -void TextEditPrivate::onSyntaxHighlighterDestroyed(QObject *o) -{ - const bool found = syntaxHighlighters.removeOne(static_cast(o)); - Q_ASSERT(found); - Q_UNUSED(found); - layoutDirty = true; - textEdit->viewport()->update(); -} - -QList TextPagerEdit::syntaxHighlighters() const -{ - return d->syntaxHighlighters; -} - -static inline bool compareExtraSelection(const TextPagerEdit::ExtraSelection &left, const TextPagerEdit::ExtraSelection &right) -{ - return left.cursor < right.cursor; -} - -void TextPagerEdit::setExtraSelections(const QList &selections) -{ - d->extraSelections = selections; - qSort(d->extraSelections.begin(), d->extraSelections.end(), compareExtraSelection); - d->layoutDirty = true; - viewport()->update(); -} - -QList TextPagerEdit::extraSelections() const -{ - return d->extraSelections; -} - -void TextEditPrivate::onTextSectionRemoved(TextPagerSection *section) -{ - Q_ASSERT(section); - if (!dirtyForSection(section)) - return; - - sectionsDirty = true; - if (section == sectionPressed) { - sectionPressed = 0; - } - if (section->hasCursor()) { - updateCursorPosition(lastHoverPos); - } -} - -void TextEditPrivate::onTextSectionAdded(TextPagerSection *section) -{ - dirtyForSection(section); - updateCursorPosition(lastHoverPos); - sectionsDirty = true; -} - -void TextEditPrivate::onScrollBarValueChanged(int value) -{ - if (blockScrollBarUpdate || value == requestedScrollBarPosition || value == viewportPosition) - return; - requestedScrollBarPosition = value; - layoutDirty = true; - textEdit->viewport()->update(); - - Q_EMIT scrollBarChanged(); -} - -void TextEditPrivate::onScrollBarActionTriggered(int action) -{ - // IR: see comment at the top of cursorMoveKeyEventReadOnly(). - // Also: added code to handle SliderPageStepAdd and SliderPageStepSub because - // these cases were not handled well by default. These cases occur when the user clicks inside - // the scrollbar area, but not on the bar itself; this should do something similar or - // identical to a page up/down operation. - - switch (action) { - case QAbstractSlider::SliderSingleStepAdd: - scrollLines(1); requestedScrollBarPosition = -1; break; - case QAbstractSlider::SliderSingleStepSub: - scrollLines(-1); requestedScrollBarPosition = -1; break; - case QAbstractSlider::SliderPageStepAdd: - scrollLines(qMax(1, visibleLines - 1)); requestedScrollBarPosition = -1; break; - case QAbstractSlider::SliderPageStepSub: - scrollLines(-qMax(1, visibleLines - 2)); requestedScrollBarPosition = -1; break; - default: break; - } - - Q_EMIT scrollBarChanged(); -} - -void TextEditPrivate::updateScrollBar() -{ - Q_ASSERT(!textEdit->verticalScrollBar()->isSliderDown()); - if (pendingScrollBarUpdate) { - const bool old = blockScrollBarUpdate; - blockScrollBarUpdate = true; - textEdit->verticalScrollBar()->setValue(viewportPosition); - blockScrollBarUpdate = old; - pendingScrollBarUpdate = false; - - Q_EMIT scrollBarChanged(); - } -} - -void TextEditPrivate::onCharactersAddedOrRemoved(int from, int count) -{ - Q_ASSERT(count >= 0); - Q_UNUSED(count); - - textCursor.clearSelection(); - - if(textCursor.position() > document->documentSize()) - textCursor.setPosition(0); - - UiLog().dbg() << - "TextEditPrivate::onCharactersAddedOrRemoved --> textCursor: " << textCursor.position(); - - /*if (from > qMin(bufferPosition + buffer.size(), layoutEnd)) { - return; - }*/ - buffer.clear(); // isn't it better to just add them here? - layoutDirty = true; - textEdit->viewport()->update(); -} - -void TextPagerEdit::ensureCursorVisible() -{ - if (d->textCursor.position() < d->viewportPosition) { - d->updateViewportPosition(qMax(0, d->textCursor.position() - 1), TextPagerLayout::Backward); - } else if (d->textCursor.position() > d->layoutEnd) { - d->updateViewportPosition(d->textCursor.position(), TextPagerLayout::Backward); - viewport()->update(); - } else { - const QRect r = viewport()->rect(); - QRect realCrect = cursorRect(d->textCursor); - QRect crect = realCrect; - crect.setLeft(r.left()); - crect.setRight(r.right()); - // ### what if the cursor is out of bounds horizontally? - - //qDebug() << "ensureCursorVisible" << r << crect << cursorRect(d->textCursor) << horizontalScrollBar()->value() << horizontalScrollBar()->maximum() << d->widest; - if (!r.contains(crect)) { - if (r.intersects(crect)) { - int scroll; - if (d->autoScrollLines != 0) { - scroll = d->autoScrollLines; - } else { - scroll = (crect.top() < r.top() ? -1 : 1); - } - d->scrollLines(scroll); - } else { - d->updateViewportPosition(d->textCursor.position(), TextPagerLayout::Backward); - } - viewport()->update(); - } - - //Adjust the horizontal position - if(realCrect.left() > horizontalScrollBar()->value()+r.width()) - { - int hval=realCrect.left()-r.width()/2; - horizontalScrollBar()->setValue((hval < horizontalScrollBar()->maximum())? - hval: - horizontalScrollBar()->maximum()); - } - else if(realCrect.right() < horizontalScrollBar()->value()) - { - int hval=realCrect.left()-r.width()/2; - horizontalScrollBar()->setValue((hval > horizontalScrollBar()->minimum())? - hval: - horizontalScrollBar()->minimum()); - } - } -} - -//textEdit->horizontalScrollBar()->setPageStep(s.width()); -//textEdit->horizontalScrollBar()->setMaximum(qMax(0, widest - s.width())); - - -TextPagerCursor &TextPagerEdit::textCursor() -{ - return d->textCursor; -} - -const TextPagerCursor &TextPagerEdit::textCursor() const -{ - return d->textCursor; -} - -void TextPagerEdit::setTextCursor(const TextPagerCursor &textCursor) -{ - const bool doEmit = (d->textCursor != textCursor); - d->textCursor = textCursor; - d->textCursor.textEdit = this; - if (doEmit) { - ensureCursorVisible(); - viewport()->update(); - Q_EMIT cursorPositionChanged(textCursor.position()); - } -} - -TextPagerCursor TextPagerEdit::cursorForPosition(const QPoint &pos) const -{ - const int idx = textPositionAt(pos); - if (idx == -1) - return TextPagerCursor(); - return TextPagerCursor(this, idx); -} - -TextPagerSection *TextPagerEdit::sectionAt(const QPoint &pos) const -{ - Q_ASSERT(d->document); - int textPos = textPositionAt(pos); - if (textPos == -1) - textPos = d->document->d->documentSize - 1; - return d->document->d->sectionAt(textPos, this); -} - -QList TextPagerEdit::sections(int from, int size, TextPagerSection::TextSectionOptions opt) const -{ - Q_ASSERT(d->document); - QList sections = d->document->d->getSections(from, size, opt, this); - return sections; -} - -TextPagerSection *TextPagerEdit::insertTextSection(int pos, int size, const QTextCharFormat &format, const QVariant &data) -{ - Q_ASSERT(d->document); - TextPagerSection *section = d->document->insertTextSection(pos, size, format, data); - if (section) { - section->d.textEdit = this; - section->d.priority = 100; - } - return section; -} - -void TextEditPrivate::updateHorizontalPosition() -{ - const QRect r = textEdit->viewport()->rect(); - const QPoint p(qBound(0, lastMouseMove.x(), r.right()), - qBound(0, lastMouseMove.y(), r.bottom())); - int pos = textPositionAt(p); - if (pos == -1) - pos = document->documentSize() - 1; - textEdit->setCursorPosition(pos, TextPagerCursor::KeepAnchor); -} - -void TextEditPrivate::updateScrollBarPosition() -{ - if (requestedScrollBarPosition == -1) { - return; - } else if (requestedScrollBarPosition == viewportPosition) { - requestedScrollBarPosition = -1; - return; - } - - const int req = requestedScrollBarPosition; - requestedScrollBarPosition = -1; - - Direction direction = Forward; - if (lastRequestedScrollBarPosition != -1 && lastRequestedScrollBarPosition != req) { - if (lastRequestedScrollBarPosition > req) { - direction = Backward; - } - } else if (req < viewportPosition) { - direction = Backward; - } - - lastRequestedScrollBarPosition = req; - - updateViewportPosition(req, direction); -} - -void TextEditPrivate::updateScrollBarPageStep() -{ - if (lines.isEmpty()) { - textEdit->verticalScrollBar()->setPageStep(1); - return; - } - const int visibleCharacters = lines.at(qMin(visibleLines, lines.size() - 1)).first - lines.at(0).first; - textEdit->verticalScrollBar()->setPageStep(visibleCharacters); -} - -void TextEditPrivate::onDocumentDestroyed() -{ - document = 0; - textEdit->setDocument(new TextPagerDocument(this)); // there should always be a document -} - -void TextEditPrivate::scrollLines(int lines) -{ - // IR: see comment at the top of cursorMoveKeyEventReadOnly(). - - int pos = viewportPosition; - const Direction d = (lines < 0 ? Backward : Forward); - const int add = lines < 0 ? -1 : 1; - - while (pos + add >= 0 && pos + add < document->documentSize()) { - if (d == Forward && - (lines - add == 0) && - bufferReadCharacter(pos) == '\n') { - // When iterating forwards, be sure not to skip over blank lines - // (ie. lines containing only '\n') by just ignoring them here - // - updateViewportPosition automatically takes us one index past - // this newline, thus displaying the next line) - - // IR: the above comment does not seem to be true, at least it is not always true. - // If performing a 'page down' operation, this 'break' causes one less line to - // be scrolled. This is because pos is already on the newline at the end of - // the previous line; this check re-reads that newline and then breaks without - // reading the last line at all. This is not done when paging backwards. So - // scrolling down 5 lines actually scrolls down 4 lines, and scrolling up 5 - // lines does indeed scroll up 5 lines. - break; - } else { - pos += add; - if (bufferReadCharacter(pos) == '\n') { - if ((lines -= add) == 0) { - break; - } - } - } - } - updateViewportPosition(pos, d); -} - -void TextEditPrivate::timerEvent(QTimerEvent *e) -{ - if (e->timerId() == tripleClickTimer.timerId()) { - tripleClickTimer.stop(); - } else if (e->timerId() == autoScrollTimer.timerId()) { - if (pendingTimeOut != -1) { - autoScrollTimer.start(pendingTimeOut, this); - pendingTimeOut = -1; - } - const QRect r = textEdit->viewport()->rect(); - Q_ASSERT(!r.contains(lastMouseMove)); - enum { LineCount = 1 }; - if (lastMouseMove.y() < r.top()) { - scrollLines(-LineCount); - if (atBeginning()) - autoScrollTimer.stop(); - } else { - Q_ASSERT(lastMouseMove.y() > r.bottom()); - scrollLines(LineCount); - if (atEnd()) - autoScrollTimer.stop(); - } - const QPoint p(qBound(0, lastMouseMove.x(), r.right()), - qBound(0, lastMouseMove.y(), r.bottom())); - int pos = textPositionAt(p); - if (pos == -1) - pos = document->documentSize() - 1; - textEdit->setCursorPosition(pos, TextPagerCursor::KeepAnchor); - } else if (e->timerId() == cursorBlinkTimer.timerId()) { - cursorVisible = !cursorVisible; - const QRect r = textEdit->cursorRect(textCursor) & textEdit->viewport()->rect(); - if (!r.isNull()) - textEdit->viewport()->update(r); - } else { - Q_ASSERT(0); - } -} - -static inline bool compareTextSectionByPriority(const TextPagerSection *left, const TextPagerSection *right) -{ - return left->priority() > right->priority(); -} - -void TextEditPrivate::updateCursorPosition(const QPoint &pos) -{ - lastHoverPos = pos; - const int textPos = textPositionAt(pos); - if (textPos != -1) { - QList hovered = textEdit->sections(textPos, 1, TextPagerSection::IncludePartial); - qSort(hovered.begin(), hovered.end(), compareTextSectionByPriority); - Q_FOREACH(TextPagerSection *section, hovered) { - if (section->hasCursor()) { - delete sectionCursor; - sectionCursor = new QCursor(section->cursor()); - textEdit->viewport()->setCursor(*sectionCursor); - return; - } - } - } - // no cursor - if (sectionCursor) { - textEdit->viewport()->setCursor(Qt::IBeamCursor); - delete sectionCursor; - sectionCursor = 0; - } - - //To notify the line num area widge! - Q_EMIT scrollBarChanged(); -} - -bool TextEditPrivate::isSectionOnScreen(const TextPagerSection *section) const -{ - Q_ASSERT(section->document() == document); - return (::matchSection(section, textEdit) - && section->position() <= layoutEnd - && section->position() + section->size() >= viewportPosition); -} - -void TextEditPrivate::cursorMoveKeyEventReadOnly(QKeyEvent *e) -{ - // IR: changed MoveToPreviousPage from visibleLines to (visibleLines-2) because: - // the scrollLines routine does not perform consistently between page up and page down; - // performing a page down followed by a page up does not get you back to where you started. - // See my comment in scrollLines, which might point to why this is. The quickest way - // to make the functions consistent was to make this change. The primary reason was to - // ensure that the user does not miss any lines of text when they page up and down through - // the document. The same change was made in onScrollBarActionTriggered. - - if (e == QKeySequence::MoveToNextLine) { - scrollLines(1); - } else if (e == QKeySequence::MoveToPreviousLine) { - scrollLines(-1); - } else if (e == QKeySequence::MoveToStartOfDocument) { - textEdit->verticalScrollBar()->setValue(0); - } else if (e == QKeySequence::MoveToEndOfDocument) { - textEdit->verticalScrollBar()->setValue(textEdit->verticalScrollBar()->maximum()); - } else if (e == QKeySequence::MoveToNextPage) { - scrollLines(qMax(1, visibleLines - 1)); - } else if (e == QKeySequence::MoveToPreviousPage) { - scrollLines(-qMax(1, visibleLines - 2)); - } else if (e == QKeySequence::MoveToStartOfLine) { - textCursor.movePosition(TextPagerCursor::StartOfLine); - e->accept(); - return; - } else if (e == QKeySequence::MoveToEndOfLine) { - textCursor.movePosition(TextPagerCursor::EndOfLine); - e->accept(); - return; - } - - else { - e->ignore(); - return; - } - e->accept(); - textCursor.setPosition(viewportPosition); -} - -void TextEditPrivate::relayout() -{ - const QSize s = textEdit->viewport()->size(); - - int widestOri=widest; - - widest=-1; - relayoutByGeometry(s.height()); - if(widest == -1) - widest=widestOri; - - textEdit->horizontalScrollBar()->setPageStep(s.width()); - textEdit->horizontalScrollBar()->setMaximum(qMax(0, widest - s.width())); -#ifdef _UI_TEXTPAGER_DEBUG - qDebug() << widest << s.width() << textEdit->horizontalScrollBar()->maximum(); -#endif -} - -bool TextEditPrivate::dirtyForSection(TextPagerSection *section) -{ - if (isSectionOnScreen(section)) { - layoutDirty = true; - textEdit->viewport()->update(); - return true; - } else { - return false; - } -} - -void TextEditPrivate::onTextSectionFormatChanged(TextPagerSection *section) -{ - dirtyForSection(section); -} - -void TextEditPrivate::onTextSectionCursorChanged(TextPagerSection *section) -{ - if (isSectionOnScreen(section)) { - updateCursorPosition(lastHoverPos); - } -} - -void TextEditPrivate::onSelectionChanged() -{ - if (inMouseEvent && textCursor.hasSelection() && QApplication::clipboard()->supportsSelection()) { - textEdit->copy(QClipboard::Selection); - } - - textEdit->viewport()->update(); - // ### could figure out old selection rect and | it with new one - // ### and update that but is it worth it? - updateCopyAndCutEnabled(); -} - -bool TextEditPrivate::canInsertFromMimeData(const QMimeData *data) const -{ - return data->hasText(); -} - - -//Find the viewport position for the end of the document supposing the the last line -//is exactly located at the bottom of the viewport. -int TextEditPrivate::findLastPageSize() const -{ - if (!document || document->documentSize() == 0) - return -1; - -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << "TextEditPrivate::findLastPageSize -->"; -#endif - TextEditPrivate p(textEdit); - p.font=textEdit->font(); - p.viewportPosition = 0; - p.document = document; - const int documentSize = document->documentSize(); - p.maxViewportPosition = documentSize; - - //The viewport height (including the height of the horizontal slider) - int vh=p.textEdit->viewport()->height(); //-p.textEdit->horizontalScrollBar()->height(); - - //Max 1.5*MinimumBufferSize can be kept in the buffer after the viewportPosition - int maxCharNum=p.MinimumBufferSize*1.5-200; - - //We use the lastpage cache to find a better start position - -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " lastPageCache height:" << lastPage.height << "position:" << lastPage.position << - "documentSize:" << lastPage.documentSize << "widest:" << lastPage.widest; -#endif - - if(lastPage.documentSize != documentSize) - { - lastPage.clear(); - lastPage.documentSize = documentSize; - } - - Direction sizeChangeDir=Forward; - if(lastPage.height != -1) { - if(lastPage.height > vh) { - maxCharNum=documentSize-lastPage.position+1; - sizeChangeDir=Backward; - } else if(lastPage.height< vh) { - maxCharNum=documentSize-lastPage.position+1000; - sizeChangeDir=Backward; - } else { - return lastPage.position; - } - } - - lastPage.height=vh; - - Q_ASSERT(maxCharNum>=0); - - int start = documentSize-maxCharNum; - if(start < 0) start=0; -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - QTime tm; - tm.start(); -#endif - - //Try to find the last page by iteration - int step=0; - const int maxStep=10; - bool reachedTop=(start == 0); - - while(step < maxStep) { -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " ITERATION STEP:" << step; - qDebug() << " start:" << start << "maxCharNum:" << maxCharNum; -#endif - //We get the viewportPosition closest to start - p.updateViewportPosition(start,sizeChangeDir,false); - -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " after updateviewport> start-viewportPos:" << start-p.viewportPosition; -#endif - //adjust the max number of characters - if(start > p.viewportPosition) - maxCharNum+=(start-p.viewportPosition)+1; - - //Relayout using the max number of characters - p.relayoutByPosition(maxCharNum); - -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " vh:" << vh << "fontSize:" << font.pointSize(); - qDebug() << " viewport size" << textEdit->viewport()->size(); - qDebug() << " layoutEnd:" << p.layoutEnd << "documentSize:" << documentSize << "viewportPosition:" << p.viewportPosition << p.contentRect; - qDebug() << " bottom:" << p.textLayouts.last()->boundingRect().bottom(); -#endif - - //The end of the layout should be the end of the document - if(p.layoutEnd == documentSize && p.textLayouts.count() >0) { - - int top=p.textLayouts.last()->boundingRect().bottom()+4-vh; - int pos=documentSize-p.textLayouts.last()->text().size(); - - for(int i=p.textLayouts.count()-2; i >=0; i--) { - - if(p.textLayouts.at(i)->boundingRect().top() <= top) { -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " find position:" << pos; // << "line:" << p.document->lineNumber(pos); -#endif - p.updateViewportPosition(pos, Backward,false); -#ifdef DEBUG_TEXTPAGER_LASTPAGESIZE - qDebug() << " viewPortPosition:" << p.viewportPosition << "time:" << tm.elapsed(); // << "line:" << p.document->lineNumber(p.viewportPosition); -#endif - lastPage.position=p.viewportPosition; - lastPage.widest=p.widest; - return p.viewportPosition; - } - pos-=p.textLayouts.at(i)->text().size(); - } - } - //We could not find the last page. We try again from a larger number of characters from the - //end of the document - step++; - maxCharNum+=1000; - start = documentSize-maxCharNum; - - if(start < 0 && reachedTop) - break; - - if(start < 0) - { - reachedTop=true; - start=0; - } - } - - lastPage.clear(); - - //the whole text is in the viewport, no vertical scrollbar is needed - if(reachedTop) - return -1; - - //If we are here we the last page position will be the end of the document and there will be - //a white space block after the last row in the editor. - //TODO: improve it! - return documentSize; -} - -void TextPagerEdit::setSyntaxHighlighter(SyntaxHighlighter *h) -{ - if (h && h->textEdit() == this) { - if (d->syntaxHighlighters.size() == 1) - return; - takeSyntaxHighlighter(h); - } - clearSyntaxHighlighters(); - if (h) { - addSyntaxHighlighter(h); - } -} - - -void TextPagerEdit::setEnableSearchHighlighter(bool b) -{ - useSearchHighlight_=b; - if(useSearchHighlight_) { - setSyntaxHighlighter(searchHighlight_); - } else { - //searchHighlight_ can only be deleted when the editor is deleted. - //Because clearSyntaxHighlighters() deletes the highlighters we need to use - //takeSyntaxHighlighter() instead. - if(d->syntaxHighlighters.contains(searchHighlight_)) { - takeSyntaxHighlighter(searchHighlight_); - d->layoutDirty = true; - viewport()->update(); - //clearSyntaxHighlighters(); - } - } -} - -void TextPagerEdit::clearSearchHighlighter() -{ - searchHighlight_->clear(); -} - -void TextPagerEdit::setSearchHighlighter(QString txt,TextPagerDocument::FindMode mode) -{ - searchHighlight_->reset(txt,mode,useSearchHighlight_); -} - -void TextPagerEdit::setSearchHighlighter(QRegExp rx,TextPagerDocument::FindMode mode) -{ - searchHighlight_->reset(rx,mode,useSearchHighlight_); -} - -void TextPagerEdit::gotoLine(int lineNum) -{ - TextPagerCursor cursor=d->document->findLine(lineNum,textCursor()); - if(!cursor.isNull()) - setTextCursor(cursor); -} - -//--------------------------------------------- -// Fontsize management -//--------------------------------------------- - -void TextPagerEdit::setFontProperty(VProperty* p) -{ - fontProp_=p; - fontProp_->addObserver(this); - updateFont(); -} - -void TextPagerEdit::zoomIn() -{ - QFont f=font(); - int fps=f.pointSize(); - f.setPointSize(fps+1); - setFont(f); - - fontSizeChangedByZoom(); -} - -void TextPagerEdit::zoomOut() -{ - int oriSize=font().pointSize(); - - QFont f=font(); - int fps=f.pointSize(); - if(fps > 1) - { - f.setPointSize(fps-1); - setFont(f); - } - - if(font().pointSize() != oriSize) - fontSizeChangedByZoom(); -} - -void TextPagerEdit::fontSizeChangedByZoom() -{ - if(fontProp_) - fontProp_->setValue(font()); -} - -void TextPagerEdit::updateFont() -{ - if(fontProp_) - { - QFont f=fontProp_->value().value(); - if(font() != f) - setFont(f); - } -} - -void TextPagerEdit::notifyChange(VProperty* p) -{ - if(fontProp_ ==p) - { - setFont(p->value().value()); - } -} - -void TextPagerEdit::setShowLineNumbers(bool b) -{ - showLineNum_=b; - if(lineNumArea_) - { - lineNumArea_->setVisible(b); - //Initialise the width - lineNumArea_->updateWidth(); - } -} - -void TextPagerEdit::setLineNumberArea(TextPagerLineNumberArea *a) -{ - lineNumArea_=a; - connect(d,SIGNAL(scrollBarChanged()), - lineNumArea_,SLOT(update())); - - //Initialise the width - lineNumArea_->updateWidth(); -} - -void TextPagerEdit::lineNumberAreaPaintEvent(QPaintEvent *e) -{ - if(!lineNumArea_ || !showLineNum_) - return; - - QPainter painter(lineNumArea_); - const QRect er = e->rect(); - //painter.translate(-horizontalScrollBar()->value(), 0); - //painter.setFont(font()); - - QVector selections; - selections.reserve(d->extraSelections.size() + 1); - int textLayoutOffset = d->viewportPosition; - - QRect numRect=er; - - //Background and border - painter.fillRect(numRect, lineNumArea_->bgColour()); - painter.setPen(QPen(lineNumArea_->separatorColour())); - painter.drawLine(lineNumArea_->width()-1,er.y(),lineNumArea_->width()-1,er.bottom()); - - int numWidth=lineNumArea_->width()-lineNumArea_->rightMargin(); - - QFont fontNormal(font()); // the font to use for most line numbers - QFont fontBold(fontNormal); // the font to use for the current line number - fontBold.setBold(true); - painter.setPen(lineNumArea_->fontColour()); - painter.setFont(fontNormal); - - int cursorPos=d->textCursor.position(); - - //const QTextLayout *cursorLayout = d->cursorVisible ? d->layoutForPosition(d->textCursor.position()) : 0; - //int extraSelectionIndex = 0; - QTextLayout::FormatRange selectionRange; - selectionRange.start = -1; - int maxLineNum=-1; - Q_FOREACH(QTextLayout *l, d->textLayouts) { - const int textSize = l->text().size(); - const QRect r = l->boundingRect().toRect(); - if (r.intersects(er)) { - - const int lineNum=lineNumber(textLayoutOffset)+1; - maxLineNum=lineNum; - QRect lRect(0,r.y(),numWidth,r.height()); - - // is this the current line? - if(cursorPos >= textLayoutOffset && cursorPos <= textLayoutOffset+l->text().size()) - { - painter.setFont(fontBold); - painter.fillRect(lRect, lineNumArea_->currentColour()); // highlight the background - painter.drawText(lRect,QString::number(lineNum),Qt::AlignRight|Qt::AlignVCenter); - painter.setFont(fontNormal); - } - else - { - painter.drawText(lRect,QString::number(lineNum),Qt::AlignRight|Qt::AlignVCenter); - } - - } else if (r.top() > er.bottom()) { - break; - } - textLayoutOffset += textSize + 1; - } - - if(maxLineNum != -1) - lineNumArea_->updateWidth(maxLineNum); -} - -//========================================================================== -// -// TextPagerLineNumberArea -// -//========================================================================== - -TextPagerLineNumberArea::TextPagerLineNumberArea(TextPagerEdit *editor) : - QWidget(editor), textEditor_ (editor), digits_(6), rightMargin_(3), - bgCol_(232,231,230), fontCol_(220,220,200), separatorCol_(220,220,200), - currentCol_(212,212,255) -{ - Q_ASSERT(textEditor_); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaBackground")) - bgCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaFontColour")) - fontCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaSeparator")) - separatorCol_=p->value().value(); - - if(VProperty* p=VConfig::instance()->find("view.textEdit.numAreaCurrent")) - currentCol_=p->value().value(); - - editor->setLineNumberArea(this); -} - -void TextPagerLineNumberArea::updateWidth(int maxLineNum) -{ - if(maxLineNum == -1 || pow(10,digits_)-1 < maxLineNum) - { - setFixedWidth(computeWidth(maxLineNum)); - } -} - -int TextPagerLineNumberArea::computeWidth(int maxLineNum) const -{ - if(maxLineNum > 0) - { - int maxDigits=1; - int mx = maxLineNum; - while ( mx >= 10) - { - mx /= 10; - ++maxDigits; - } - - if(maxDigits > digits_) - digits_=maxDigits; - } - - return (textEditor_)?(3 + textEditor_->fontMetrics().width(QLatin1Char('9')) * digits_ + rightMargin_):3+rightMargin_; - -} - - - - diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,228 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGEREDIT_HPP__ -#define TEXTPAGEREDIT_HPP__ - -#include -#include - -#include "syntaxhighlighter.hpp" -#include "TextPagerDocument.hpp" -#include "TextPagerCursor.hpp" -#include "TextPagerSection.hpp" - -#include "VProperty.hpp" - -class TextPagerLineNumberArea; -class TextEditPrivate; -class TextPagerSearchHighlighter; - -class TextPagerEdit : public QAbstractScrollArea, public VPropertyObserver -{ - friend class TextPagerLineNumberArea; - - Q_OBJECT - Q_PROPERTY(int cursorWidth READ cursorWidth WRITE setCursorWidth) - Q_PROPERTY(bool readOnly READ readOnly WRITE setReadOnly) - Q_PROPERTY(bool cursorVisible READ cursorVisible WRITE setCursorVisible) - Q_PROPERTY(QString selectedText READ selectedText) - Q_PROPERTY(int maximumSizeCopy READ maximumSizeCopy WRITE setMaximumSizeCopy) - Q_PROPERTY(bool lineBreaking READ lineBreaking WRITE setLineBreaking) - -public: - TextPagerEdit(QWidget *parent = 0); - ~TextPagerEdit(); - - TextPagerDocument *document() const; - void setDocument(TextPagerDocument *doc); - - int cursorWidth() const; - void setCursorWidth(int cc); - - struct ExtraSelection - { - TextPagerCursor cursor; - QTextCharFormat format; - }; - - void setExtraSelections(const QList &selections); - QList extraSelections() const; - - void setSyntaxHighlighter(SyntaxHighlighter *h); - inline SyntaxHighlighter *syntaxHighlighter() const { return syntaxHighlighters().value(0); } - - QList syntaxHighlighters() const; - void addSyntaxHighlighter(SyntaxHighlighter *highlighter); - void takeSyntaxHighlighter(SyntaxHighlighter *highlighter); - void removeSyntaxHighlighter(SyntaxHighlighter *highlighter); - void clearSyntaxHighlighters(); - - //bool load(QIODevice *device, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse, QTextCodec *codec = 0); - - bool load(const QString &fileName, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse, QTextCodec *codec = 0); - - void paintEvent(QPaintEvent *e); - void scrollContentsBy(int dx, int dy); - - bool moveCursorPosition(TextPagerCursor::MoveOperation op, TextPagerCursor::MoveMode = TextPagerCursor::MoveAnchor, int n = 1); - void setCursorPosition(int pos, TextPagerCursor::MoveMode mode = TextPagerCursor::MoveAnchor); - - int viewportPosition() const; - int cursorPosition() const; - - int textPositionAt(const QPoint &pos) const; - - bool readOnly() const; - void setReadOnly(bool rr); - - bool lineBreaking() const; - void setLineBreaking(bool lb); - - int maximumSizeCopy() const; - void setMaximumSizeCopy(int max); - - QRect cursorBlockRect(const TextPagerCursor &cursor) const; - QRect cursorRect(const TextPagerCursor &cursor) const; - - int lineNumber(int position) const; - int columnNumber(int position) const; - int lineNumber(const TextPagerCursor &cursor) const; - int columnNumber(const TextPagerCursor &cursor) const; - - bool cursorVisible() const; - void setCursorVisible(bool cc); - - QString selectedText() const; - bool hasSelection() const; - - void setText(const QString &text); - QString read(int pos, int size) const; - QChar readCharacter(int index) const; - - void insert(int pos, const QString &text); - void remove(int from, int size); - - TextPagerCursor &textCursor(); - const TextPagerCursor &textCursor() const; - void setTextCursor(const TextPagerCursor &textCursor); - - TextPagerCursor cursorForPosition(const QPoint &pos) const; - - TextPagerSection *sectionAt(const QPoint &pos) const; - - QList sections(int from = 0, int size = -1, TextPagerSection::TextSectionOptions opt = 0) const; - inline TextPagerSection *sectionAt(int pos) const { return sections(pos, 1, TextPagerSection::IncludePartial).value(0); } - TextPagerSection *insertTextSection(int pos, int size, const QTextCharFormat &format = QTextCharFormat(), - const QVariant &data = QVariant()); - - void ensureCursorVisible(const TextPagerCursor &cursor, int linesMargin = 0); - - void setEnableSearchHighlighter(bool); - void clearSearchHighlighter(); - void setSearchHighlighter(QString txt,TextPagerDocument::FindMode mode); - void setSearchHighlighter(QRegExp rx,TextPagerDocument::FindMode mode); - - void gotoLine(int); - void setFontProperty(VProperty* p); - void notifyChange(VProperty* p); - void zoomIn(); - void zoomOut(); - - void setShowLineNumbers(bool b); - void setLineNumberArea(TextPagerLineNumberArea *a); - - enum ActionType { - CopyAction, - SelectAllAction - }; - QAction *action(ActionType type) const; - -public Q_SLOTS: - void ensureCursorVisible(); - void copy(QClipboard::Mode mode = QClipboard::Clipboard); - void selectAll(); - void clearSelection(); - -Q_SIGNALS: - void copyAvailable(bool on); - void textChanged(); - void selectionChanged(); - void cursorPositionChanged(int pos); - void sectionClicked(TextPagerSection *section, const QPoint &pos); - -protected: - //virtual void paste(int position, QClipboard::Mode mode); - virtual void changeEvent(QEvent *e); - virtual void keyPressEvent(QKeyEvent *e); - virtual void keyReleaseEvent(QKeyEvent *e); - virtual void wheelEvent(QWheelEvent *e); - virtual void mousePressEvent(QMouseEvent *e); - virtual void mouseDoubleClickEvent(QMouseEvent *); - virtual void mouseMoveEvent(QMouseEvent *e); - virtual void mouseReleaseEvent(QMouseEvent *e); - virtual void resizeEvent(QResizeEvent *e); - -private: - void updateFont(); - void fontSizeChangedByZoom(); - - void lineNumberAreaPaintEvent(QPaintEvent *e); - int lineNumberAreaWidth(); - void updateLineNumberArea(); - - TextEditPrivate *d; - friend class TextLayoutCacheManager; - friend class TextEditPrivate; - friend class TextPagerCursor; - - TextPagerSearchHighlighter* searchHighlight_; - bool useSearchHighlight_; - - bool showLineNum_; - TextPagerLineNumberArea* lineNumArea_; - VProperty* fontProp_; -}; - - -class TextPagerLineNumberArea : public QWidget -{ -public: - explicit TextPagerLineNumberArea(TextPagerEdit *editor); - QSize sizeHint() const {return QSize(computeWidth(), 0);} - int rightMargin() const {return rightMargin_;} - void updateWidth(int maxLineNum=-1); - QColor bgColour() const {return bgCol_;} - QColor fontColour() const {return fontCol_;} - QColor separatorColour() const {return separatorCol_;} - QColor currentColour() const {return currentCol_;} - -protected: - void paintEvent(QPaintEvent *event) { textEditor_->lineNumberAreaPaintEvent(event);} - -private: - int computeWidth(int maxLineNum=-1) const; - - TextPagerEdit *textEditor_; - mutable int digits_; - int rightMargin_; - QColor bgCol_; - QColor fontCol_; - QColor separatorCol_; - QColor currentCol_; -}; - - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit_p.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit_p.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerEdit_p.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerEdit_p.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGEREDIT_P_HPP__ -#define TEXTPAGEREDIT_P_HPP__ - -#include -#include -#include -#include -#include -#include "TextPagerLayout_p.hpp" -#include "TextPagerDocument_p.hpp" -#include "TextPagerCursor.hpp" -#include "TextPagerEdit.hpp" - -struct DocumentCommand; -struct CursorData { - int position, anchor; -}; - -struct LastPageCache { - LastPageCache() : position(-1),height(-1),widest(-1), documentSize(-1) {} - void clear() {position=-1; height=-1; widest=-1; documentSize=-1;} - int position; - int height; - int widest; - int documentSize; -}; - -class TextEditPrivate : public QObject, public TextPagerLayout -{ - Q_OBJECT -public: - TextEditPrivate(TextPagerEdit *qptr) - : requestedScrollBarPosition(-1), lastRequestedScrollBarPosition(-1), cursorWidth(2), - sectionCount(0), maximumSizeCopy(50000), pendingTimeOut(-1), autoScrollLines(0), - readOnly(true), cursorVisible(false), blockScrollBarUpdate(false), - updateScrollBarPageStepPending(true), inMouseEvent(false), sectionPressed(0), - pendingScrollBarUpdate(false), sectionCursor(0) - { - textEdit = qptr; - } - - bool canInsertFromMimeData(const QMimeData *data) const; - void updateHorizontalPosition(); - void updateScrollBarPosition(); - void updateScrollBarPageStep(); - void scrollLines(int lines); - void timerEvent(QTimerEvent *e); - void updateCursorPosition(const QPoint &pos); - int findLastPageSize() const; - bool atBeginning() const { return viewportPosition == 0; } - bool atEnd() const { return textEdit->verticalScrollBar()->value() == textEdit->verticalScrollBar()->maximum(); } - bool dirtyForSection(TextPagerSection *section); - void updateCopyAndCutEnabled(); - bool isSectionOnScreen(const TextPagerSection *section) const; - void cursorMoveKeyEventReadOnly(QKeyEvent *e); - virtual void relayout(); // from TextPagerLayout - void adjustVerticalScrollBar(); - - int requestedScrollBarPosition, lastRequestedScrollBarPosition, cursorWidth, sectionCount, - maximumSizeCopy, pendingTimeOut, autoScrollLines; - bool readOnly, cursorVisible, blockScrollBarUpdate, updateScrollBarPageStepPending, inMouseEvent; - QBasicTimer autoScrollTimer, cursorBlinkTimer; - QAction *actions[TextPagerEdit::SelectAllAction]; - TextPagerSection *sectionPressed; - TextPagerCursor textCursor, dragOverrideCursor; - QBasicTimer tripleClickTimer; - bool pendingScrollBarUpdate; - QCursor *sectionCursor; - QPoint lastHoverPos, lastMouseMove; - QHash > undoRedoCommands; - mutable LastPageCache lastPage; - -public Q_SLOTS: - void onSyntaxHighlighterDestroyed(QObject *o); - void onSelectionChanged(); - void onTextSectionAdded(TextPagerSection *section); - void onTextSectionRemoved(TextPagerSection *section); - void onTextSectionFormatChanged(TextPagerSection *section); - void onTextSectionCursorChanged(TextPagerSection *section); - void updateScrollBar(); - void onDocumentDestroyed(); - void onDocumentSizeChanged(int size); - -#if 0 - void onDocumentCommandInserted(DocumentCommand *cmd); - void onDocumentCommandFinished(DocumentCommand *cmd); - void onDocumentCommandRemoved(DocumentCommand *cmd); - void onDocumentCommandTriggered(DocumentCommand *cmd, bool undo); -#endif - - - void onScrollBarValueChanged(int value); - void onScrollBarActionTriggered(int action); - void onCharactersAddedOrRemoved(int index, int count); - -Q_SIGNALS: - void scrollBarChanged(); -}; - -/* -class DebugWindow : public QWidget -{ -public: - DebugWindow(TextEditPrivate *p) - : priv(p) - { - } - - void paintEvent(QPaintEvent *) - { - if (priv->lines.isEmpty()) - return; - - QPainter p(this); - p.fillRect(QRect(0, pixels(priv->viewportPosition), width(), - pixels(priv->lines.last().first + - priv->lines.last().second.textLength())), - Qt::black); - p.fillRect(QRect(0, pixels(priv->viewportPosition), width(), pixels(priv->layoutEnd)), Qt::red); - } - - int pixels(int pos) const - { - double fraction = double(pos) / double(priv->document->documentSize()); - return int(double(height()) * fraction); - } -private: - TextEditPrivate *priv; -}; -*/ - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerLayout_p.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerLayout_p.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerLayout_p.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerLayout_p.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,454 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "TextPagerLayout_p.hpp" -#include "TextPagerEdit_p.hpp" -#include "TextPagerDocument.hpp" -#include "TextPagerEdit.hpp" - -int TextPagerLayout::viewportWidth() const -{ - if (!lineBreaking) - return INT_MAX - 1024; - return textEdit ? textEdit->viewport()->width() : viewport; -} - -int TextPagerLayout::doLayout(int index, QList *sections) // index is in document coordinates -{ - QTextLayout *textLayout = 0; - if (!unusedTextLayouts.isEmpty()) { - textLayout = unusedTextLayouts.takeLast(); - textLayout->clearAdditionalFormats(); - } else { - textLayout = new QTextLayout; - textLayout->setCacheEnabled(true); - textLayout->setFont(font); - QTextOption option; - option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); - textLayout->setTextOption(option); - } - textLayouts.append(textLayout); - if (index != 0 && bufferReadCharacter(index - 1) != '\n') { - qWarning() << index << viewportPosition << document->read(index - 1, 20) - << bufferReadCharacter(index - 1); - } - Q_ASSERT(index == 0 || bufferReadCharacter(index - 1) == '\n'); - const int max = bufferPosition + buffer.size(); - const int lineStart = index; - while (index < max && bufferReadCharacter(index) != '\n') - ++index; - - const QString string = buffer.mid(lineStart - bufferPosition, index - lineStart); - Q_ASSERT(string.size() == index - lineStart); - Q_ASSERT(!string.contains('\n')); - if (index < max) - ++index; // for the newline - textLayout->setText(string); - - QMultiMap formatMap; - if (sections) { - do { - Q_ASSERT(!sections->isEmpty()); - TextPagerSection *l = sections->first(); - Q_ASSERT(::matchSection(l, textEdit)); - Q_ASSERT(l->position() + l->size() >= lineStart); - if (l->position() >= index) { - break; - } - // section is in this QTextLayout - QTextLayout::FormatRange range; - range.start = qMax(0, l->position() - lineStart); // offset in QTextLayout - range.length = qMin(l->position() + l->size(), index) - lineStart - range.start; - range.format = l->format(); - formatMap.insertMulti(l->priority(), range); - if (l->position() + l->size() >= index) { // > ### ??? - // means section didn't end here. It continues in the next QTextLayout - break; - } - sections->removeFirst(); - } while (!sections->isEmpty()); - } - QList formats = formatMap.values(); - - int leftMargin = LeftMargin; - int rightMargin = 0; - int topMargin = 0; - int bottomMargin = 0; - Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { - syntaxHighlighter->d->currentBlockPosition = lineStart; - syntaxHighlighter->d->formatRanges.clear(); - syntaxHighlighter->d->currentBlock = string; - syntaxHighlighter->highlightBlock(string); - syntaxHighlighter->d->currentBlock.clear(); - if (syntaxHighlighter->d->blockFormat.isValid()) { - blockFormats[textLayout] = syntaxHighlighter->d->blockFormat; - if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockLeftMargin)) - leftMargin = syntaxHighlighter->d->blockFormat.leftMargin(); - if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockRightMargin)) - rightMargin = syntaxHighlighter->d->blockFormat.rightMargin(); - if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockTopMargin)) - topMargin = syntaxHighlighter->d->blockFormat.topMargin(); - if (syntaxHighlighter->d->blockFormat.hasProperty(QTextFormat::BlockBottomMargin)) - bottomMargin = syntaxHighlighter->d->blockFormat.bottomMargin(); - - } - syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState; - if (!syntaxHighlighter->d->formatRanges.isEmpty()) - formats += syntaxHighlighter->d->formatRanges; - } - textLayout->setAdditionalFormats(formats); - textLayout->beginLayout(); - const int lineWidth = viewportWidth() - (leftMargin + rightMargin); - - int localWidest = -1; - Q_FOREVER { - QTextLine line = textLayout->createLine(); - if (!line.isValid()) { - break; - } - line.setLineWidth(lineWidth); - if (!lineBreaking) - localWidest = qMax(localWidest, line.naturalTextWidth() + (LeftMargin * 2)); - // ### support blockformat margins etc - int y = topMargin + lastBottomMargin; - if (!lines.isEmpty()) { - y += int(lines.last().second.rect().bottom()); - // QTextLine doesn't seem to get its rect() update until a - // new line has been created (or presumably in endLayout) - } - line.setPosition(QPoint(leftMargin, y)); - lines.append(qMakePair(lineStart + line.textStart(), line)); - } - - widest = qMax(widest, localWidest); - lastBottomMargin = bottomMargin; - - textLayout->endLayout(); -#ifndef QT_NO_DEBUG - for (int i=1; iboundingRect().toRect(); - // this will actually take the entire width set in setLineWidth - // and not what it actually uses. - r.setWidth(localWidest); - - contentRect |= r; - Q_ASSERT(!lineBreaking || contentRect.right() <= qint64(viewportWidth()) + LeftMargin - || viewportWidth() == -1); - - Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { - syntaxHighlighter->d->formatRanges.clear(); - syntaxHighlighter->d->blockFormat = QTextBlockFormat(); - syntaxHighlighter->d->currentBlockPosition = -1; - } - - return index; -} - -int TextPagerLayout::textPositionAt(const QPoint &p) const -{ - QPoint pos = p; - if (pos.x() >= 0 && pos.x() < LeftMargin) - pos.rx() = LeftMargin; // clicking in the margin area should count as the first characters - - int textLayoutOffset = viewportPosition; - Q_FOREACH(const QTextLayout *l, textLayouts) { - if(l->boundingRect().y() <= pos.y() && l->boundingRect().bottom() >=pos.y()) { - //if (l->boundingRect().toRect().contains(pos)) { - const int lineCount = l->lineCount(); - for (int i=0; ilineAt(i); - if (line.y() <= pos.y() && pos.y() <= line.height() + line.y()) { // ### < ??? - { - if(pos.x() > l->boundingRect().right()) - pos.setX(l->boundingRect().right()-1); - - return textLayoutOffset + line.xToCursor(qMax(LeftMargin, pos.x())); - } - } - } - } - textLayoutOffset += l->text().size() + 1; // + 1 for newlines which aren't in the QTextLayout - } - return -1; -} - -QList TextPagerLayout::relayoutCommon() -{ -// widest = -1; // ### should this be relative to current content or remember? What if you remove the line that was the widest? - Q_ASSERT(layoutDirty); - layoutDirty = false; - Q_ASSERT(document); - lines.clear(); - unusedTextLayouts = textLayouts; - textLayouts.clear(); - contentRect = QRect(); - visibleLines = lastVisibleCharacter = -1; - - Q_FOREACH(SyntaxHighlighter *syntaxHighlighter, syntaxHighlighters) { - syntaxHighlighter->d->previousBlockState = syntaxHighlighter->d->currentBlockState = -1; - } - - if (viewportPosition < bufferPosition - || (bufferPosition + buffer.size() < document->documentSize() - && buffer.size() - bufferOffset() < MinimumBufferSize)) { - bufferPosition = qMax(0, viewportPosition - MinimumBufferSize); - buffer = document->read(bufferPosition, int(MinimumBufferSize * 2.5)); - sections = document->d->getSections(bufferPosition, buffer.size(), TextPagerSection::IncludePartial, textEdit); - } else if (sectionsDirty) { - sections = document->d->getSections(bufferPosition, buffer.size(), TextPagerSection::IncludePartial, textEdit); - } - sectionsDirty = false; - QList l = sections; - while (!l.isEmpty() && l.first()->position() + l.first()->size() < viewportPosition) - l.takeFirst(); // could cache these as well - return l; -} - -void TextPagerLayout::relayoutByGeometry(int height) -{ - if (!layoutDirty) - return; - - QList l = relayoutCommon(); - - const int max = viewportPosition + buffer.size() - bufferOffset(); // in document coordinates - ASSUME(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n'); - - static const int extraLines = qMax(2, qgetenv("LAZYTEXTEDIT_EXTRA_LINES").toInt()); - int index = viewportPosition; - while (index < max) { - index = doLayout(index, l.isEmpty() ? 0 : &l); - Q_ASSERT(index == max || document->readCharacter(index - 1) == '\n'); - Q_ASSERT(!textLayouts.isEmpty()); - const int y = int(textLayouts.last()->boundingRect().bottom()); - if (y >= height) { - if (visibleLines == -1) { - visibleLines = lines.size(); - lastVisibleCharacter = index; - } else if (lines.size() >= visibleLines + extraLines) { - break; - } - } - } - if (visibleLines == -1) { - visibleLines = lines.size(); - lastVisibleCharacter = index; - } - - - layoutEnd = qMin(index, max); - qDeleteAll(unusedTextLayouts); - unusedTextLayouts.clear(); - Q_ASSERT(viewportPosition < layoutEnd || - (viewportPosition == layoutEnd && viewportPosition == document->documentSize())); -// qDebug() << "layoutEnd" << layoutEnd << "viewportPosition" << viewportPosition; -} - -void TextPagerLayout::relayoutByPosition(int size) -{ - if (!layoutDirty) - return; - - QList l = relayoutCommon(); - - const int max = viewportPosition + qMin(size, buffer.size() - bufferOffset()); - Q_ASSERT(viewportPosition == 0 || bufferReadCharacter(viewportPosition - 1) == '\n'); - int index = viewportPosition; - while (index < max) { - index = doLayout(index, l.isEmpty() ? 0 : &l); - } - layoutEnd = index; - - qDeleteAll(unusedTextLayouts); - unusedTextLayouts.clear(); - Q_ASSERT(viewportPosition < layoutEnd || - (viewportPosition == layoutEnd && viewportPosition == document->documentSize())); -} - -void TextPagerLayout::relayout() -{ - //relayoutByPosition(2000); // ### totally arbitrary number - relayoutByPosition(1.5*MinimumBufferSize); -} - -QTextLayout *TextPagerLayout::layoutForPosition(int pos, int *offset, int *index) const -{ - if (offset) - *offset = -1; - if (index) - *index = -1; - - if (textLayouts.isEmpty() || pos < viewportPosition || pos > layoutEnd) { - return 0; - } - - int textLayoutOffset = viewportPosition; - int i = 0; - - Q_FOREACH(QTextLayout *l, textLayouts) { - if (pos >= textLayoutOffset && pos <= l->text().size() + textLayoutOffset) { - if (offset) - *offset = pos - textLayoutOffset; - if (index) - *index = i; - return l; - } - ++i; - textLayoutOffset += l->text().size() + 1; - } - return 0; -} - -QTextLine TextPagerLayout::lineForPosition(int pos, int *offsetInLine, int *lineIndex, bool *lastLine) const -{ - if (offsetInLine) - *offsetInLine = -1; - if (lineIndex) - *lineIndex = -1; - if (lastLine) - *lastLine = false; - - if (pos < viewportPosition || pos >= layoutEnd || textLayouts.isEmpty() || lines.isEmpty()) { - return QTextLine(); - } - int layoutIndex = 0; - QTextLayout *layout = textLayouts.value(layoutIndex); - Q_ASSERT(layout); - for (int i=0; i &line = lines.at(i); - int lineEnd = line.first + line.second.textLength(); - const bool last = line.second.lineNumber() + 1 == layout->lineCount(); - if (last) { - ++lineEnd; - // 1 is for newline characters - layout = textLayouts.value(++layoutIndex); - // could be 0 - } - if (pos < lineEnd) { - if (offsetInLine) { - *offsetInLine = pos - line.first; - Q_ASSERT(*offsetInLine >= 0); - Q_ASSERT(*offsetInLine < lineEnd + pos); - } - if (lineIndex) { - *lineIndex = i; - } - if (lastLine) - *lastLine = last; - return line.second; - } else if (!layout) { - break; - } - } - qWarning() << "Couldn't find a line for" << pos << "viewportPosition" << viewportPosition - << "layoutEnd" << layoutEnd; - Q_ASSERT(0); - return QTextLine(); -} - -// pos is not necessarily on a newline. Finds closest newline in the -// right direction and sets viewportPosition to that. Updates -// scrollbars if this is a TextEditPrivate - -void TextPagerLayout::updateViewportPosition(int pos, Direction direction,bool applyIt) -{ - pos = qMin(pos, maxViewportPosition); - if (document->documentSize() == 0) { - viewportPosition = 0; - } else { - Q_ASSERT(document->documentSize() > 0); - int index = document->find('\n', qMax(0, pos + (direction == Backward ? -1 : 0)), - TextPagerDocument::FindMode(direction)).anchor(); - if (index == -1) { - if (direction == Backward) { - index = 0; - } else { - index = qMax(0, document->find('\n', document->documentSize() - 1, TextPagerDocument::FindBackward).position()); - // position after last newline in document - // if there is no newline put it at 0 - } - } else { - ++index; - } - Q_ASSERT(index != -1); - viewportPosition = index; - - if (viewportPosition != 0 && document->read(viewportPosition - 1, 1) != QString("\n")) - qWarning() << "viewportPosition" << viewportPosition << document->read(viewportPosition - 1, 10) << this; - ASSUME(viewportPosition == 0 || document->read(viewportPosition - 1, 1) == QString("\n")); - } - if (viewportPosition > maxViewportPosition && direction == Forward) { - updateViewportPosition(viewportPosition, Backward); - return; - } - layoutDirty = true; - - if(applyIt) { - - if (textEdit && !suppressTextEditUpdates) { - textEdit->viewport()->update(); - TextEditPrivate *p = static_cast(this); - p->pendingScrollBarUpdate = true; - p->updateCursorPosition(p->lastHoverPos); - if (!textEdit->verticalScrollBar()->isSliderDown()) { - p->updateScrollBar(); - } // sliderReleased is connected to updateScrollBar() - } - - relayout(); - } -} - -#ifndef QT_NO_DEBUG_STREAM -QDebug &operator<<(QDebug &str, const QTextLine &line) -{ - if (!line.isValid()) { - str << "QTextLine() (invalid)"; - return str; - } - str.space() << "lineNumber" << line.lineNumber() - << "textStart" << line.textStart() - << "textLength" << line.textLength() - << "position" << line.position(); - return str; -} - -QString TextPagerLayout::dump() const -{ - QString out; - QTextStream ts(&out, QIODevice::WriteOnly); - ts << "viewportPosition " << viewportPosition - << " layoutEnd " << layoutEnd - << " viewportWidth " << viewportWidth() << '\n'; - for (int i=0; ilineCount(); ++j) { - QTextLine line = layout->lineAt(j); - ts << layout->text().mid(line.textStart(), line.textLength()); - if (j + 1 < layout->lineCount()) { - ts << "\n"; - } else { - ts << "\n"; - } - } - } - return out; -} - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerLayout_p.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerLayout_p.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerLayout_p.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerLayout_p.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,125 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERLAYOUT_P_HPP_ -#define TEXTPAGERLAYOUT_P_HPP_ - -#include -#include -#include -#include -#include -#include -#include -#ifndef QT_NO_DEBUG_STREAM -#include -#endif -#include "TextPagerDocument.hpp" -#include "TextPagerEdit.hpp" -#include "syntaxhighlighter.hpp" -#include "WeakPointer.hpp" - -#ifndef QT_NO_DEBUG_STREAM -QDebug &operator<<(QDebug &str, const QTextLine &line); -#endif - -class TextDocumentBuffer -{ -public: - TextDocumentBuffer(TextPagerDocument *doc) : document(doc), bufferPosition(0) {} - virtual ~TextDocumentBuffer() {} - - inline QString bufferRead(int from, int size) const - { - Q_ASSERT(document); - if (from < bufferPosition || from + size > bufferPosition + buffer.size()) { - return document->read(from, size); - } - return buffer.mid(from - bufferPosition, size); - } - inline QChar bufferReadCharacter(int index) const // document coordinates - { - Q_ASSERT(document); - if (index >= bufferPosition && index < bufferPosition + buffer.size()) { - return buffer.at(index - bufferPosition); - } else { - Q_ASSERT(index >= 0 && index < document->documentSize()); // what if index == documentSize? - return document->readCharacter(index); - } - } - - TextPagerDocument *document; - int bufferPosition; - QString buffer; -}; - -class TextPagerEdit; -class TextPagerLayout : public TextDocumentBuffer -{ -public: - enum { MinimumBufferSize = 90000, LeftMargin = 3 }; - TextPagerLayout(TextPagerDocument *doc = 0) - : TextDocumentBuffer(doc), textEdit(0), - viewportPosition(0), layoutEnd(-1), viewport(-1), - visibleLines(-1), lastVisibleCharacter(-1), lastBottomMargin(0), - widest(-1), maxViewportPosition(0), layoutDirty(true), sectionsDirty(true), - lineBreaking(false), suppressTextEditUpdates(false) - { - } - - virtual ~TextPagerLayout() - { - qDeleteAll(textLayouts); - qDeleteAll(unusedTextLayouts); - } - - TextPagerEdit *textEdit; - QList syntaxHighlighters; - int viewportPosition, layoutEnd, viewport, visibleLines, - lastVisibleCharacter, lastBottomMargin, widest, maxViewportPosition; - bool layoutDirty, sectionsDirty, lineBreaking, suppressTextEditUpdates; - QList textLayouts, unusedTextLayouts; - QHash blockFormats; - QList extraSelections; - QList > lines; // int is start position of line in document coordinates - QRect contentRect; // contentRect means the laid out area, not just the area currently visible - QList sections; // these are all the sections in the buffer. Some might be before the current viewport - QFont font; - - QList relayoutCommon(); // should maybe be smarter about MinimumScreenSize. Detect it based on font and viewport size - void relayoutByPosition(int size); - void relayoutByGeometry(int height); - virtual void relayout(); - - int viewportWidth() const; - - int doLayout(int index, QList *sections); - - QTextLine lineForPosition(int pos, int *offsetInLine = 0, - int *lineIndex = 0, bool *lastLine = 0) const; - QTextLayout *layoutForPosition(int pos, int *offset = 0, int *index = 0) const; - - int textPositionAt(const QPoint &pos) const; - inline int bufferOffset() const { return viewportPosition - bufferPosition; } - - QString dump() const; - - enum Direction { - Forward = 0, - Backward = TextPagerDocument::FindBackward - }; - void updateViewportPosition(int pos, Direction direction,bool applyIt=true); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchHighlighter.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchHighlighter.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchHighlighter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchHighlighter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,145 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TextPagerSearchHighlighter.hpp" - -#include "VConfig.hpp" -#include "VProperty.hpp" - -#include - -QColor TextPagerSearchHighlighter::bgColour_=QColor(200, 255, 200); - -TextPagerSearchHighlighter::TextPagerSearchHighlighter(QObject *parent) : - SyntaxHighlighter(parent), - mode_(NoMode), - caseSensitive_(false), - wholeWords_(false) - { - if(VProperty *p=VConfig::instance()->find("panel.search.highlightColour")) - { - bgColour_=p->value().value(); - } - - format_.setBackground(Qt::yellow); - - rx_=QRegExp("(server)"); - } - -//from QRegExp -bool TextPagerSearchHighlighter::isWordCharacter(const QChar& ch) const -{ - return ch.isLetterOrNumber() || ch.isMark() || ch == QLatin1Char('_'); -} - -void TextPagerSearchHighlighter::highlightBlock(const QString &string) -{ - if(string.simplified().isEmpty()) - return; - - if(mode_ == RegexpMode) - { - if(rx_.isEmpty()) return; - int index=0; - while((index = rx_.indexIn(string, index)) != -1) { - - if(rx_.matchedLength() == 0) - return; - setFormat(index, rx_.matchedLength(), format_); - index+=rx_.matchedLength(); - } - } - else if(mode_ == TextMode) - { - if(text_.isEmpty()) return; - int index=0; - while((index = string.indexOf(text_,index, - caseSensitive_?Qt::CaseSensitive:Qt::CaseInsensitive)) != -1) - { - bool found=true; - if(wholeWords_) - { - if(index>0) - found=!isWordCharacter(string.at(index-1)); - if(found && index + text_.size() < string.size()) - found=!isWordCharacter(string.at(index+text_.size())); - } - if(found) - setFormat(index, text_.size(), format_); - - index+=string.size(); - } - } -} - -void TextPagerSearchHighlighter::reset(QString txt,TextPagerDocument::FindMode mode,bool apply) -{ - bool changed=false; - if(mode_ != TextMode) - { - mode_=TextMode; - rx_=QRegExp(); - changed=true; - } - - if(txt != text_) - { - text_=txt; - changed=true; - } - - bool cs=mode & TextPagerDocument::FindCaseSensitively; - if(cs != caseSensitive_) - { - caseSensitive_=cs; - changed=true; - } - - bool ww=mode & TextPagerDocument::FindWholeWords; - if(ww != wholeWords_) - { - wholeWords_=ww; - changed=true; - } - - if(changed && apply) - rehighlight(); - -} - -void TextPagerSearchHighlighter::reset(QRegExp rx,TextPagerDocument::FindMode mode,bool apply) -{ - bool changed=false; - if(mode_ != RegexpMode) - { - mode_=RegexpMode; - text_.clear(); - changed=true; - } - - if(rx_.pattern() != rx.pattern() || rx_.caseSensitivity() != rx.caseSensitivity() || - rx_.patternSyntax() != rx.patternSyntax()) - { - rx_=rx; - changed=true; - } - - if(changed && apply) - rehighlight(); -} - -void TextPagerSearchHighlighter::clear() -{ - rx_=QRegExp(); - text_.clear(); - rehighlight(); -} - - diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchHighlighter.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchHighlighter.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchHighlighter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchHighlighter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,43 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ -#define VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ - -#include "syntaxhighlighter.hpp" - -#include "TextPagerDocument.hpp" - -#include -#include - -class TextPagerSearchHighlighter : public SyntaxHighlighter -{ -public: - TextPagerSearchHighlighter(QObject *parent=0); - virtual void highlightBlock(const QString &string); - void reset(QString txt,TextPagerDocument::FindMode mode,bool apply); - void reset(QRegExp rx,TextPagerDocument::FindMode mode, bool apply); - void clear(); - enum Mode {NoMode,TextMode,RegexpMode}; - -protected: - bool isWordCharacter(const QChar& ch) const; - - Mode mode_; - QRegExp rx_; - QString text_; - QTextCharFormat format_; - bool caseSensitive_; - bool wholeWords_; - static QColor bgColour_; -}; - -#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHHIGHLIGHTER_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchInterface.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchInterface.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchInterface.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchInterface.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,252 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#include - -#include "TextPager/TextPagerSearchInterface.hpp" - -#include "TextPagerEdit.hpp" -#include "UserMessage.hpp" - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#endif - -TextPagerCursor::MoveOperation TextPagerSearchInterface::translateCursorMoveOp(QTextCursor::MoveOperation move) -{ - switch (move) - { - case QTextCursor::NoMove: return TextPagerCursor::NoMove; - case QTextCursor::Start: return TextPagerCursor::Start; - case QTextCursor::StartOfLine: return TextPagerCursor::StartOfLine; - case QTextCursor::StartOfWord: return TextPagerCursor::StartOfWord; - case QTextCursor::PreviousWord: return TextPagerCursor::PreviousWord; - case QTextCursor::End: return TextPagerCursor::End; - default: - { - assert(0); - return TextPagerCursor::NoMove; - } - } -} - - -bool TextPagerSearchInterface::findString (QString str, bool highlightAll, QTextDocument::FindFlags flags, - QTextCursor::MoveOperation move, int iteration, StringMatchMode::Mode matchMode) -{ - if(!editor_) - return false; - - if(editor_->document()->documentSize() == 0) - return false; - - bool doSearch=true; - if(str.simplified().isEmpty()) - { - doSearch=false; - } - - TextPagerCursor cursor(editor_->textCursor()); - - cursor.movePosition(translateCursorMoveOp(move)); // move the cursor? - - TextPagerDocument::FindMode mode=TextPagerDocument::FindWrap; - - if(flags & QTextDocument::FindCaseSensitively) - mode |= TextPagerDocument::FindCaseSensitively; - - if(flags & QTextDocument::FindBackward) - mode |= TextPagerDocument::FindBackward; - - if(flags & QTextDocument::FindWholeWords) - mode |= TextPagerDocument::FindWholeWords; - - bool found = false; - - Qt::CaseSensitivity cs = (flags & QTextDocument::FindCaseSensitively) ? Qt::CaseSensitive : Qt::CaseInsensitive; - - if(doSearch) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - } - - switch (matchMode) - { - case StringMatchMode::ContainsMatch: - { - editor_->setSearchHighlighter(str,mode); - - if(doSearch) - { - cursor = editor_->document()->find(str, cursor, mode); // perform the search - found = (!cursor.isNull()); - } - break; - } - case StringMatchMode::WildcardMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - regexp.setPatternSyntax(QRegExp::Wildcard); - - editor_->setSearchHighlighter(regexp,mode); - - if(doSearch) - { - cursor = editor_->document()->find(regexp, cursor, mode); // perform the search } - found = (!cursor.isNull()); - } - break; - } - case StringMatchMode::RegexpMatch: - { - QRegExp regexp(str); - regexp.setCaseSensitivity(cs); - - editor_->setSearchHighlighter(regexp,mode); - - if(doSearch) - { - cursor = editor_->document()->find(regexp, cursor, mode); // perform the search - found = (!cursor.isNull()); - } - break; - } - - default: - break; - } - - if(found) - { - editor_->setTextCursor(cursor); // mark the selection of the match - editor_->ensureCursorVisible(); - cursor.movePosition(TextPagerCursor::StartOfLine); - } - - if(doSearch) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - } - - return found; -} - -void TextPagerSearchInterface::automaticSearchForKeywords(bool userClickedReload) -{ - if(editor_->document()->documentSize() ==0) - return; - - bool performSearch = vpPerformAutomaticSearch_->value().toBool(); - - if (performSearch) - { - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - - // search direction - QTextDocument::FindFlags findFlags; - TextPagerCursor cursor(editor_->textCursor()); - std::string searchFrom = vpAutomaticSearchFrom_->valueAsStdString(); - QTextCursor::MoveOperation move; - - if (searchFrom == "bottom") - { - findFlags = QTextDocument::FindBackward; - move = QTextCursor::End; - } - else - { - move = QTextCursor::Start; - } - - // case sensitivity - bool caseSensitive = vpAutomaticSearchCase_->value().toBool(); - if (caseSensitive) - findFlags = findFlags | QTextDocument::FindCaseSensitively; - - // string match mode - std::string matchMode(vpAutomaticSearchMode_->valueAsStdString()); - StringMatchMode::Mode mode = StringMatchMode::operToMode(matchMode); - - // the term to be searched for - std::string searchTerm_s(vpAutomaticSearchText_->valueAsStdString()); - QString searchTerm = QString::fromStdString(searchTerm_s); - - // perform the search - bool found = findString (searchTerm, false, findFlags, move, 1, mode); - - if(!found) - { - if(userClickedReload) - { - // move the cursor to the start of the last line - gotoLastLine(); - } - } - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - - } - else - { - // move the cursor to the start of the last line - gotoLastLine(); - } -} - -void TextPagerSearchInterface::refreshSearch() -{ - if(!editor_) - return; - - TextPagerCursor cursor(editor_->textCursor()); - if (cursor.hasSelection()) - { - cursor.movePosition(TextPagerCursor::StartOfLine, TextPagerCursor::MoveAnchor); - editor_->setTextCursor(cursor); - } -} - - -void TextPagerSearchInterface::clearHighlights() -{ - if(editor_) - editor_->clearSearchHighlighter(); -} - -void TextPagerSearchInterface::enableHighlights() -{ - if(editor_) - editor_->setEnableSearchHighlighter(true); -} - -void TextPagerSearchInterface::disableHighlights() -{ - if(editor_) - editor_->setEnableSearchHighlighter(false); -} - -void TextPagerSearchInterface::gotoLastLine() -{ - // move the cursor to the start of the last line - TextPagerCursor cursor = editor_->textCursor(); - cursor.movePosition(TextPagerCursor::End); - cursor.movePosition(TextPagerCursor::StartOfLine); - editor_->setTextCursor(cursor); - editor_->ensureCursorVisible(); -} diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchInterface.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchInterface.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSearchInterface.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSearchInterface.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ -#define VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ - -#include "AbstractTextEditSearchInterface.hpp" -#include "TextPagerCursor.hpp" - -class TextPagerEdit; - -class TextPagerSearchInterface : public AbstractTextEditSearchInterface -{ -public: - TextPagerSearchInterface() : editor_(NULL) {} - void setEditor(TextPagerEdit* e) {editor_=e;} - - bool findString (QString str, bool highlightAll, QTextDocument::FindFlags findFlags, - QTextCursor::MoveOperation move, int iteration,StringMatchMode::Mode matchMode); - - void automaticSearchForKeywords(bool); - void refreshSearch(); - void clearHighlights(); - void disableHighlights(); - void enableHighlights(); - bool highlightsNeedSearch() {return false;} - void gotoLastLine(); - -protected: - TextPagerCursor::MoveOperation translateCursorMoveOp(QTextCursor::MoveOperation move); - TextPagerEdit *editor_; - -}; - -#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERSEARCHINTERFACE_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "TextPagerSection.hpp" -#include "TextPagerDocument.hpp" -#include "TextPagerDocument_p.hpp" - -TextPagerSection::~TextPagerSection() -{ - if (d.document) - d.document->takeTextSection(this); -} - -QString TextPagerSection::text() const -{ - Q_ASSERT(d.document); - return d.document->read(d.position, d.size); -} - -void TextPagerSection::setFormat(const QTextCharFormat &format) -{ - Q_ASSERT(d.document); - d.format = format; - Q_EMIT d.document->d->sectionFormatChanged(this); -} - -QCursor TextPagerSection::cursor() const -{ - return d.cursor; -} - -void TextPagerSection::setCursor(const QCursor &cursor) -{ - d.cursor = cursor; - d.hasCursor = true; - Q_EMIT d.document->d->sectionCursorChanged(this); -} - -void TextPagerSection::resetCursor() -{ - d.hasCursor = false; - d.cursor = QCursor(); - Q_EMIT d.document->d->sectionCursorChanged(this); -} - -bool TextPagerSection::hasCursor() const -{ - return d.hasCursor; -} - -void TextPagerSection::setPriority(int priority) -{ - d.priority = priority; - Q_EMIT d.document->d->sectionFormatChanged(this); // ### it hasn't really but I to need it dirtied -} diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,75 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERSECTION_HPP__ -#define TEXTPAGERSECTION_HPP__ - -#include -#include -#include -#include - -class TextPagerDocument; -class TextPagerEdit; -class TextPagerSection -{ -public: - enum TextSectionOption { - IncludePartial = 0x01 - }; - Q_DECLARE_FLAGS(TextSectionOptions, TextSectionOption); - - ~TextPagerSection(); - QString text() const; - int position() const { return d.position; } - int size() const { return d.size; } - QTextCharFormat format() const { return d.format; } - void setFormat(const QTextCharFormat &format); - QVariant data() const { return d.data; } - void setData(const QVariant &data) { d.data = data; } - TextPagerDocument *document() const { return d.document; } - TextPagerEdit *textEdit() const { return d.textEdit; } - QCursor cursor() const; - void setCursor(const QCursor &cursor); - void resetCursor(); - bool hasCursor() const; - int priority() const { return d.priority; } - void setPriority(int priority); -private: - struct Data { - Data(int p, int s, TextPagerDocument *doc, const QTextCharFormat &f, const QVariant &d) - : position(p), size(s), priority(0), document(doc), textEdit(0), format(f), data(d), hasCursor(false) - {} - int position, size, priority; - TextPagerDocument *document; - TextPagerEdit *textEdit; - QTextCharFormat format; - QVariant data; - QCursor cursor; - bool hasCursor; - } d; - - TextPagerSection(int pos, int size, TextPagerDocument *doc, const QTextCharFormat &format, const QVariant &data) - : d(pos, size, doc, format, data) - {} - - friend class TextPagerDocument; - friend class TextDocumentPrivate; - friend class TextPagerEdit; -}; - -Q_DECLARE_OPERATORS_FOR_FLAGS(TextPagerSection::TextSectionOptions); - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection_p.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection_p.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerSection_p.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerSection_p.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,34 +0,0 @@ -// Copyright 2010 Anders Bakken -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef TEXTPAGERSECTION_P_HPP_ -#define TEXTPAGERSECTION_P_HPP_ - -#include -#include -class TextPagerSection; -class TextSectionManager : public QObject -{ - Q_OBJECT -public: - static TextSectionManager *instance() { static TextSectionManager *inst = new TextSectionManager; return inst; } -Q_SIGNALS: - void sectionFormatChanged(TextPagerSection *section); - void sectionCursorChanged(TextPagerSection *section); -private: - TextSectionManager() : QObject(QCoreApplication::instance()) {} - friend class TextPagerSection; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerWidget.cpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerWidget.cpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "TextPagerWidget.hpp" - -#include "GotoLineDialog.hpp" - -bool add = false; - -TextPagerWidget::TextPagerWidget(QWidget *parent) : - //TextPagerEdit(parent), - doLineNumbers(true), - gotoLineDialog_(NULL) -{ - QHBoxLayout* hb=new QHBoxLayout(this); - hb->setContentsMargins(0,0,0,0); - hb->setSpacing(0); - - textEditor_=new TextPagerEdit(this); - lineNumArea_ = new TextPagerLineNumberArea(textEditor_); - - hb->addWidget(lineNumArea_); - hb->addWidget(textEditor_,1); - - setAttribute(Qt::WA_MouseTracking); -} - -void TextPagerWidget::clear() -{ - textEditor_->document()->clear(); -} - -bool TextPagerWidget::load(const QString &fileName, TextPagerDocument::DeviceMode mode) -{ - return textEditor_->load(fileName, mode, NULL); -} - -void TextPagerWidget::setText(const QString &txt) -{ - textEditor_->setText(txt); -} - -void TextPagerWidget::setFontProperty(VProperty* p) -{ - textEditor_->setFontProperty(p); -} - -void TextPagerWidget::zoomIn() -{ - textEditor_->zoomIn(); -} - -void TextPagerWidget::zoomOut() -{ - textEditor_->zoomOut(); -} -// --------------------------------------------------------------------------- -// TextEdit::gotoLine -// triggered when the user asks to bring up the 'go to line' dialog -// --------------------------------------------------------------------------- - -void TextPagerWidget::gotoLine() -{ - // create the dialog if it does not already exist - - if (!gotoLineDialog_) - { - gotoLineDialog_ = new GotoLineDialog(this); - - connect(gotoLineDialog_, SIGNAL(gotoLine(int)), this, SLOT(gotoLine(int))); - } - - // if created, set it up and display it - - if (gotoLineDialog_) - { - gotoLineDialog_->show(); - gotoLineDialog_->raise(); - gotoLineDialog_->activateWindow(); - gotoLineDialog_->setupUIBeforeShow(); - } -} - -void TextPagerWidget::gotoLine(int line) -{ - textEditor_->gotoLine(line); -} diff -Nru ecflow-4.9.0/Viewer/src/TextPager/TextPagerWidget.hpp ecflow-4.11.1/Viewer/src/TextPager/TextPagerWidget.hpp --- ecflow-4.9.0/Viewer/src/TextPager/TextPagerWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/TextPagerWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,53 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_TEXTPAGERWIDGET_HPP_ -#define VIEWER_SRC_TEXTPAGERWIDGET_HPP_ - -#include "TextPagerEdit.hpp" - -class GotoLineDialog; - -class TextPagerWidget : public QWidget //TextPagerEdit -{ - Q_OBJECT -public: - TextPagerWidget(QWidget *parent = 0); - - TextPagerEdit* textEditor() const {return textEditor_;} - void clear(); - bool load(const QString &fileName, TextPagerDocument::DeviceMode mode = TextPagerDocument::Sparse); - void setText(const QString& txt); - - void setFontProperty(VProperty* p); - void zoomIn(); - void zoomOut(); - void gotoLine(); - - //void mouseMoveEvent(QMouseEvent *e); - //void timerEvent(QTimerEvent *e); -protected Q_SLOTS: - void gotoLine(int); - -Q_SIGNALS: - void cursorCharacter(const QChar &ch); - -private: - - bool doLineNumbers; - QBasicTimer appendTimer, changeSelectionTimer; - TextPagerEdit *textEditor_; - - TextPagerLineNumberArea *lineNumArea_; - GotoLineDialog *gotoLineDialog_; -}; - - -#endif /* VIEWER_SRC_TEXTPAGER_TEXTPAGERWIDGET_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TextPager/WeakPointer.hpp ecflow-4.11.1/Viewer/src/TextPager/WeakPointer.hpp --- ecflow-4.9.0/Viewer/src/TextPager/WeakPointer.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TextPager/WeakPointer.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -#ifndef WEAKPOINTER_H -#define WEAKPOINTER_H - -#include - -#if (QT_VERSION < QT_VERSION_CHECK(4, 6, 0)) -// while QWeakPointer existed in Qt 4.5 it lacked certain APIs (like -// data so I'll stick with QPointer until 4.6) -#include -#define WeakPointer QPointer -#else -#include -#define WeakPointer QWeakPointer -#endif - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TimeItemWidget.cpp ecflow-4.11.1/Viewer/src/TimeItemWidget.cpp --- ecflow-4.9.0/Viewer/src/TimeItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TimeItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "LineEdit.hpp" - -#include "TimeItemWidget.hpp" - -#include "Node.hpp" - -//======================================================== -// -// TimeItemWidget -// -//======================================================== - -TimeItemWidget::TimeItemWidget(QWidget *parent) : QWidget(parent) -{ - setupUi(this); - -} - -QWidget* TimeItemWidget::realWidget() -{ - return this; -} - -void TimeItemWidget::reload(VInfo_ptr nodeInfo) -{ - active_=true; -} - -void TimeItemWidget::clearContents() -{ - InfoPanelItem::clear(); -} - - -static InfoPanelItemMaker maker1("time"); diff -Nru ecflow-4.9.0/Viewer/src/TimeItemWidget.hpp ecflow-4.11.1/Viewer/src/TimeItemWidget.hpp --- ecflow-4.9.0/Viewer/src/TimeItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TimeItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TIMEITEMWIDGET_HPP_ -#define TIMEITEMWIDGET_HPP_ -#include "ui_TimeItemWidget.h" - -#include - -#include "InfoPanelItem.hpp" -#include "VInfo.hpp" - -class LineEdit; - -class TimeItemWidget - : public QWidget - , public InfoPanelItem - , protected Ui::TimeItemWidget - -{ -public: - explicit TimeItemWidget(QWidget *parent=0); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - void nodeChanged(const VNode*, const std::vector&) {} - void defsChanged(const std::vector&) {} - - QGraphicsView* view() { return graphicsView; } - -protected: - void updateState(const ChangeFlags&) {} -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/TimeItemWidget.ui ecflow-4.11.1/Viewer/src/TimeItemWidget.ui --- ecflow-4.9.0/Viewer/src/TimeItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TimeItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,206 +0,0 @@ - - - TimeItemWidget - - - - 0 - 0 - 859 - 621 - - - - Form - - - - - - 0 - - - - - %ECF_LOG% - - - - - - - All - - - filterGroup - - - - - - - Tasks - - - true - - - filterGroup - - - - - - - TimeSort - - - true - - - sortGroup - - - - - - - NameSort - - - sortGroup - - - - - - - - 2015 - 1 - 1 - - - - - - - - - 2100 - 1 - 1 - - - - - - - - - 16 - 16 - - - - - - - - - - M - - - - - - - - 16 - 16 - - - - - - - - - - L - - - - - - - - 16 - 16 - - - - - - - - - - U - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - View options - - - Detached - - - - :/viewer/images/configure.svg:/viewer/images/configure.svg - - - true - - - true - - - - - - - - - - - - - - - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeModel.cpp ecflow-4.11.1/Viewer/src/TreeNodeModel.cpp --- ecflow-4.9.0/Viewer/src/TreeNodeModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1388 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TreeNodeModel.hpp" - -#include - -#include "ConnectState.hpp" -#include "VFilter.hpp" -#include "ServerFilter.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "VFilter.hpp" -#include "VNState.hpp" -#include "VSState.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" -#include "VIcon.hpp" -#include "VFileInfo.hpp" -#include "VModelData.hpp" -#include "VTree.hpp" - -//#define _UI_TREENODEMODEL_DEBUG - -//======================================= -// -// TreeNodeModel -// -//======================================= - -TreeNodeModel::TreeNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef, - AttributeFilter *atts,IconFilter* icons,QObject *parent) : - AbstractNodeModel(parent), - data_(0), - atts_(atts), - icons_(icons), - serverToolTip_(true), - nodeToolTip_(true), - attributeToolTip_(true) -{ - //Create the data handler for the tree model. - data_=new VTreeModelData(filterDef,atts_,this); - - //Reset the data handler - data_->reset(serverFilter); - - //Icon filter changes - connect(icons_,SIGNAL(changed()), - this,SIGNAL(rerender())); - -} - -VModelData* TreeNodeModel::data() const -{ - return data_; -} - - -int TreeNodeModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 1; -} - -int TreeNodeModel::rowCount( const QModelIndex& parent) const -{ - //There is no data at all - if(!hasData()) - { - return 0; - } - //We use only column 0 - else if(parent.column() > 0) - { - return 0; - } - //"parent" is the root - else if(!parent.isValid()) - { - return data_->count(); - } - //"parent" is a server - else if(isServer(parent)) - { - if(VTreeServer *server=indexToServer(parent)) - { - if(server->inScan()) - return 0; - else - return server->tree()->attrNum(atts_)+server->tree()->numOfChildren(); - } - } - //"parent" is a node - else if(VTreeNode* parentNode=indexToNode(parent)) - { - return parentNode->attrNum(atts_)+parentNode->numOfChildren(); - } - - return 0; -} - -Qt::ItemFlags TreeNodeModel::flags ( const QModelIndex & index) const -{ - Qt::ItemFlags defaultFlags; - - defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; - - return defaultFlags; -} - -//This function can be called billions of times, so it has to be extremely fast. -QVariant TreeNodeModel::data(const QModelIndex& index, int role ) const -{ - //Data lookup can be costly so we only enter the function for the valid cases - if(index.isValid() && (role > Qt::UserRole || role == Qt::DisplayRole || - role == Qt::ToolTipRole || role == Qt::BackgroundRole || - role == Qt::ForegroundRole)) - { - //At this point we know that the index is valid so we do not need to - //check it anymore. - - //This role is called millions of times when expanding or - //relayouting huge trees. So has to be HIGHLY optimised!!!!! - if(role == AttributeLineRole) - { - if(VTreeNode* node=indexToAttrParentNode(index)) - { - VNode *vnode=node->vnode(); - Q_ASSERT(vnode); - if(VAttribute* a=vnode->attribute(index.row(),atts_)) - { - return a->lineNum(); - } - } - return 0; - } - - //Identifies server - else if(role == ServerRole) - { - if(isServerForValid(index)) - return 0; - else - return -1; - } - - else if(role == AttributeRole) - { - return isAttribute(index); - } - - //Server - else if(isServerForValid(index)) - { - return serverData(index,role); - } - - //If we are here we can be sure that the index in not a server! - - //We only continue for the relevant roles for nodes and attributes - if(role == InfoRole || role == LoadRole) - { - return QVariant(); - } - - bool itIsANode=false; - if(VTreeNode* node=indexToAttrParentOrNode(index,itIsANode)) - { - //Attribute - if(itIsANode ==false) - { - return attributesData(index,role,node); - } - - //Node - if(itIsANode) - { - return nodeData(index,role,node); - } - } - } - - return QVariant(); -} - -QVariant TreeNodeModel::serverData(const QModelIndex& index,int role) const -{ - if(role == FilterRole) - return true; - - if(role == Qt::ToolTipRole && !serverToolTip_) - return QVariant(); - - ServerHandler *server=indexToServerHandler(index); - if(!server) - return QVariant(); - - if(index.column() == 0) - { - if(role == ServerPointerRole) - return qVariantFromValue((void *) server); - - //The colour of the server node - if(role == ConnectionRole) - return (server->connectState()->state() == ConnectState::Lost)?0:1; - - //The colour of the server node - else if(role == Qt::BackgroundRole) - return server->vRoot()->stateColour(); - - //The font colour of the server node - else if(role == Qt::ForegroundRole) - return server->vRoot()->stateFontColour(); - - //The text - else if(role == Qt::DisplayRole) - return QString::fromStdString(server->name()); - - //The number of nodes the server has - else if(role == NodeNumRole) - { - if(server->activity() != ServerHandler::LoadActivity) - { - return server->vRoot()->totalNum(); - } - return QVariant(); - } - - //Extra information about the server activity - else if(role == InfoRole) - { - switch(server->activity()) - { - case ServerHandler::LoadActivity: - return "Loading ..."; - default: - return ""; - } - } - - else if(role == LoadRole) - return (server->activity() == ServerHandler::LoadActivity); - - else if(role == IconRole) - { - if(icons_->isEmpty()) - return QVariant(); - else - return VIcon::pixmapList(server->vRoot(),icons_); - } - - //Tooltip - else if(role == Qt::ToolTipRole) - { - QString txt=server->vRoot()->toolTip(); - txt+=VIcon::toolTip(server->vRoot(),icons_); - return txt; - } - } - - return QVariant(); -} - -QVariant TreeNodeModel::nodeData(const QModelIndex& index, int role,VTreeNode* tnode) const -{ - if(role == Qt::ToolTipRole && !nodeToolTip_) - return QVariant(); - - if(!tnode) - return QVariant(); - - VNode* vnode=tnode->vnode(); - if(!vnode || !vnode->node()) - return QVariant(); - - if(role == NodePointerRole) - return qVariantFromValue((void *) vnode); - - else if(role == ConnectionRole) - { - return (vnode->server()->connectState()->state() == ConnectState::Lost)?0:1; - } - - else if(role == Qt::DisplayRole) - return vnode->name(); - - else if(role == Qt::BackgroundRole) - { - if(vnode->isSuspended()) - { - QVariantList lst; - lst << vnode->stateColour() << vnode->realStateColour(); - return lst; - } - else - return vnode->stateColour() ; - } - - else if(role == Qt::ForegroundRole) - return vnode->stateFontColour(); - - else if(role == NodeTypeRole) - { - if(vnode->isTask()) return 2; - else if(vnode->isSuite()) return 0; - else if(vnode->isFamily()) return 1; - else if(vnode->isAlias()) return 3; - return 0; - } - else if(role == NodeTypeForegroundRole) - { - return vnode->typeFontColour(); - } - else if(role == IconRole) - { - if(icons_->isEmpty()) - return QVariant(); - else - return VIcon::pixmapList(vnode,icons_); - } - else if(role == Qt::ToolTipRole) - { - QString txt=vnode->toolTip(); - txt+=VIcon::toolTip(vnode,icons_); - return txt; - } - - //The number of nodes a suite has - else if(role == NodeNumRole) - { - if(vnode->isTopLevel()) - { - if(data_->isFilterComplete()) - return vnode->server()->vRoot()->totalNumOfTopLevel(vnode); - else - return QString::number(tnode->root()->totalNumOfTopLevel(tnode)) + "/" + - QString::number(vnode->server()->vRoot()->totalNumOfTopLevel(vnode)); - } - return QVariant(); - } - - //The number of nodes a suite has - else if(role == AbortedReasonRole && vnode->isAborted()) - { - return QString::fromStdString(vnode->abortedReason()); - } - - return QVariant(); -} - -//======================================================================= -// -// Attributes data -// -//======================================================================= - -QVariant TreeNodeModel::attributesData(const QModelIndex& index, int role,VTreeNode* tnode) const -{ - if(role == Qt::ToolTipRole && !attributeToolTip_) - return QVariant(); - - if(role == Qt::BackgroundRole) - return QColor(220,220,220); - - VNode *vnode=tnode->vnode(); - Q_ASSERT(vnode); - - if(role == ConnectionRole) - { - return (vnode->server()->connectState()->state() == ConnectState::Lost)?0:1; - } - else if(role == Qt::DisplayRole) - { - if(VAttribute* a=vnode->attribute(index.row(),atts_)) - return a->data(); - else - return QStringList(); - } - else if(role == Qt::ToolTipRole) - { - if(VAttribute* a=vnode->attribute(index.row(),atts_)) - return a->toolTip(); - else - return QString(); - } - - return QVariant(); -} - -QVariant TreeNodeModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - //No header!!! - return QVariant(); -} - -QModelIndex TreeNodeModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When "parent" is the root this index refers to a server - if(!parent.isValid()) - { - //For the server the internal pointer is NULL - if(row < data_->count()) - { - return createIndex(row,column,(void*)NULL); - } - } - - //Here we must be under one of the servers - else - { - //If "parent" is a server this index refers to a topLevel node (suite). - //We set the server as an internal pointer - if(VTreeServer* server=indexToServer(parent)) - { - return createIndex(row,column,server); - } - - //If "parent" is not a server it must be a tree node. The internal pointer is the parent tree node. - else if(VTreeNode* parentNode=indexToNode(parent)) - { - return createIndex(row,column,parentNode); - } - } - return QModelIndex(); -} - -QModelIndex TreeNodeModel::parent(const QModelIndex &child) const -{ - //If "child" is a server the parent is the root - if(isServer(child)) - return QModelIndex(); - - int row=-1; - - //If the "child"'s internal pointer is a server it can be a server attribute or a topLevel node (suite) - //and the parent is this server. - if((row=data_->indexOfServer(child.internalPointer())) != -1) - { - return createIndex(row,0,(void*)NULL); - } - - //The "child" cannot be a server attribute or a topLevel node so it must be a node or an attribute. - //Its internal pointer must point to the parent node. - else if(VTreeNode *parentNode=static_cast(child.internalPointer())) - { - //The parent is a topLevel node (suite): its internal pointer is the server - if(parentNode->isTopLevel()) - { - VTreeNode* root=parentNode->parent(); - Q_ASSERT(root); - row=root->indexOfChild(parentNode); - Q_ASSERT(row >=0); - VTreeServer *ts=root->server(); - - int serverAttrNum=root->attrNum(atts_); - return createIndex(serverAttrNum+row,0,ts); - } - //The parent is a non topLevel node (non-suite): its internal pointer - //is its parent (i.e.. the grandparent) - else if(VTreeNode *grandParentNode=parentNode->parent()) - { - int num=grandParentNode->attrNum(atts_)+grandParentNode->indexOfChild(parentNode); - return createIndex(num,0,grandParentNode); - } - } - - return QModelIndex(); -} - -//---------------------------------------------- -// -// Server to index mapping and lookup -// -//---------------------------------------------- - -bool TreeNodeModel::isServer(const QModelIndex & index) const -{ - //For the servers the internal pointer is NULL - return (index.isValid() && index.internalPointer() == 0); -} - -//This has to be extrememly fast! -//When we know that the index is valid. -bool TreeNodeModel::isServerForValid(const QModelIndex & index) const -{ - //For the servers the internal pointer is NULL - return index.internalPointer() == 0; -} - -bool TreeNodeModel::isNode(const QModelIndex & index) const -{ - return (indexToNode(index) != NULL); -} - -bool TreeNodeModel::isAttribute(const QModelIndex & index) const -{ - return (index.isValid() && !isServer(index) && !isNode(index)); -} - -ServerHandler* TreeNodeModel::indexToServerHandler(const QModelIndex & index) const -{ - //For servers the internal id is a null pointer - if(index.isValid()) - { - if(index.internalPointer() == NULL) - return data_->serverHandler(index.row()); - } - - return NULL; -} - -VTreeServer* TreeNodeModel::indexToServer(const QModelIndex & index) const -{ - //For servers the internal id is a null pointer - if(index.isValid()) - { - if(index.internalPointer() == NULL) - return data_->server(index.row())->treeServer(); - } - - return NULL; -} - -VTreeServer* TreeNodeModel::nameToServer(const std::string& name) const -{ - VModelServer* ms=data_->server(name); - return (ms)?ms->treeServer():NULL; -} - -QModelIndex TreeNodeModel::serverToIndex(ServerHandler* server) const -{ - //For servers the internal id is set to their position in servers_ + 1 - int i; - if((i=data_->indexOfServer(server))!= -1) - return createIndex(i,0,(void*)NULL); - - return QModelIndex(); -} - -QModelIndex TreeNodeModel::serverToIndex(VModelServer* server) const -{ - //For servers the internal id is set to their position in servers_ + 1 - int i; - if((i=data_->indexOfServer(server))!= -1) - return createIndex(i,0,(void*)NULL); - - return QModelIndex(); -} - -//---------------------------------------------- -// -// Node to index mapping and lookup -// -//---------------------------------------------- - -//We can only call it when the index is valid! -VTreeNode* TreeNodeModel::indexToAttrParentNode(const QModelIndex & index) const -{ - void *ip; - if((ip=index.internalPointer()) == NULL) - return 0; - - //If it is not a sever ... - - //If the internal pointer is a server it is either a server attribute or a - //top level node (suite) - - if(VModelServer *mserver=data_->server(ip)) - { - VTreeServer* server=mserver->treeServer(); - //It is an attribute - if(index.row() < server->tree()->attrNum(atts_)) - { - return server->tree(); - } - } - - //Otherwise the internal pointer points to the parent node. - else if(VTreeNode *parentNode=static_cast(ip)) - { - //It is an attribute - if(index.row() < parentNode->attrNum(atts_)) - return parentNode; - } - - return 0; -} - -//We can only call it when the index is valid! -VTreeNode* TreeNodeModel::indexToAttrParentOrNode(const QModelIndex & index,bool &itIsANode) const -{ - void *ip; - if((ip=index.internalPointer()) == NULL) - return 0; - - //If it is not a sever ... - - itIsANode=false; - //If the internal pointer is a server it is either a server attribute or a - //top level node (suite) - if(VModelServer *mserver=data_->server(ip)) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - - //It is an attribute - int serverAttNum=server->tree()->attrNum(atts_); - - if(index.row() < serverAttNum) - { - return server->tree(); - } - //It is a top level node - else - { - itIsANode=true; - return server->tree()->childAt(index.row()-serverAttNum); - } - } - - //Otherwise the internal pointer points to the parent node. - else if(VTreeNode *parentNode=static_cast(ip)) - { - int attNum=parentNode->attrNum(atts_); - - //It is an attribute - if(index.row() < attNum) - { - return parentNode; - } - else - { - itIsANode=true; - return parentNode->childAt(index.row()-attNum); - } - } - - return 0; -} - -VTreeNode* TreeNodeModel::indexToServerOrNode( const QModelIndex & index) const -{ - VTreeNode* node=indexToNode(index); - if(!node) - { - if(VTreeServer* ts=indexToServer(index)) - node=ts->tree(); - } - - return node; -} - -VTreeNode* TreeNodeModel::indexToNode( const QModelIndex & index) const -{ - //If it is not a sever ... - if(index.isValid() && !isServer(index)) - { - //If the internal pointer is a server it is either a server attribute or a - //top level node (suite) - - if(VModelServer *mserver=data_->server(index.internalPointer())) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - - int serverAttNum=server->tree()->attrNum(atts_); - - //It is an attribute - if(index.row() < serverAttNum) - { - return NULL; - } - //It is a top level node - else - { - return server->tree()->childAt(index.row()-serverAttNum); - } - } - - //Otherwise the internal pointer points to the parent node. - else if(VTreeNode *parentNode=static_cast(index.internalPointer())) - { - int attNum=parentNode->attrNum(atts_); - if(index.row() >= attNum) - { - return parentNode->childAt(index.row()-attNum); - } - } - } - - return NULL; -} - -//Find the index for the node! The VNode can be a server as well!!! -QModelIndex TreeNodeModel::nodeToIndex(const VNode* node, int column) const -{ - if(!node) - return QModelIndex(); - - //This is a server!!! - if(node->isServer()) - { - return serverToIndex(node->server()); - } - //If the node is toplevel node (suite). - else if(node->isTopLevel()) - { - if(VModelServer *mserver=data_->server(node->server())) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - - //the node is displayed in the tree - if(VTreeNode* tn=server->tree()->find(node)) - { - int row=tn->indexInParent(); - Q_ASSERT(tn->parent() == server->tree()); - Q_ASSERT(row >=0); - row+=server->tree()->attrNum(atts_); - return createIndex(row,column,server); - } - } - } - - //Other nodes - else if(VNode *parentNode=node->parent()) - { - if(VModelServer *mserver=data_->server(node->server())) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - - if(VTreeNode* tn=server->tree()->find(node)) - { - VTreeNode* tnParent=tn->parent(); - Q_ASSERT(tnParent); - Q_ASSERT(tnParent->vnode() == parentNode); - int row=tnParent->indexOfChild(tn); - row+=tnParent->attrNum(atts_); - return createIndex(row,column,tnParent); - } - } - } - - return QModelIndex(); -} - -//Find the index for the node -QModelIndex TreeNodeModel::nodeToIndex(const VTreeNode* node, int column) const -{ - if(!node) - return QModelIndex(); - - //It is a server - if(node->parent() == 0) - { - VTreeServer *server=node->server(); - Q_ASSERT(server); - return serverToIndex(server); - } - else if(node->isTopLevel()) - { - VTree *vt=node->root(); - Q_ASSERT(vt); - Q_ASSERT(vt == node->parent()); - int row=vt->indexOfChild(node); - if(row != -1) - { - row+=vt->attrNum(atts_); - return createIndex(row,column,vt->server()); - } - - } - if(VTreeNode *parentNode=node->parent()) - { - int row=parentNode->indexOfChild(node); - if(row != -1) - { - row+=parentNode->attrNum(atts_); - return createIndex(row,column,parentNode); - } - } - - return QModelIndex(); - -} - -//Find the index for the node when we know what the server is! -QModelIndex TreeNodeModel::nodeToIndex(VTreeServer* server,const VTreeNode* node, int column) const -{ - if(!node) - return QModelIndex(); - - //If the node is toplevel node (suite). - if(node->isTopLevel()) - { - Q_ASSERT(node->parent() == server->tree()); - int row=server->tree()->indexOfChild(node); - Q_ASSERT(row >=0); - - row+=server->tree()->attrNum(atts_); - return createIndex(row,column,server); - } - //Other nodes - else if(VTreeNode *parentNode=node->parent()) - { - int row=parentNode->indexOfChild(node); - if(row != -1) - { - row+=parentNode->attrNum(atts_); - return createIndex(row,column,parentNode); - } - } - - return QModelIndex(); - -} - -//Find the index for the node! The VNode can be a server as well!!! -QModelIndex TreeNodeModel::attributeToIndex(const VAttribute* a, int column) const -{ - if(!a) - return QModelIndex(); - - VNode* node=a->parent(); - if(!node) - return QModelIndex(); - - VModelServer *mserver=data_->server(node->server()); - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - - int row=node->indexOfAttribute(a,atts_); - if(row != -1) - { - //This is a server!!! - if(node->isServer()) - { - return createIndex(row,column,server); - } - else - { - if(VTreeNode* tn=server->tree()->find(node)) - { - return createIndex(row,column,tn); - } - } - } - - return QModelIndex(); -} - -#if 0 -QModelIndex TreeNodeModel::forceShowNode(const VNode* node) const -{ - //There is nothing to do when there is no status filter set - if(data_->isFilterNull()) - return QModelIndex(); - - Q_ASSERT(node); - Q_ASSERT(!node->isServer()); - Q_ASSERT(node->server()); - - if(VModelServer *mserver=data_->server(node->server())) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - server->setForceShowNode(node); - return nodeToIndex(node); - } - - return QModelIndex(); -} - -QModelIndex TreeNodeModel::forceShowAttribute(const VAttribute* a) const -{ - VNode* node=a->parent(); - Q_ASSERT(node); - //Q_ASSERT(!node->isServer()); - Q_ASSERT(node->server()); - - if(VModelServer *mserver=data_->server(node->server())) - { - VTreeServer* server=mserver->treeServer(); - Q_ASSERT(server); - server->setForceShowAttribute(a); - return attributeToIndex(a); - } - - return QModelIndex(); -} - -#endif - -//Sets the object strored in info as the current forceShow object in the -//data. A forceShow object must always be part of the tree whatever state or -//atribute filter is set. There can be only one forceShow object at a time in the -//whole model/tree. -void TreeNodeModel::setForceShow(VInfo_ptr info) -{ - UI_FUNCTION_LOG - - if(info) - UiLog().dbg() << " info=" << info->path(); - - //Clear the forceShow object in all the server data objects. Only one of - //these is actually need to clear it. - for(int i=0; i < data_->count(); i++) - { - VTreeServer *ts=data_->server(i)->treeServer(); - Q_ASSERT(ts); - ts->clearForceShow(info->item()); - } - - //Sets the forceShow objects in all the server data objects. Only one of these will - //actually set the forceShow. - for(int i=0; i < data_->count(); i++) - { - VTreeServer *ts=data_->server(i)->treeServer(); - Q_ASSERT(ts); - ts->setForceShow(info->item()); - } -} - -//The selection changed in the view -void TreeNodeModel::selectionChanged(QModelIndexList lst) -{ - UI_FUNCTION_LOG - - //if(data_->isFilterNull()) - // return; - - if(lst.count() > 0) - { - QModelIndex idx=lst.back(); - - VInfo_ptr info=nodeInfo(idx); - - setForceShow(info); - } - - - #if 0 - Q_FOREACH(QModelIndex idx,lst) - { - VInfo_ptr info=nodeInfo(idx); - - for(int i=0; i < data_->count(); i++) - { - VTreeServer *ts=data_->server(i)->treeServer(); - Q_ASSERT(ts); - ts->clearForceShow(info->item()); - } - - for(int i=0; i < data_->count(); i++) - { - VTreeServer *ts=data_->server(i)->treeServer(); - Q_ASSERT(ts); - ts->setForceShow(info->item()); - } - } - #endif -} - - -//------------------------------------------------------------------ -// Create info object to index. It is used to identify nodes in -// the tree all over in the programme outside the view. -//------------------------------------------------------------------ - -//WARNING: if we are in the middle of an attribute filter change it will not give -//correct results, because atts_ contains the new filter state, but the whole VTree still -//base on the previous atts_ state!! However we can assume that the index vas visible in -//the tree so attrNum() is cached on the tree nodes so we get correct results for nodes. -//Attributes however cannot be identified correctly. - -VInfo_ptr TreeNodeModel::nodeInfo(const QModelIndex& index) -{ - //For invalid index no info is created. - if(!index.isValid()) - { - VInfo_ptr res; - return res; - } - - //Check if the node is a server - if(ServerHandler *s=indexToServerHandler(index)) - { - return VInfoServer::create(s); - } - - //If the internal pointer is a server it is either a server attribute or a - //top level node (suite) - if(VModelServer *mserver=data_->server(index.internalPointer())) - { - VTreeServer *server=mserver->treeServer(); - Q_ASSERT(server); - - //If the attrNum is cached it is correct! - int serverAttNum=server->tree()->attrNum(atts_); - - //It is a top level node - if(index.row() >= serverAttNum) - { - VNode *n=server->tree()->childAt(index.row()-serverAttNum)->vnode(); - return VInfoNode::create(n); - } - else - { - //TODO: serverattribute - } - } - - //Otherwise the internal pointer points to the parent node - else if(VTreeNode *parentNode=static_cast(index.internalPointer())) - { - //If the attrNum is cached it is correct! - int attNum=parentNode->attrNum(atts_); - - //It is a node - if(index.row() >= attNum) - { - VNode *n=parentNode->childAt(index.row()-attNum)->vnode(); - return VInfoNode::create(n); - } - //It is an attribute - else - { - //This wil not work properly if we are in the middle of an attribute - //filter change! atts_ is the new filter state, but index.row() is based on - //the previous filter state!! - if(VAttribute* a=parentNode->vnode()->attribute(index.row(),atts_)) - { - VInfo_ptr p=VInfoAttribute::create(a); - return p; - - } - } - } - - VInfo_ptr res; - return res; -} - -//---------------------------------------- -// Slots -//---------------------------------------- - -//Server is about to be added -void TreeNodeModel::slotServerAddBegin(int row) -{ - beginInsertRows(QModelIndex(),row,row); -} - -//Addition of the new server has finished -void TreeNodeModel::slotServerAddEnd() -{ - endInsertRows(); -} - -//Server is about to be removed -void TreeNodeModel::slotServerRemoveBegin(VModelServer* server,int /*nodeNum*/) -{ -#ifdef _UI_TREENODEMODEL_DEBUG - UiLog().dbg() << "TreeNodeModel::slotServerRemoveBegin -->"; -#endif - - int row=data_->indexOfServer(server); - Q_ASSERT(row >= 0); - -#ifdef _UI_TREENODEMODEL_DEBUG - UiLog().dbg() << " row: " << row; -#endif - beginRemoveRows(QModelIndex(),row,row); -} - -//Removal of the server has finished -void TreeNodeModel::slotServerRemoveEnd(int /*nodeNum*/) -{ -#ifdef _UI_TREENODEMODEL_DEBUG - UiLog().dbg() << "TreeNodeModel::slotServerRemoveEnd -->"; -#endif - - endRemoveRows(); -} - -void TreeNodeModel::slotDataChanged(VModelServer* server) -{ - QModelIndex idx=serverToIndex(server); - Q_EMIT dataChanged(idx,idx); -} - -//The node changed (it status etc) -void TreeNodeModel::slotNodeChanged(VTreeServer* server,const VTreeNode* node) -{ - if(!node) - return; - - QModelIndex index=nodeToIndex(server,node,0); - - if(!index.isValid()) - return; - - Q_EMIT dataChanged(index,index); -} - -//One of the attributes of the node changed. The total number of the -//attributes is the same. -void TreeNodeModel::slotAttributesChanged(VTreeServer* server,const VTreeNode* node) -{ - if(!node) - return; - - //Find the index of the node. - QModelIndex parent=nodeToIndex(server,node,0); - if(!parent.isValid()) - return; - - //Find out the indexes for all the attributes. For an attribute - //the internal pointer of the index points to the parent VNode. - QModelIndex idx1=index(0,0,parent); - QModelIndex idx2=index(node->attrNum(atts_)-1,0,parent); - - Q_EMIT dataChanged(idx1,idx2); -} - -//Attributes were added or removed -void TreeNodeModel::slotBeginAddRemoveAttributes(VTreeServer* server,const VTreeNode* node,int currentNum,int cachedNum) -{ - int diff=currentNum-cachedNum; - if(diff==0) - return; - - //Find the index of the node. This call does not use the - //number of attributes of the node so it is safe. - QModelIndex parent=nodeToIndex(server,node,0); - if(!parent.isValid()) - return; - - //At this point the model state is based on cachedNum!!!! - //So VNode::attr() must return cachedNum and we need to pretend we have - //cachedNum number of attributes - - //Insertion - if(diff > 0) - { - //We add extra rows to the end of the attributes - beginInsertRows(parent,cachedNum,cachedNum+diff-1); - } - //Deletion - else if(diff <0) - { - //We remove rows from the end - beginRemoveRows(parent,cachedNum+diff,cachedNum-1); - } - - //At this point the model state is based on currentNum!!!! -} - -//Attributes were added or removed -void TreeNodeModel::slotEndAddRemoveAttributes(VTreeServer* server,const VTreeNode* node,int currentNum,int cachedNum) -{ - int diff=currentNum-cachedNum; - if(diff==0) - return; - - //Find the index of the node. This call does not use the - //number of attributes of the node so it is safe. - QModelIndex parent=nodeToIndex(server,node,0); - if(!parent.isValid()) - return; - - //At this point the model state is based on currentNum!!!! - - //Insertion - if(diff > 0) - { - endInsertRows(); - } - //Deletion - else if(diff <0) - { - endRemoveRows(); - } - - //We need to update all the attributes to reflect the change! - //(Since we do not have information about what attribute was actually added - //we always add or remove attributes at the end of the attribute list!!! Then the - //slot below will update all the attributes of the node in the tree). - slotAttributesChanged(server,node); -} - -//The server scan has started (to populate the tree). At this point the tree is empty only containing the -//root node. Num tells us the number of children nodes (aka suites) the root node will contain. -void TreeNodeModel::slotBeginServerScan(VModelServer* server,int num) -{ - assert(active_ == true); - - QModelIndex idx=serverToIndex(server); - - //At this point the server node does not have any rows in the model!!! - if(idx.isValid() && num >0) - { - beginInsertRows(idx,0,num-1); - } -} - -//The server scan has finished. The tree is fully populated. -void TreeNodeModel::slotEndServerScan(VModelServer* server,int num) -{ - assert(active_ == true); - - QModelIndex idx=serverToIndex(server); - if(idx.isValid() && num >0) - { - endInsertRows(); - } - - VTreeServer *ts=server->treeServer(); - Q_ASSERT(ts); - Q_EMIT scanEnded(ts->tree()); - - if(ts->isFirstScan()) - Q_EMIT firstScanEnded(ts); -} - -//The server clear has started. It will remove all the nodes except the root node. -//So we need to remove all the rows belonging to the rootnode. -void TreeNodeModel::slotBeginServerClear(VModelServer* server,int) -{ - assert(active_ == true); - - QModelIndex idx=serverToIndex(server); - - if(idx.isValid()) - { - VTreeServer *ts=server->treeServer(); - Q_ASSERT(ts); - Q_EMIT clearBegun(ts->tree()); - - //We removes the attributes as well!!! - int num=rowCount(idx); - beginRemoveRows(idx,0,num-1); - } -} -//The server clear has finished. The tree is empty only containing the rootnode -void TreeNodeModel::slotEndServerClear(VModelServer* server,int) -{ - assert(active_ == true); - endRemoveRows(); -} - -void TreeNodeModel::slotBeginFilterUpdateRemove(VTreeServer* server,const VTreeNode* topChange,int totalRows) -{ - Q_ASSERT(topChange); - Q_ASSERT(!topChange->isRoot()); - - QModelIndex idx=nodeToIndex(server,topChange); - int attrNum=topChange->attrNum(atts_); - int chNum=topChange->numOfChildren(); - int totalNum=attrNum+chNum; - - Q_ASSERT(totalNum == totalRows); - Q_ASSERT(attrNum >=0); - Q_ASSERT(chNum >=0); - - if(totalRows >0) - { - Q_EMIT filterUpdateRemoveBegun(topChange); - beginRemoveRows(idx,attrNum,attrNum+chNum-1); - } -} - -void TreeNodeModel::slotEndFilterUpdateRemove(VTreeServer* /*server*/,const VTreeNode* topChange,int totalRows) -{ - Q_ASSERT(!topChange->isRoot()); - - if(totalRows >0) - { - endRemoveRows(); - } -} - -void TreeNodeModel::slotBeginFilterUpdateAdd(VTreeServer* server,const VTreeNode* topChange,int chNum) -{ - Q_ASSERT(topChange); - Q_ASSERT(!topChange->isRoot()); - - QModelIndex idx=nodeToIndex(server,topChange); - int attrNum=topChange->attrNum(atts_); - //int totalNum=attrNum+chNum; - - Q_ASSERT(attrNum >=0); - Q_ASSERT(chNum >=0); - - if(chNum >0) - { - beginInsertRows(idx,attrNum,attrNum+chNum-1); - } -} - -void TreeNodeModel::slotEndFilterUpdateAdd(VTreeServer* /*server*/,const VTreeNode* topChange,int chNum) -{ - Q_ASSERT(topChange); - Q_ASSERT(!topChange->isRoot()); - - if(chNum >0) - { - endInsertRows(); - } - - Q_EMIT filterUpdateAddEnded(topChange); -} - -void TreeNodeModel::slotBeginFilterUpdateRemoveTop(VTreeServer* server,int row) -{ - Q_ASSERT(server); - QModelIndex idx=serverToIndex(server); - int attrNum=server->tree()->attrNum(atts_); - int chNum=server->tree()->numOfChildren(); - - Q_ASSERT(chNum > row); - Q_ASSERT(attrNum >=0); - Q_ASSERT(chNum >=0); - - beginRemoveRows(idx,attrNum+row,attrNum+row); -} - -void TreeNodeModel::slotEndFilterUpdateRemoveTop(VTreeServer* server,int) -{ - Q_ASSERT(server); - endRemoveRows(); -} - -void TreeNodeModel::slotBeginFilterUpdateInsertTop(VTreeServer* server,int row) -{ - Q_ASSERT(server); - QModelIndex idx=serverToIndex(server); - int attrNum=server->tree()->attrNum(atts_); - int chNum=server->tree()->numOfChildren(); - - Q_ASSERT(chNum >= row); - Q_ASSERT(attrNum >=0); - Q_ASSERT(chNum >=0); - - beginInsertRows(idx,attrNum+row,attrNum+row); -} - -void TreeNodeModel::slotEndFilterUpdateInsertTop(VTreeServer* server,int row) -{ - Q_ASSERT(server); - endInsertRows(); - - int attrNum=server->tree()->attrNum(atts_); - int chNum=server->tree()->numOfChildren(); - - Q_ASSERT(chNum >= row); - Q_ASSERT(attrNum >=0); - Q_ASSERT(chNum >=0); - - if(row >=0) - { - const VTreeNode* topChange=server->tree()->childAt(row); - //when a suite becomes visible we must notify the view about it so that - //the expand state could be correctly set!!! - Q_EMIT filterUpdateAddEnded(topChange); - } -} - - -int TreeNodeModel::iconNum(VNode* n) const -{ - if(icons_->isEmpty()) - return 0; - else - return VIcon::pixmapNum(n,icons_); -} diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeModel.hpp ecflow-4.11.1/Viewer/src/TreeNodeModel.hpp --- ecflow-4.9.0/Viewer/src/TreeNodeModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,139 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TREENODEMODEL_H -#define TREENODEMODEL_H - -#include - -#include "AbstractNodeModel.hpp" -#include "Node.hpp" -#include "VAttributeType.hpp" -#include "Viewer.hpp" -#include "VInfo.hpp" - -class AttributeFilter; -class NodeFilterDef; -class ServerFilter; -class ServerHandler; -class VModelServer; -class VTreeModelData; -class VTreeNode; -class VTreeServer; - -class TreeNodeModel : public AbstractNodeModel -{ -Q_OBJECT - -public: - TreeNodeModel(ServerFilter* serverFilter,NodeFilterDef* filterDef, - AttributeFilter *atts,IconFilter* icons,QObject *parent=0); - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - QModelIndex serverToIndex(ServerHandler*) const; - - QModelIndex nodeToIndex(const VTreeNode*,int column=0) const; - QModelIndex nodeToIndex(const VNode*,int column=0) const; - VTreeServer* indexToServer(const QModelIndex & index) const; - VTreeServer* nameToServer(const std::string&) const; - VTreeNode* indexToNode( const QModelIndex & index) const; - VTreeNode* indexToServerOrNode( const QModelIndex & index) const; - - QModelIndex attributeToIndex(const VAttribute* a, int column=0) const; - - VInfo_ptr nodeInfo(const QModelIndex& index); - void setForceShow(VInfo_ptr); - void selectionChanged(QModelIndexList lst); - - void setEnableServerToolTip(bool st) {serverToolTip_=st;} - void setEnableNodeToolTip(bool st) {nodeToolTip_=st;} - void setEnableAttributeToolTip(bool st) {attributeToolTip_=st;} - - VModelData* data() const; - -public Q_SLOTS: - void slotServerAddBegin(int row); - void slotServerAddEnd(); - void slotServerRemoveBegin(VModelServer*,int); - void slotServerRemoveEnd(int); - - void slotNodeChanged(VTreeServer*,const VTreeNode*); - void slotAttributesChanged(VTreeServer*,const VTreeNode*); - void slotBeginAddRemoveAttributes(VTreeServer*,const VTreeNode*,int,int); - void slotEndAddRemoveAttributes(VTreeServer*,const VTreeNode*,int,int); - void slotBeginFilterUpdateRemove(VTreeServer*,const VTreeNode*,int); - void slotEndFilterUpdateRemove(VTreeServer*,const VTreeNode*,int); - void slotBeginFilterUpdateAdd(VTreeServer*,const VTreeNode*,int); - void slotEndFilterUpdateAdd(VTreeServer*,const VTreeNode*,int); - void slotBeginFilterUpdateRemoveTop(VTreeServer*,int); - void slotEndFilterUpdateRemoveTop(VTreeServer*,int); - void slotBeginFilterUpdateInsertTop(VTreeServer*,int); - void slotEndFilterUpdateInsertTop(VTreeServer*,int); - - //void slotResetBranch(VModelServer*,const VNode*); - void slotDataChanged(VModelServer*); - void slotBeginServerScan(VModelServer* server,int); - void slotEndServerScan(VModelServer* server,int); - void slotBeginServerClear(VModelServer* server,int); - void slotEndServerClear(VModelServer* server,int); - - int iconNum(VNode*) const; - bool isNode(const QModelIndex & index) const; - bool isAttribute(const QModelIndex & index) const; - -Q_SIGNALS: - void clearBegun(const VTreeNode*); - void scanEnded(const VTreeNode*); - void filterUpdateRemoveBegun(const VTreeNode*); - void filterUpdateAddEnded(const VTreeNode*); - void filterChangeBegun(); - void filterChangeEnded(); - void firstScanEnded(const VTreeServer*); - -#if 0 -protected: - QModelIndex forceShowNode(const VNode* node) const; - QModelIndex forceShowAttribute(const VAttribute* a) const; -#endif - -private: - bool isServer(const QModelIndex& index) const; - bool isServerForValid(const QModelIndex& index) const; - - ServerHandler* indexToServerHandler(const QModelIndex & index) const; - QModelIndex serverToIndex(VModelServer* server) const; - - QModelIndex nodeToIndex(VTreeServer*,const VTreeNode*,int column=0) const; - VTreeNode* indexToAttrParentNode(const QModelIndex & index) const; - VTreeNode* indexToAttrParentOrNode(const QModelIndex & index,bool &itIsANode) const; - QVariant serverData(const QModelIndex& index,int role) const; - QVariant nodeData(const QModelIndex& index,int role,VTreeNode*) const; - QVariant attributesData(const QModelIndex& index,int role,VTreeNode*) const; - - //Attribute filter - VTreeModelData* data_; - AttributeFilter* atts_; - IconFilter* icons_; - - bool serverToolTip_; - bool nodeToolTip_; - bool attributeToolTip_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeView.cpp ecflow-4.11.1/Viewer/src/TreeNodeView.cpp --- ecflow-4.9.0/Viewer/src/TreeNodeView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,747 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TreeNodeView.hpp" - -#include -#include -#include -#include -#include -#include - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#endif - -#include "AbstractNodeView.hpp" -#include "ActionHandler.hpp" -#include "Animation.hpp" -#include "AttributeEditor.hpp" -#include "ExpandState.hpp" -#include "TableNodeSortModel.hpp" -#include "PropertyMapper.hpp" -#include "StandardView.hpp" -#include "ServerHandler.hpp" -#include "TreeNodeModel.hpp" -#include "TreeNodeViewDelegate.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VItemPathParser.hpp" -#include "VNode.hpp" -#include "VModelData.hpp" -#include "VTree.hpp" - -#define _UI_TREENODEVIEW_DEBUG - -TreeNodeView::TreeNodeView(AbstractNodeView* view,TreeNodeModel* model,NodeFilterDef* filterDef,QWidget* parent) : - QObject(parent),NodeViewBase(filterDef), - view_(view), - model_(model), - needItemsLayout_(false), - prop_(NULL), - setCurrentIsRunning_(false), - setCurrentFromExpandIsRunning_(false), - canRegainCurrentFromExpand_(true), - inStartUp_(true) -{ - Q_ASSERT(view_); - - setObjectName("view"); - setProperty("style","nodeView"); - setProperty("view","tree"); - - //Context menu - connect(view_, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - - //Selection - connect(view_,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClickItem(const QModelIndex))); - - //Selection - connect(view_,SIGNAL(selectionChangedInView(const QItemSelection&, const QItemSelection &)), - this,SLOT(selectionChanged(const QItemSelection&, const QItemSelection &))); - - //expandState_=new ExpandState(this,model_); - actionHandler_=new ActionHandler(this,view_); - - connect(view_->delegate(),SIGNAL(sizeHintChangedGlobal()), - this,SLOT(slotSizeHintChangedGlobal())); - - //Properties - std::vector propVec; - propVec.push_back("view.tree.indentation"); - propVec.push_back("view.tree.background"); - propVec.push_back("view.tree.drawBranchLine"); - propVec.push_back("view.tree.branchLineColour"); - propVec.push_back("view.tree.serverToolTip"); - propVec.push_back("view.tree.nodeToolTip"); - propVec.push_back("view.tree.attributeToolTip"); - - prop_=new PropertyMapper(propVec,this); - - VProperty *prop=0; - std::string propName; - - //Initialise indentation - prop=prop_->find("view.tree.indentation",true); - adjustIndentation(prop->value().toInt()); - - //Init bg colour - prop=prop_->find("view.tree.background",true); - adjustBackground(prop->value().value()); - - //Init branch line status (on/off) - prop=prop_->find("view.tree.drawBranchLine",true); - adjustDrawBranchLine(prop->value().toBool()); - - //Init branch line/connector colour - prop=prop_->find("view.tree.branchLineColour",true); - adjustBranchLineColour(prop->value().value()); - - //Adjust tooltip - prop=prop_->find("view.tree.serverToolTip",true); - adjustServerToolTip(prop->value().toBool()); - - prop=prop_->find("view.tree.nodeToolTip",true); - adjustNodeToolTip(prop->value().toBool()); - - prop=prop_->find("view.tree.attributeToolTip",true); - adjustAttributeToolTip(prop->value().toBool()); - - inStartUp_=false; -} - -TreeNodeView::~TreeNodeView() -{ - delete actionHandler_; - delete prop_; -} - -QWidget* TreeNodeView::realWidget() -{ - return view_; -} - -QObject* TreeNodeView::realObject() -{ - return this; -} - -//Collect the selected list of indexes -QModelIndexList TreeNodeView::selectedList() -{ - QModelIndexList lst; - Q_FOREACH(QModelIndex idx,view_->selectedIndexes()) - if(idx.column() == 0) - lst << idx; - return lst; -} - -// This slot is called when the selection changed in the view -void TreeNodeView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ -#ifdef _UI_TREENODEVIEW_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndexList lst=view_->selectedIndexes(); - - //When the selection was triggered from restoring the expand state of the tree - //we do not want to broadcast it - if(lst.count() > 0 && !setCurrentFromExpandIsRunning_) - { - VInfo_ptr info=model_->nodeInfo(lst.back()); - if(info && !info->isEmpty()) - { -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " emit=" << info->path(); -#endif - Q_EMIT selectionChanged(info); - } - //Remembers the current selection - lastSelection_=info; - } - - - //The model has to know about the selection in order to manage the - //nodes that are forced to be shown. We only do it when we are not in the the middle of - // "setCurrentSelection()" call because that handles the forceShow independently. - if(!setCurrentIsRunning_) - model_->selectionChanged(lst); -} - -//Returns the current selection (the last one!) -VInfo_ptr TreeNodeView::currentSelection() -{ - QModelIndexList lst=view_->selectedIndexes(); - if(lst.count() > 0) - { - return model_->nodeInfo(lst.back()); - } - return VInfo_ptr(); -} - -//Sets the current selection to the given VInfo item. -// called: -// -from outside of the view when the selection is broadcast from another view -// -from within the view when the tree expand state was restored -void TreeNodeView::setCurrentSelection(VInfo_ptr info) -{ -#ifdef _UI_TREENODEVIEW_DEBUG - UI_FUNCTION_LOG -#endif - //We cannot call it recursively - Q_ASSERT(setCurrentIsRunning_ == false); - - //While the current item is being selected we do not allow - //another setCurrent call to go through - if(!info || setCurrentIsRunning_) - return; - - //Indicate that setCurrent started - setCurrentIsRunning_=true; - -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " info=" << info->path(); -#endif - - //Forcing an object to be shown can result in altering and relayouting the tree. We - //have to block the regaining of the selection at the end of the layout - //process when the tree expand state is restored. - canRegainCurrentFromExpand_=false; - - //Force the object to be shown in the tree - model_->setForceShow(info); - - //Lookup the object in the model - QModelIndex idx=model_->infoToIndex(info); - - //Get the index again if it is needed - //if(!idx.isValid()) - //{ - // idx=model_->infoToIndex(info); - //} - - //The re-layouting finished. We do not need to block the regaining of selection when - //the tree expand state is restored. - canRegainCurrentFromExpand_=true; - - //If the item is in the model we set it as current - if(idx.isValid()) - { - view_->setCurrentIndex(idx); //this will call selectionChanged - view_->scrollTo(idx); - } - - //Indicate that the set current process finished - setCurrentIsRunning_=false; -} - -//Sets the current selection to the given VInfo item -//when the tree expand state is restored -void TreeNodeView::setCurrentSelectionFromExpand(VInfo_ptr info) -{ -#ifdef _UI_TREENODEVIEW_DEBUG - UI_FUNCTION_LOG -#endif - if(!info || setCurrentFromExpandIsRunning_) - return; - -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " info=" << info->path(); -#endif - - setCurrentFromExpandIsRunning_=true; - setCurrentSelection(info); - setCurrentFromExpandIsRunning_=false; -} - -//Selects the first server in the view -void TreeNodeView::selectFirstServer() -{ - QModelIndex idx=model_->index(0,0); - if(idx.isValid()) - { - view_->setCurrentIndex(idx); - VInfo_ptr info=model_->nodeInfo(idx); - Q_EMIT selectionChanged(info); - } -} - -void TreeNodeView::slotContextMenu(const QPoint &position) -{ -#ifdef _UI_TREENODEVIEW_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndex indexClicked=view_->indexAt(position); - QModelIndexList indexSel=selectedList(); - if(!indexSel.contains(indexClicked)) - { - indexSel.clear(); - indexSel << indexClicked; - } - - QPoint scrollOffset(view_->horizontalScrollBar()->value(),view_->verticalScrollBar()->value()); - handleContextMenu(indexClicked,indexSel,view_->mapToGlobal(position),position+scrollOffset,view_); -} - -void TreeNodeView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) -{ - //Node actions - if(indexClicked.isValid() && indexClicked.column() == 0) //indexLst[0].isValid() && indexLst[0].column() == 0) - { - std::vector nodeLst; - for(int i=0; i < indexLst.count(); i++) - { - VInfo_ptr info=model_->nodeInfo(indexLst[i]); - if(info && !info->isEmpty()) - nodeLst.push_back(info); - } - - actionHandler_->contextMenu(nodeLst,globalPos); - } - - //Desktop actions - else - { - } -} - -void TreeNodeView::slotDoubleClickItem(const QModelIndex& idx) -{ - VInfo_ptr info=model_->nodeInfo(idx); - if(info && info->isAttribute()) - { - slotViewCommand(info,"edit"); - } -} - -void TreeNodeView::slotViewCommand(VInfo_ptr info,QString cmd) -{ - //Expand all the children of the given node - if(cmd == "expand") - { - QModelIndex idx=model_->infoToIndex(info); - if(idx.isValid()) - { -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif -#ifdef _UI_TREENODEVIEW_DEBUG - QTime t; - t.start(); -#endif - //apply expand in the view - view_->expandAll(idx); -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << "expandAll time=" << t.elapsed()/1000. << "s"; -#endif -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - //save/update the expand state object - saveExpandAll(idx); - } - } - else if(cmd == "collapse") - { - QModelIndex idx=model_->infoToIndex(info); - if(idx.isValid()) - { - //apply expand in the view - view_->collapseAll(idx); - - //save/update the expand state object - saveCollapseAll(idx); - } - } - - else if(cmd == "edit") - { - if(info && info->isAttribute()) - { - AttributeEditor::edit(info,view_); - } - } - - //Only filter the given suite (of the node stored in info). - //We achive this by setting the suitefilter to filter only this - //suite. - else if(cmd == "filterOne") - { - if(info) - { - if(ServerHandler* server=info->server()) - { - VNode* n=info->node(); - if(n && info->isNode()) - server->setSuiteFilterWithOne(n); - } - } - } - - /*if(cmd == "set_as_root") - { - model_->setRootNode(nodeLst.at(0)->node()); - expandAll(); - }*/ -} - -void TreeNodeView::reload() -{ - //model_->reload(); - //expandAll(); -} - -void TreeNodeView::rerender() -{ - if(needItemsLayout_) - { - view_->doItemsLayout(); - needItemsLayout_=false; - } - else - { - view_->viewport()->update(); - } -} - -void TreeNodeView::slotRerender() -{ - rerender(); -} - -void TreeNodeView::slotRepaint(Animation* an) -{ - if(!an) - return; - - Q_FOREACH(VNode* n,an->targets()) - { - view_->update(model_->nodeToIndex(n)); - } -} - -void TreeNodeView::slotSizeHintChangedGlobal() -{ - needItemsLayout_=true; -} - -//==================================================== -// Expand state management -//==================================================== - -void TreeNodeView::expandTo(const QModelIndex& idxTo) -{ - QModelIndex idx=model_->parent(idxTo); - QModelIndexList lst; - - while(idx.isValid()) - { - lst.push_front(idx); - idx=idx.parent(); - } - - Q_FOREACH(QModelIndex d,lst) - { - view_->expand(d); - } -} - -#if 0 -//Save all -void TreeNodeView::slotSaveExpand() -{ - //For each server we save the expand state - for(int i=0; i < model_->rowCount(); i++) - { - QModelIndex serverIdx=model_->index(i, 0); - VTreeServer* ts=model_->indexToServer(serverIdx); - Q_ASSERT(ts); - - //The expand state is stored on the VTreeServer and must survive updates and refreshes! - ExpandState* es=ts->expandState(); - if(!es) - { - es=new ExpandState(view_,model_); - ts->setExpandState(es); //the treeserver takes ownership of the expandstate - } - - //Save the current state - Q_ASSERT(ts->tree()); - VNode* vnode=ts->tree()->vnode(); - Q_ASSERT(vnode); - es->save(vnode); - } -} - -void TreeNodeView::slotRestoreExpand() -{ - //For each server we restore the expand state - for(int i=0; i < model_->rowCount(); i++) - { - QModelIndex serverIdx=model_->index(i, 0); - VTreeServer* ts=model_->indexToServer(serverIdx); - Q_ASSERT(ts); - - //The expand state is stored on the VTreeServer - ExpandState* es=ts->expandState(); - if(es) - { - Q_ASSERT(ts->tree()); - VNode* vnode=ts->tree()->vnode(); - Q_ASSERT(vnode); - - bool expanded=view_->isExpanded(serverIdx); - view_->collapse(serverIdx); - es->collectExpanded(vnode,view_->expandedIndexes); - if(expanded) - view_->expand(serverIdx); - } - } - regainSelectionFromExpand(); - -} -#endif - -//Save the expand state for the given node (it can be a server as well) -void TreeNodeView::slotSaveExpand(const VTreeNode* node) -{ - UI_FUNCTION_LOG - Q_ASSERT(node); - VTreeServer* ts=node->server(); - Q_ASSERT(ts); - -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " node=" << node->vnode()->fullPath(); -#endif - - ExpandState* es=ts->expandState(); - if(!es) - { - es=new ExpandState(view_,model_); - ts->setExpandState(es); //the treeserver takes ownership of the expandstate - } - - Q_ASSERT(es); - - //Save the current state - es->save(node->vnode()); - - //es->print(); -} - -//Restore the expand state for the given node (it can be a server as well) -void TreeNodeView::slotRestoreExpand(const VTreeNode* node) -{ - UI_FUNCTION_LOG - Q_ASSERT(node); - VTreeServer* ts=node->server(); - Q_ASSERT(ts); - -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " node=" << node->vnode()->fullPath(); -#endif - - if(ExpandState* es=ts->expandState()) - { - QModelIndex idx=model_->nodeToIndex(node); - if(idx.isValid()) - { - bool expandedOri=view_->isExpanded(idx); - view_->collapse(idx); - es->collectExpanded(node->vnode(),view_->expandedIndexes); -#ifdef _UI_TREENODEVIEW_DEBUG - UiLog().dbg() << " expanded=" << view_->isExpanded(idx); -#endif - if(expandedOri || view_->isExpanded(idx)) - view_->expand(idx); - } - - //es->print(); - } - - if(canRegainCurrentFromExpand_) - regainSelectionFromExpand(); -} - -void TreeNodeView::saveExpandAll(const QModelIndex& idx) -{ - if(!idx.isValid()) - return; - - VTreeNode* tnode=model_->indexToServerOrNode(idx); - Q_ASSERT(tnode); - VTreeServer* ts=tnode->server(); - Q_ASSERT(ts); - Q_ASSERT(ts->tree()); - VNode* vnode=ts->tree()->vnode(); - Q_ASSERT(vnode); - - //The expand state is stored on the VTreeServer - ExpandState* es=ts->expandState(); - if(!es) - { - es=new ExpandState(view_,model_); - ts->setExpandState(es); //the treeserver takes ownership of the expandstate - } - if(es->isEmpty()) - { - es->save(vnode); - } - es->saveExpandAll(vnode); -} - -void TreeNodeView::saveCollapseAll(const QModelIndex& idx) -{ - if(!idx.isValid()) - return; - - VTreeNode* tnode=model_->indexToServerOrNode(idx); - Q_ASSERT(tnode); - VTreeServer* ts=tnode->server(); - Q_ASSERT(ts); - Q_ASSERT(ts->tree()); - VNode* vnode=ts->tree()->vnode(); - Q_ASSERT(vnode); - - //The expand state is stored on the VTreeServer - ExpandState* es=ts->expandState(); - if(!es) - { - es=new ExpandState(view_,model_); - ts->setExpandState(es); //the treeserver takes ownership of the expandstate - } - if(es->isEmpty()) - { - es->save(vnode); - } - es->saveCollapseAll(vnode); -} - - -void TreeNodeView::regainSelectionFromExpand() -{ - Q_ASSERT(canRegainCurrentFromExpand_ == true); - - VInfo_ptr s=currentSelection(); - if(!s) - { - if(lastSelection_) - { - std::string lastPath=lastSelection_->storedPath(); - lastSelection_->regainData(); - if(!lastSelection_->hasData()) - { - lastSelection_.reset(); - - //We might have lost the selection because the - //selected node was removed. We try to find the parent - //and select it when exists. - VItemPathParser p(lastPath); - VInfo_ptr ptr=VInfo::createFromPath(p.parent()); - if(ptr) - { - setCurrentSelectionFromExpand(ptr); - } - } - else - { - setCurrentSelectionFromExpand(lastSelection_); - } - } - } -} - -//============================================== -// Property handling -//============================================== - -void TreeNodeView::adjustIndentation(int indent) -{ - if(indent >=0) - { - view_->setIndentation(indent); - needItemsLayout_=true; - } -} - -void TreeNodeView::adjustBackground(QColor col) -{ - if(col.isValid()) - { - QPalette p=view_->viewport()->palette(); - p.setColor(QPalette::Window,col); - view_->viewport()->setPalette(p); - - //When we set the palette on startup something resets the palette - //before the first paint event happens. So we set the expected bg colour - //so that the view should know what bg colour it should use. - if(inStartUp_) - view_->setExpectedBg(col); - } -} - -void TreeNodeView::adjustDrawBranchLine(bool b) -{ - view_->setDrawConnector(b); -} - -void TreeNodeView::adjustBranchLineColour(QColor col) -{ - view_->setConnectorColour(col); -} - -void TreeNodeView::adjustServerToolTip(bool st) -{ - model_->setEnableServerToolTip(st); -} - -void TreeNodeView::adjustNodeToolTip(bool st) -{ - model_->setEnableNodeToolTip(st); -} - -void TreeNodeView::adjustAttributeToolTip(bool st) -{ - model_->setEnableAttributeToolTip(st); -} - -void TreeNodeView::notifyChange(VProperty* p) -{ - if(p->path() == "view.tree.background") - { - adjustBackground(p->value().value()); - } - else if(p->path() == "view.tree.indentation") - { - adjustIndentation(p->value().toInt()); - } - else if(p->path() == "view.tree.drawBranchLine") - { - adjustDrawBranchLine(p->value().toBool()); - } - else if(p->path() == "view.tree.branchLineColour") - { - adjustBranchLineColour(p->value().value()); - } - else if(p->path() == "view.tree.serverToolTip") - { - adjustServerToolTip(p->value().toBool()); - } - else if(p->path() == "view.tree.nodeToolTip") - { - adjustNodeToolTip(p->value().toBool()); - } - else if(p->path() == "view.tree.attributeToolTip") - { - adjustAttributeToolTip(p->value().toBool()); - } -} diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeViewDelegate.cpp ecflow-4.11.1/Viewer/src/TreeNodeViewDelegate.cpp --- ecflow-4.9.0/Viewer/src/TreeNodeViewDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1398 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TreeNodeViewDelegate.hpp" - -#include -#include -#include -#include -#include - -#include "AbstractNodeModel.hpp" -#include "Animation.hpp" -#include "FontMetrics.hpp" -#include "IconProvider.hpp" -#include "Palette.hpp" -#include "PropertyMapper.hpp" -#include "ServerHandler.hpp" -#include "TreeNodeModel.hpp" -#include "UiLog.hpp" - -static std::vector propVec; - -static QColor typeFgColourClassic=QColor(Qt::white); -static QColor typeBgColourClassic=QColor(150,150,150); -static QColor childCountColour=QColor(90,91,92); - - -struct NodeShape -{ - QColor col_; - QPolygon shape_; -}; - -struct NodeText -{ - QColor fgCol_; - QColor bgCol_; - QRect br_; - QString text_; -}; - -struct ServerUpdateData -{ - QRect br_; - int prevTime_; - int nextTime_; - QString prevText_; - QString nextText_; - float prog_; -}; - - -class TreeNodeDelegateBox : public NodeDelegateBox -{ -public: - - TreeNodeDelegateBox() : textTopCorrection(0), textBottomCorrection(0) - { - topMargin=2; - bottomMargin=2; - leftMargin=1; - rightMargin=2; - topPadding=0; - bottomPadding=0; - leftPadding=2; - rightPadding=2; - } - - int realFontHeight; - int textTopCorrection; - int textBottomCorrection; - - void adjust(const QFont& f) - { - FontMetrics fm(f); - realFontHeight=fm.realHeight(); - textTopCorrection=fm.topPaddingForCentre(); - textBottomCorrection=fm.bottomPaddingForCentre(); - fontHeight=fm.height(); - - if(textTopCorrection > 1) - { - textTopCorrection-=2; - realFontHeight+=2; - } - - height=realFontHeight+topPadding+bottomPadding; - fullHeight=height+topMargin+bottomMargin; - sizeHintCache=QSize(100,fullHeight); - spacing=fm.width('A')*3/4; - - int h=static_cast(static_cast(fm.height())*0.7); - iconSize=h; - if(iconSize % 2 == 1) - iconSize+=1; - - iconGap=1; - if(iconSize > 16) - iconGap=2; - - iconPreGap=fm.width('A')/2; - } - - QRect adjustTextRect(const QRect& rIn) const - { - //Q_ASSERT(rIn.height() == fontHeight); - QRect r=rIn; - r.setY(r.y()-textTopCorrection); - r.setHeight(fontHeight); - return r; - } - - QRect adjustSelectionRect(const QRect& optRect) const { - QRect r=optRect; - return r; - } -}; - -class TreeAttrDelegateBox : public AttrDelegateBox -{ -public: - TreeAttrDelegateBox() : textTopCorrection(0), textBottomCorrection(0) - { - topMargin=2; - bottomMargin=2; - leftMargin=1; - rightMargin=2; - topPadding=0; - bottomPadding=0; - leftPadding=1; - rightPadding=2; - } - - int realFontHeight; - int textTopCorrection; - int textBottomCorrection; - - void adjust(const QFont& f) - { - FontMetrics fm(f); - realFontHeight=fm.realHeight(); - textTopCorrection=fm.topPaddingForCentre(); - textBottomCorrection=fm.bottomPaddingForCentre(); - fontHeight=fm.height(); - - height=realFontHeight+topPadding+bottomPadding; - fullHeight=height+topMargin+bottomMargin; - sizeHintCache=QSize(100,fullHeight); - spacing=fm.width('A')*3/4; - } - - QRect adjustTextRect(const QRect& rIn) const - { - QRect r=rIn; - r.setY(r.y()-textTopCorrection+1); - r.setHeight(fontHeight); - return r; - } - - QRect adjustTextBgRect(const QRect& rIn) const - { - QRect r=rIn; - return r.adjusted(0,-1,0,1); - } - - QRect adjustSelectionRect(const QRect& optRect) const { - QRect r=optRect; - return r.adjusted(0,-selectRm.topOffset(),0,-selectRm.bottomOffset()); - } - - QRect adjustSelectionRectNonOpt(const QRect& optRect) const { - return adjustSelectionRect(optRect); - } -}; - - - -TreeNodeViewDelegate::TreeNodeViewDelegate(TreeNodeModel* model,QWidget *parent) : - nodeRectRad_(0), - drawChildCount_(true), - nodeStyle_(ClassicNodeStyle), - drawNodeType_(true), - bgCol_(Qt::white), - model_(model) -{ - - drawAttrSelectionRect_=true; - - attrFont_=font_; - attrFont_.setPointSize(8); - serverInfoFont_=font_; - suiteNumFont_=font_; - abortedReasonFont_=font_; - abortedReasonFont_.setBold(true); - typeFont_=font_; - typeFont_.setBold(true); - typeFont_.setPointSize(font_.pointSize()-1); - - //Property - if(propVec.empty()) - { - propVec.push_back("view.tree.nodeFont"); - propVec.push_back("view.tree.attributeFont"); - propVec.push_back("view.tree.display_child_count"); - propVec.push_back("view.tree.displayNodeType"); - propVec.push_back("view.tree.background"); - propVec.push_back("view.common.node_style"); - - //Base settings - addBaseSettings(propVec); - } - - prop_=new PropertyMapper(propVec,this); - - //WARNING: updateSettings() cannot be called from here because - //nodeBox_ and attrBox_ are only created in the derived classes. So it muse be - //called from those derived classes. - - //The parent must be the view!!! - animation_=new AnimationHandler(parent); - - nodeBox_=new TreeNodeDelegateBox; - attrBox_=new TreeAttrDelegateBox; - - nodeBox_->adjust(font_); - attrBox_->adjust(attrFont_); - - typeFont_=font_; - typeFont_.setBold(true); - typeFont_.setPointSize(font_.pointSize()-1); - - updateSettings(); -} - -TreeNodeViewDelegate::~TreeNodeViewDelegate() -{ - delete animation_; -} - -void TreeNodeViewDelegate::updateSettings() -{ - Q_ASSERT(nodeBox_); - Q_ASSERT(attrBox_); - - if(VProperty* p=prop_->find("view.common.node_style")) - { - if(p->value().toString() == "classic") - nodeStyle_=ClassicNodeStyle; - else - nodeStyle_=BoxAndTextNodeStyle; - } - if(VProperty* p=prop_->find("view.tree.nodeRectRadius")) - { - nodeRectRad_=p->value().toInt(); - } - if(VProperty* p=prop_->find("view.tree.nodeFont")) - { - QFont newFont=p->value().value(); - - if(font_ != newFont ) - { - font_=newFont; - nodeBox_->adjust(font_); - serverInfoFont_=font_; - serverNumFont_.setFamily(font_.family()); - serverNumFont_.setPointSize(font_.pointSize()-1); - suiteNumFont_.setFamily(font_.family()); - suiteNumFont_.setPointSize(font_.pointSize()-1); - abortedReasonFont_.setFamily(font_.family()); - abortedReasonFont_.setPointSize(font_.pointSize()); - typeFont_.setFamily(font_.family()); - typeFont_.setPointSize(font_.pointSize()-1); - - Q_EMIT sizeHintChangedGlobal(); - } - } - if(VProperty* p=prop_->find("view.tree.attributeFont")) - { - QFont newFont=p->value().value(); - - if(attrFont_ != newFont) - { - attrFont_=newFont; - attrBox_->adjust(attrFont_); - Q_EMIT sizeHintChangedGlobal(); - } - } - - if(VProperty* p=prop_->find("view.tree.display_child_count")) - { - drawChildCount_=p->value().toBool(); - } - - if(VProperty* p=prop_->find("view.tree.displayNodeType")) - { - drawNodeType_=p->value().toBool(); - } - - if(VProperty* p=prop_->find("view.tree.background")) - { - bgCol_=p->value().value(); - } - - //Update the settings handled by the base class - updateBaseSettings(); -} - -bool TreeNodeViewDelegate::isSingleHeight(int h) const -{ - return (h==nodeBox_->fullHeight || h == attrBox_->fullHeight); -} - -QSize TreeNodeViewDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex & index ) const -{ - return nodeBox_->sizeHintCache; -} - -//This has to be extremely fast -void TreeNodeViewDelegate::sizeHint(const QModelIndex& index,int& w,int& h) const -{ - QVariant tVar=index.data(Qt::DisplayRole); - - h=nodeBox_->fullHeight; - - //For nodes we compute the exact size of visual rect - if(tVar.type() == QVariant::String) - { - QString text=index.data(Qt::DisplayRole).toString(); - if(index.data(AbstractNodeModel::ServerRole).toInt() ==0) - { - widthHintServer(index,w,text); - } - else - { - w=nodeWidth(index,text); - } - } - //For attributes we do not need the exact width since they do not have children so - //there is nothing on their right in the view. We compute their proper size when - //they are first rendered. However the exact height must be known at this stage! - else if(tVar.type() == QVariant::StringList) - { - //Each attribute has this height except the multiline labels - h=attrBox_->fullHeight; - - //It is a big enough hint for the width. - w=300; - - //For multiline labels we need to compute the height - int attLineNum=0; - if((attLineNum=index.data(AbstractNodeModel::AttributeLineRole).toInt()) > 1) - { - h=labelHeight(attLineNum); - } - } -} - -void TreeNodeViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index,QSize& size) const -{ - size=QSize(0,0); - - //Background - QStyleOptionViewItem vopt(option); - initStyleOption(&vopt, index); - - //Save painter state - painter->save(); - - if(index.data(AbstractNodeModel::ConnectionRole).toInt() == 0) - { - QRect fullRect=QRect(0,vopt.rect.y(),painter->device()->width(),vopt.rect.height()); - painter->fillRect(fullRect,lostConnectBgBrush_); - QRect bandRect=QRect(0,vopt.rect.y(),5,vopt.rect.height()); - painter->fillRect(bandRect,lostConnectBandBrush_); - } - - QVariant tVar=index.data(Qt::DisplayRole); - painter->setFont(font_); - - if(tVar.type() == QVariant::String) - { - int width=0; - QString text=index.data(Qt::DisplayRole).toString(); - if(index.data(AbstractNodeModel::ServerRole).toInt() ==0) - { - width=renderServer(painter,index,vopt,text); - } - else - { - width=renderNode(painter,index,vopt,text); - } - - size=QSize(width,nodeBox_->fullHeight); - } - //Render attributes - else if(tVar.type() == QVariant::StringList) - { - QStringList lst=tVar.toStringList(); - if(lst.count() > 0) - { - QMap::const_iterator it=attrRenderers_.find(lst.at(0)); - if(it != attrRenderers_.end()) - { - AttributeRendererProc a=it.value(); - (this->*a)(painter,lst,vopt,size); - } - //if(width==0) - // width=300; - } - - } - - painter->restore(); - - //else - // QStyledItemDelegate::paint(painter,option,index); -} - - - -int TreeNodeViewDelegate::renderServer(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const -{ - ServerHandler* server=static_cast(index.data(AbstractNodeModel::ServerPointerRole).value()); - Q_ASSERT(server); - - int totalWidth=0; - bool selected=option.state & QStyle::State_Selected; - //int offset=nodeBox_->leftMargin; - QFontMetrics fm(font_); - - //The initial filled rect (we will adjust its width) - QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); - -#if 0 - painter->setPen(QColor(190,190,190)); - painter->drawLine(0,option.rect.y()+1,painter->device()->width(),option.rect.y()+1); - painter->drawLine(0,option.rect.bottom()-1,painter->device()->width(),option.rect.bottom()-1); -#endif -#if 0 - - QRect progRect(0,itemRect.y()-deltaH,painter->device()->width(),itemRect.height()+2*deltaH); - progRect.setWidth(painter->device()->width()); - painter->setBrush(Qt::NoBrush); - painter->setPen(QColor(190,190,190)); - painter->drawRect(progRect); - painter->setBrush(QColor(230,230,230)); - painter->drawRect(progRect.adjusted(0,0,-progRect.width()*0.4,0)); -#endif - - int currentRight=itemRect.x(); - - NodeShape stateShape; - stateShape.col_=index.data(Qt::BackgroundRole).value(); - - NodeText nodeText; - nodeText.text_=text; - int textWidth=fm.width(text); - - if(nodeStyle_ == BoxAndTextNodeStyle) - { - stateShape.shape_=QPolygon(QRect(itemRect.x(),itemRect.y(),itemRect.height(),itemRect.height())); - QRect shBr=stateShape.shape_.boundingRect(); - currentRight=shBr.x()+shBr.width()+2; - nodeText.br_=QRect(currentRight,itemRect.y(),textWidth+nodeBox_->leftPadding, itemRect.height()); - nodeText.fgCol_=QColor(Qt::black); - currentRight=nodeText.br_.x()+nodeText.br_.width(); - } - else - { - stateShape.shape_=QPolygon(QRect(itemRect.x(),itemRect.y(),textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding,itemRect.height())); - nodeText.br_=QRect(itemRect.x()+nodeBox_->leftPadding,itemRect.y(),textWidth, itemRect.height()); - nodeText.fgCol_=index.data(Qt::ForegroundRole).value(); - QRect shBr=stateShape.shape_.boundingRect(); - currentRight=shBr.x()+shBr.width();; - } - - //Refresh timer - /* bool hasTimer=true; - QRect timeRect(currentRight+offset,itemRect.y()-1,itemRect.height()+2,itemRect.height()+2); - currentRight=timeRect.right();*/ - - //Icons area - QList pixLst; - QList pixRectLst; - - QVariant va=index.data(AbstractNodeModel::IconRole); - if(va.type() == QVariant::List) - { - QVariantList lst=va.toList(); - if(lst.count() >0) - { - int xp=currentRight+nodeBox_->iconPreGap; - int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; - for(int i=0; i < lst.count(); i++) - { - int id=lst[i].toInt(); - if(id != -1) - { - pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); - pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); - xp+=nodeBox_->iconSize+nodeBox_->iconGap; - } - } - - if(!pixLst.isEmpty()) - { - currentRight=xp-nodeBox_->iconGap; - } - } - } - - //The pixmap (optional) - bool hasPix=false; - QRect pixRect; - - //The info rectangle (optional) - QRect infoRect; - QString infoTxt=index.data(AbstractNodeModel::InfoRole).toString(); - bool hasInfo=(infoTxt.isEmpty() == false); - - if(hasInfo) - { - //infoFont.setBold(true); - fm=QFontMetrics(serverInfoFont_); - - int infoWidth=fm.width(infoTxt); - infoRect = nodeText.br_; - infoRect.setLeft(currentRight+fm.width('A')); - infoRect.setWidth(infoWidth); - currentRight=infoRect.x()+infoRect.width(); - } - - //The load icon (optional) - QRect loadRect; - bool hasLoad=index.data(AbstractNodeModel::LoadRole).toBool(); - Animation* an=0; - - //Update load animation - if(hasLoad) - { - an=animation_->find(Animation::ServerLoadType,true); - - loadRect = QRect(currentRight+fm.width('A'), - itemRect.top()+(itemRect.height()-an->scaledSize().height())/2, - an->scaledSize().width(), - an->scaledSize().height()); - - currentRight=loadRect.x()+loadRect.width(); - - //Add this index to the animations - //There is no guarantee that this index will be valid in the future!!! - an->addTarget(server->vRoot()); - } - //Stops load animation - else - { - if((an=animation_->find(Animation::ServerLoadType,false)) != NULL) - an->removeTarget(server->vRoot()); - } - - //The node number (optional) - QRect numRect; - QString numTxt; - bool hasNum=false; - - if(drawChildCount_) - { - QVariant va=index.data(AbstractNodeModel::NodeNumRole); - hasNum=(va.isNull() == false); - if(hasNum) - { - numTxt="(" + QString::number(va.toInt()) + ")"; - - QFontMetrics fmNum(serverNumFont_); - - int numWidth=fmNum.width(numTxt); - numRect = nodeText.br_; - numRect.setLeft(currentRight+fmNum.width('A')/2); - numRect.setWidth(numWidth); - currentRight=numRect.x()+numRect.width(); - } - } - - //Update -#if 0 - bool hasUpdate=false; - ServerUpdateData updateData; - if(server) - { - hasUpdate=true; - updateData.br_=QRect(currentRight+3*offset,itemRect.y()-1,0,itemRect.height()+2); - updateData.prevTime_=server->secsSinceLastRefresh(); - updateData.nextTime_=server->secsTillNextRefresh(); - if(updateData.prevTime_ >=0) - { - if(updateData.nextTime_ >=0) - { - updateData.prevText_="-" + formatTime(updateData.prevTime_); - updateData.nextText_="+" +formatTime(updateData.nextTime_); - updateData.prog_=(static_cast(updateData.prevTime_)/static_cast(updateData.prevTime_+updateData.nextTime_)); - updateData.br_.setWidth(fm.width("ABCDE")+fm.width(updateData.prevText_)+fm.width(updateData.nextText_)+2*offset); - } - else - { - updateData.prevText_="last update: " + formatTime(updateData.prevTime_); - updateData.prog_=0; - updateData.br_.setWidth(fm.width(updateData.prevText_)); - } - currentRight=updateData.br_.right(); - } - else - { - hasUpdate=false; - } - } -#endif - - //Define clipping - int rightPos=currentRight+1; - totalWidth=rightPos-option.rect.left(); - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } - - //Draw state/node rect - renderServerCell(painter,stateShape,nodeText,selected); - - //Draw timer - /*int remaining=6*60*1000; // server->remainingTimeToRefresh(); - int total=10*60*1000; //server->refreshPeriod() ; - renderTimer(painter,timeRect,remaining,total);*/ - - //Draw icons - for(int i=0; i < pixLst.count(); i++) - { - painter->drawPixmap(pixRectLst[i],pixLst[i]); - } - - //Draw pixmap if needed - if(hasPix) - { - painter->drawPixmap(pixRect,errPix_); - } - - //Draw info - if(hasInfo) - { - painter->setPen(Qt::black); - painter->setFont(serverInfoFont_); - painter->drawText(infoRect,Qt::AlignLeft | Qt::AlignVCenter,infoTxt); - } - - //Draw number - if(hasNum) - { - painter->setPen(childCountColour); - painter->setFont(serverNumFont_); - painter->drawText(numRect,Qt::AlignLeft | Qt::AlignVCenter,numTxt); - } - - //Draw load animation - if(hasLoad) - { - Animation* an=animation_->find(Animation::ServerLoadType,false); - if(an) - { - painter->drawPixmap(loadRect,an->currentPixmap()); - } - } -#if 0 - if(hasUpdate) - { - renderServerUpdate(painter,updateData); - } -#endif - if(setClipRect) - { - painter->restore(); - } - - return totalWidth; -} - -int TreeNodeViewDelegate::renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const -{ - int totalWidth=0; - bool selected=option.state & QStyle::State_Selected; - QFontMetrics fm(font_); - - //The initial filled rect (we will adjust its width) - QRect itemRect=option.rect.adjusted(nodeBox_->leftMargin,nodeBox_->topMargin,0,-nodeBox_->bottomMargin); - - NodeShape stateShape; - NodeShape realShape; - NodeText nodeText; - NodeText typeText; - - //get the colours - QVariant bgVa=index.data(Qt::BackgroundRole); - bool hasRealBg=false; - if(bgVa.type() == QVariant::List) - { - QVariantList lst=bgVa.toList(); - if(lst.count() == 2) - { - hasRealBg=true; - stateShape.col_=lst[0].value(); - realShape.col_=lst[1].value(); - } - } - else - { - stateShape.col_=bgVa.value(); - } - - int currentRight=itemRect.x(); - - //Node type - QFontMetrics fmType(typeFont_); - int typeWidth=fmType.width(" S"); - - if(drawNodeType_) - { - int nodeType=index.data(AbstractNodeModel::NodeTypeRole).toInt(); - switch(nodeType) - { - case 0: typeText.text_="S"; break; - case 1: typeText.text_="F"; break; - case 2: typeText.text_="T"; break; - case 3: typeText.text_="A"; break; - default: break; - } - } - - //The text rectangle - nodeText.text_=text; - int textWidth=fm.width(text); - - if(nodeStyle_ == BoxAndTextNodeStyle) - { - int realW=itemRect.height()/4; - - QRect r1(currentRight,itemRect.y(),itemRect.height(),itemRect.height()); - if(hasRealBg) - r1.adjust(0,0,-realW,0); - - stateShape.shape_=QPolygon(r1); - - if(hasRealBg) - { - QRect r2(r1.x()+r1.width(),r1.top(),realW+1,r1.height()); - realShape.shape_=QPolygon(r2); - currentRight=r2.x()+r2.width()+2; - } - else - currentRight=r1.x()+r1.width()+2; - - if(drawNodeType_) - { - typeText.br_=r1; - typeText.fgCol_=index.data(AbstractNodeModel::NodeTypeForegroundRole).value(); - } - - nodeText.br_=QRect(currentRight,r1.top(),textWidth+nodeBox_->leftPadding,r1.height()); - nodeText.fgCol_=QColor(Qt::black); - currentRight=nodeText.br_.x() + nodeText.br_.width(); - -#if 0 - if(nodeStyle_ == BoxAndTextNodeStyle) - { - int realW=itemRect.height()/4; - - //state box - currentRight+=itemRect.height(); - if(hasRealBg) - { - currentRight+=1; - } - currentRight+=2; - - //node name - currentRight+=textWidth+nodeBox_->leftPadding; - } -#endif - - } - //Classic style - else - { - if(drawNodeType_) - { - typeText.br_=QRect(currentRight,itemRect.y(),typeWidth,itemRect.height()); - typeText.fgCol_=typeFgColourClassic; - typeText.bgCol_=typeBgColourClassic; - currentRight=typeText.br_.x()+typeText.br_.width()+2; - } - - QRect r1(currentRight,itemRect.y(),textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding,itemRect.height()); - stateShape.shape_=QPolygon(r1); - currentRight=r1.x()+r1.width(); - - nodeText.br_=QRect(r1.left()+nodeBox_->leftPadding,r1.top(),textWidth,r1.height()); - nodeText.fgCol_=index.data(Qt::ForegroundRole).value(); - - if(hasRealBg) - { - int realW=6; - QRect r2(r1.x()+r1.width(),r1.top(),realW+1,r1.height()); - realShape.shape_=QPolygon(r2); - currentRight=r2.x()+r2.width(); - } - } - - //Icons area - QList pixLst; - QList pixRectLst; - - QVariant va=index.data(AbstractNodeModel::IconRole); - if(va.type() == QVariant::List) - { - QVariantList lst=va.toList(); - if(lst.count() >0) - { - int xp=currentRight+nodeBox_->iconPreGap; - int yp=itemRect.center().y()+1-nodeBox_->iconSize/2; - for(int i=0; i < lst.count(); i++) - { - int id=lst[i].toInt(); - if(id != -1) - { - pixLst << IconProvider::pixmap(id,nodeBox_->iconSize); - pixRectLst << QRect(xp,yp,nodeBox_->iconSize,nodeBox_->iconSize); - xp+=nodeBox_->iconSize+nodeBox_->iconGap; - } - } - - if(!pixLst.isEmpty()) - { - currentRight=xp-nodeBox_->iconGap; - } - } - } - - //The node number (optional) - QRect numRect; - QString numTxt; - bool hasNum=false; - - if(drawChildCount_) - { - va=index.data(AbstractNodeModel::NodeNumRole); - hasNum=(va.isNull() == false); - - if(hasNum) - { - numTxt="(" + va.toString() + ")"; - QFontMetrics fmNum(suiteNumFont_); - - int numWidth=fmNum.width(numTxt); - numRect = nodeText.br_; - numRect.setLeft(currentRight+fmNum.width('A')/2); - numRect.setWidth(numWidth); - currentRight=numRect.x()+numRect.width(); - } - } - - //The aborted reason - QRect reasonRect; - QString reasonTxt=index.data(AbstractNodeModel::AbortedReasonRole).toString(); - if(reasonTxt.contains('\n')) - reasonTxt=reasonTxt.split("\n").first(); - - bool hasReason=(!reasonTxt.isEmpty()); - if(hasReason) - { - QFontMetrics fmReason(abortedReasonFont_); - reasonRect = nodeText.br_; - reasonRect.setLeft(currentRight+fmReason.width('A')/2); - reasonTxt=fmReason.elidedText(reasonTxt,Qt::ElideRight,220); - reasonRect.setWidth(fmReason.width(reasonTxt)); - currentRight=reasonRect.x()+reasonRect.width(); - } - - //Define clipping - int rightPos=currentRight+1; - totalWidth=rightPos-option.rect.left(); - -#if 0 - const bool setClipRect = rightPos > option.rect.right(); - if(setClipRect) - { - painter->save(); - painter->setClipRect(option.rect); - } -#endif - - - renderNodeCell(painter,stateShape,realShape,nodeText,typeText,selected); - - //Draw icons - for(int i=0; i < pixLst.count(); i++) - { - //painter->fillRect(pixRectLst[i],Qt::black); - painter->drawPixmap(pixRectLst[i],pixLst[i]); - } - - //Draw number - if(hasNum) - { - painter->setPen(childCountColour); - painter->setFont(suiteNumFont_); - painter->drawText(numRect,Qt::AlignLeft | Qt::AlignVCenter,numTxt); - } - - //Draw aborted reason - if(hasReason) - { - painter->setPen(stateShape.col_.darker(120)); - painter->setFont(abortedReasonFont_); - painter->drawText(reasonRect,Qt::AlignLeft | Qt::AlignVCenter,reasonTxt); - } -#if 0 - if(setClipRect) - { - painter->restore(); - } -#endif - return totalWidth; -} - -void TreeNodeViewDelegate::renderServerCell(QPainter *painter,const NodeShape& stateShape, - const NodeText& text,bool selected) const -{ - renderNodeShape(painter,stateShape); - - //Draw text - painter->setFont(font_); - painter->setPen(QPen(text.fgCol_,0)); - painter->drawText(text.br_,Qt::AlignHCenter | Qt::AlignVCenter,text.text_); - - //selection - painter->setPen(nodeSelectPen_); - painter->setBrush(Qt::NoBrush); - QPolygon sel=stateShape.shape_; - - if(selected) - { - if(nodeStyle_ == BoxAndTextNodeStyle) - { - sel=sel.united(QPolygon(text.br_.adjusted(0,0,2,0))); - } - painter->drawRect(sel.boundingRect()); - } -} - -void TreeNodeViewDelegate::renderNodeCell(QPainter *painter,const NodeShape& stateShape,const NodeShape &realShape, - const NodeText& nodeText,const NodeText& typeText,bool selected) const -{ - renderNodeShape(painter,stateShape); - renderNodeShape(painter,realShape); - - //Draw type - if(drawNodeType_) - { - if(typeText.bgCol_.isValid()) - { - //painter->fillRect(typeText.br_,typeText.bgCol_); - painter->setBrush(typeText.bgCol_); - painter->setPen(typeText.bgCol_); - painter->drawRect(typeText.br_); - } - painter->setFont(typeFont_); - painter->setPen(typeText.fgCol_); - painter->setBrush(Qt::NoBrush); - painter->drawText(typeText.br_,Qt::AlignCenter,typeText.text_); - } - - //Draw text - painter->setFont(font_); - painter->setPen(QPen(nodeText.fgCol_,0)); - painter->drawText(nodeBox_->adjustTextRect(nodeText.br_),Qt::AlignHCenter | Qt::AlignVCenter,nodeText.text_); - - //selection - painter->setPen(nodeSelectPen_); - painter->setBrush(Qt::NoBrush); - QPolygon sel=stateShape.shape_; - - if(selected) - { - if(nodeStyle_ == BoxAndTextNodeStyle) - { - if(!realShape.shape_.isEmpty()) - sel=sel.united(realShape.shape_); - - sel=sel.united(QPolygon(nodeText.br_.adjusted(0,0,0,0))); - } - else - { - if(!realShape.shape_.isEmpty()) - sel=sel.united(realShape.shape_); - } - painter->drawRect(sel.boundingRect()); - } - - /* - - if(nodeStyle_ == BoxAndTextNodeStyle) - { - painter->setFont(typeFont_); - painter->setPen(); - painter->drawText(typeText.br_,Qt::AlignCenter,typeText.text_); - } - else - { - painter->setPen(Qt::NoPen); - - if(typeTxt == "S") - painter->setBrush(QColor(150,150,150)); //bgBrush); - else if(typeTxt == "F") - painter->setBrush(QColor(150,150,150)); //bgBrush); - else - painter->setBrush(QColor(190,190,190)); //bgBrush); - - painter->drawRect(typeRect); - - painter->setPen(Qt::white); - painter->setFont(typeFont_); - painter->drawText(typeRect,Qt::AlignCenter,typeTxt); - } - } - - //Draw text - painter->setFont(font_); - painter->setPen(QPen(fg,0)); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - - //selection - painter->setPen(nodeSelectPen_); - painter->setBrush(Qt::NoBrush); - QPolygon sel=stateShape.shape_; - - if(selected) - { - if(nodeStyle_ == BoxAndTextNodeStyle) - { - if(!realShape.shape_.isEmpty()) - sel=sel.united(realShape.shape_); - - sel=sel.united(QPolygon(textRect.adjusted(0,0,2,0))); - } - else - { - if(!realShape.shape_.isEmpty()) - sel=sel.united(realShape.shape_); - } - painter->drawRect(sel.boundingRect()); - //painter->drawPolygon(sel); - } -*/ - //painter->setRenderHints(QPainter::Antialiasing,false); -} - - -void TreeNodeViewDelegate::renderNodeShape(QPainter* painter,const NodeShape& shape) const -{ - if(shape.shape_.isEmpty()) - return; - - QColor bg=shape.col_; - QColor bgLight, borderCol; - Palette::statusColours(bg,bgLight,borderCol); - - QBrush bgBrush; - if(useStateGrad_) - { - grad_.setColorAt(0,bgLight); - grad_.setColorAt(1,bg); - bgBrush=QBrush(grad_); - } - else - bgBrush=QBrush(bg); - - //Fill shape - painter->setPen(borderCol); - painter->setBrush(bgBrush); - painter->drawPolygon(shape.shape_); -} - -void TreeNodeViewDelegate::renderTimer(QPainter *painter,QRect target,int remaining, int total) const -{ - QImage img(target.width(),target.height(),QImage::Format_ARGB32_Premultiplied); - QRect r=img.rect().adjusted(2,2,-2,-2); - img.fill(Qt::transparent); - QPainter p(&img); - p.setRenderHint(QPainter::Antialiasing,true); - - int angle=static_cast(360.*static_cast(total-remaining)/static_cast(total)); - /*angle-=90.; - if(angle >=0 && angle <= 90) angle=90-angle; - else - angle=450-angle;*/ - - QColor c(43,97,158); - - QBrush b(c); - p.setBrush(b); - p.setPen(c); - p.drawPie(r,90*16,-angle*16); - p.setBrush(Qt::NoBrush); - p.drawEllipse(r); - - painter->drawImage(target,img,img.rect()); -} - - -void TreeNodeViewDelegate::renderServerUpdate(QPainter* painter,const ServerUpdateData& data) const -{ - QFont font(font_); - font.setPointSize(font_.pointSize()-1); - QFontMetrics fm(font); - painter->setFont(font); - painter->setPen(Qt::black); - - QColor minCol=QColor(198,215,253); - QColor maxCol=QColor(43,97,158); - - QRect r1=data.br_; - r1.setWidth(fm.width(data.prevText_)); - painter->setPen(minCol); - //painter->setPen(Qt::red); - painter->drawText(r1,Qt::AlignLeft | Qt::AlignVCenter,data.prevText_); - - if(!data.prevText_.isEmpty()) - { - QRect r2=data.br_; - r2.setX(data.br_.right()-fm.width(data.nextText_)); - //painter->setPen(QColor(1,128,73)); - painter->setPen(maxCol); - painter->drawText(r2,Qt::AlignRight | Qt::AlignVCenter,data.nextText_); - - int dh=(data.br_.height()-fm.height()+1)/2; - QRect r=data.br_.adjusted(r1.width()+4,2*dh,-r2.width()-4,-2*dh); - - int pos=static_cast(data.prog_* r.width()); - QRect rPrev=r.adjusted(0,0,-(r.width()-pos),0); - - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(1,0); - QColor posCol=interpolate(minCol,maxCol,data.prog_); - - grad.setColorAt(0,minCol); - grad.setColorAt(1,posCol); - painter->setPen(Qt::NoPen); - painter->setBrush(grad); - painter->drawRect(rPrev); - - painter->setBrush(Qt::NoBrush); - painter->setPen(QColor(190,190,190)); - painter->drawRect(r); - } -} - -void TreeNodeViewDelegate::widthHintServer(const QModelIndex& index,int& itemWidth, QString text) const -{ - ServerHandler* server=static_cast(index.data(AbstractNodeModel::ServerPointerRole).value()); - Q_ASSERT(server); - - QFontMetrics fm(font_); - - //The initial filled rect. We only care of the width - QRect itemRect(nodeBox_->leftMargin,0,10,10); - int currentRight=itemRect.left(); - - NodeShape stateShape; - - NodeText nodeText; - nodeText.text_=text; - int textWidth=fm.width(text); - - if(nodeStyle_ == BoxAndTextNodeStyle) - { - currentRight+=2+textWidth+nodeBox_->leftPadding; - } - else - { - currentRight+=textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding; - } - - //Icons area - Q_ASSERT(model_); - int pixNum=model_->iconNum(server->vRoot()); - if(pixNum > 0) - { - currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; - } - - //The info rectangle (optional) - QString infoTxt=index.data(AbstractNodeModel::InfoRole).toString(); - bool hasInfo=(infoTxt.isEmpty() == false); - - if(hasInfo) - { - fm=QFontMetrics(serverInfoFont_); - int infoWidth=fm.width(infoTxt); - currentRight+=fm.width('A')+infoWidth; - } - - //The load icon (optional) - bool hasLoad=index.data(AbstractNodeModel::LoadRole).toBool(); - Animation* an=0; - - //Update load animation - if(hasLoad) - { - an=animation_->find(Animation::ServerLoadType,true); - currentRight+=fm.width('A')+an->scaledSize().width(); - } - //Stops load animation - else - { - //if((an=animation_->find(Animation::ServerLoadType,false)) != NULL) - // an->removeTarget(server->vRoot()); - } - - //The node number (optional) - QString numTxt; - bool hasNum=false; - - if(drawChildCount_) - { - QVariant va=index.data(AbstractNodeModel::NodeNumRole); - hasNum=(va.isNull() == false); - if(hasNum) - { - numTxt="(" + QString::number(va.toInt()) + ")"; - QFontMetrics fmNum(serverNumFont_); - int numWidth=fmNum.width(numTxt); - currentRight+=fmNum.width('A')/2+numWidth; - } - } - - itemWidth=currentRight+1; -} - -int TreeNodeViewDelegate::nodeWidth(const QModelIndex& index,QString text) const -{ - VNode* node=static_cast(index.data(AbstractNodeModel::NodePointerRole).value()); - Q_ASSERT(node); - - int offset=nodeBox_->leftMargin; - QFontMetrics fm(font_); - - //The initial filled rect. We only care about the width! - QRect itemRect(offset,0,10,10); - int currentRight=itemRect.left(); - - bool hasRealBg=node->isSuspended(); - - //Node type - QFontMetrics fmType(typeFont_); - int typeWidth=fmType.width(" S"); - - //The text rectangle - int textWidth=fm.width(text); - - if(nodeStyle_ == BoxAndTextNodeStyle) - { - //state box - currentRight+=itemRect.height(); - if(hasRealBg) - { - currentRight+=1; - } - currentRight+=2; - - //node name - currentRight+=textWidth+nodeBox_->leftPadding; - } - //Classic style - else - { - //node type box - if(drawNodeType_) - { - currentRight+=typeWidth+2; - } - - //node name + state - currentRight+=textWidth+nodeBox_->leftPadding+nodeBox_->rightPadding; - - if(hasRealBg) - { - int realW=6; - currentRight+=realW+1; - } - } - - //Icons area - int pixNum=0; - - //in some subclasses we might not have a model_ - if(model_) - { - pixNum=model_->iconNum(node); - if(pixNum > 0) - { - currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; - } - } - else - { - QVariant va=index.data(AbstractNodeModel::IconRole); - if(va.type() == QVariant::List) - { - QVariantList lst=va.toList(); - pixNum=lst.count(); - } - } - - if(pixNum > 0) - { - currentRight+=nodeBox_->iconPreGap+pixNum*nodeBox_->iconSize + (pixNum-1)*nodeBox_->iconGap; - } - - - //The node number (optional) - if(drawChildCount_) - { - if(node->isTopLevel()) - { - QVariant va=index.data(AbstractNodeModel::NodeNumRole); - if(va.isNull() == false) - { - QString numTxt="(" + va.toString() + ")"; - QFontMetrics fmNum(suiteNumFont_); - int numWidth=fmNum.width(numTxt); - currentRight+=fmNum.width('A')/2+numWidth; - } - } - } - - //The aborted reason - if(node->isAborted()) - { - QString reasonTxt=QString::fromStdString(node->abortedReason()); - if(reasonTxt.contains('\n')) - reasonTxt=reasonTxt.split("\n").first(); - - bool hasReason=(!reasonTxt.isEmpty()); - if(hasReason) - { - QFontMetrics fmReason(abortedReasonFont_); - reasonTxt=fmReason.elidedText(reasonTxt,Qt::ElideRight,220); - currentRight+=fmReason.width('A')/2+fmReason.width(reasonTxt); - } - } - - return currentRight+1; -} - - -QString TreeNodeViewDelegate::formatTime(int timeInSec) const -{ - int h=timeInSec/3600; - int r=timeInSec%3600; - int m=r/60; - int s=r%60; - - QTime t(h,m,s); - if(h > 0) - return t.toString("h:mm:ss"); - else - return t.toString("m:ss"); - - return QString(); -} - -QColor TreeNodeViewDelegate::interpolate(QColor c1,QColor c2,float r) const -{ - return QColor::fromRgbF(c1.redF()+r*(c2.redF()-c1.redF()), - c1.greenF()+r*(c2.greenF()-c1.greenF()), - c1.blueF()+r*(c2.blueF()-c1.blueF())); -} diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeViewDelegate.hpp ecflow-4.11.1/Viewer/src/TreeNodeViewDelegate.hpp --- ecflow-4.9.0/Viewer/src/TreeNodeViewDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TREENODEVIEWDELEGATEBASE_HPP -#define TREENODEVIEWDELEGATEBASE_HPP - -#include -#include -#include -#include - -#include "NodeViewDelegate.hpp" -#include "VProperty.hpp" - -#include - -class AnimationHandler; -class PropertyMapper; -class NodeShape; -class NodeText; -class ServerUpdateData; -class TreeNodeModel; - -class TreeNodeViewDelegate : public NodeViewDelegate -{ -Q_OBJECT - -public: - explicit TreeNodeViewDelegate(TreeNodeModel* model,QWidget *parent=0); - ~TreeNodeViewDelegate(); - - bool isSingleHeight(int h) const; - - //from baseclass - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const {} - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex& index ) const; - - //custom implementations - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index,QSize&) const; - void sizeHint(const QModelIndex& index,int& w,int& h) const; - - -Q_SIGNALS: - void sizeHintChangedGlobal(); - -protected: - void updateSettings(); - - int renderServer(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const; - - int renderNode(QPainter *painter,const QModelIndex& index, - const QStyleOptionViewItem& option,QString text) const; - - void renderServerCell(QPainter *painter,const NodeShape& stateShape, - const NodeText& text,bool selected) const; - - void renderNodeCell(QPainter *painter,const NodeShape& stateShape,const NodeShape &realShape, - const NodeText& nodeText,const NodeText& typeText,bool selected) const; - - void renderNodeShape(QPainter* painter,const NodeShape& shape) const; - void renderTimer(QPainter *painter,QRect target, int remaining, int total) const; - void renderServerUpdate(QPainter* painter,const ServerUpdateData&) const; - - void widthHintServer(const QModelIndex& index,int& itemWidth,QString text) const; - int nodeWidth(const QModelIndex& index,QString text) const; - - QString formatTime(int timeInSec) const; - QColor interpolate(QColor c1,QColor c2,float r) const; - - enum NodeStyle {ClassicNodeStyle,BoxAndTextNodeStyle}; - - AnimationHandler* animation_; - - int nodeRectRad_; - bool drawChildCount_; - NodeStyle nodeStyle_; - - bool drawNodeType_; - QColor typeBgCol_; - - QFont serverNumFont_; - QFont suiteNumFont_; - QFont serverInfoFont_; - QFont abortedReasonFont_; - QFont typeFont_; - QColor bgCol_; - - TreeNodeModel* model_; -}; - -#endif // TREENODEVIEWDELEGATEBASE_HPP - - - diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeView.hpp ecflow-4.11.1/Viewer/src/TreeNodeView.hpp --- ecflow-4.9.0/Viewer/src/TreeNodeView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,111 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TreeNodeView_HPP_ -#define TreeNodeView_HPP_ - -#include - -#include "NodeViewBase.hpp" - -#include "VInfo.hpp" -#include "VProperty.hpp" - -class AbstractNodeView; -class ActionHandler; -class Animation; -class ExpandNode; -class TableNodeSortModel; -class PropertyMapper; -class TreeNodeModel; -class StandardNodeViewDelegategate; -class VTreeNode; - -class TreeNodeView : public QObject, public NodeViewBase, public VPropertyObserver -{ -Q_OBJECT - -public: - TreeNodeView(AbstractNodeView* view,TreeNodeModel* model,NodeFilterDef* filterDef,QWidget *parent=0); - ~TreeNodeView(); - - void reload(); - void rerender(); - QWidget* realWidget(); - QObject* realObject(); - VInfo_ptr currentSelection(); - void setCurrentSelection(VInfo_ptr n); - void selectFirstServer(); - - void notifyChange(VProperty* p); - - void readSettings(VSettings* vs) {} - void writeSettings(VSettings* vs) {} - -public Q_SLOTS: - void slotContextMenu(const QPoint &position); - void slotViewCommand(VInfo_ptr,QString); -#if 0 - void slotSaveExpand(); - void slotRestoreExpand(); -#endif - void slotSaveExpand(const VTreeNode* node); - void slotRestoreExpand(const VTreeNode* node); - void slotRepaint(Animation*); - void slotRerender(); - void slotSizeHintChangedGlobal(); - -protected Q_SLOTS: - void slotDoubleClickItem(const QModelIndex&); - void selectionChanged(const QItemSelection &selected, const QItemSelection &deselected); - -Q_SIGNALS: - void selectionChanged(VInfo_ptr); - void infoPanelCommand(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - -protected: - QModelIndexList selectedList(); - void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); - - void saveExpand(ExpandNode *parentExpand,const QModelIndex& idx); - void restoreExpand(ExpandNode *expand,const VNode* node); - void expandTo(const QModelIndex& idxTo); - void saveExpandAll(const QModelIndex& idx); - void saveCollapseAll(const QModelIndex& idx); - - void setCurrentSelectionFromExpand(VInfo_ptr info); - void regainSelectionFromExpand(); - - void adjustBackground(QColor col); - void adjustIndentation(int); - void adjustDrawBranchLine(bool b); - void adjustBranchLineColour(QColor col); - void adjustServerToolTip(bool); - void adjustNodeToolTip(bool); - void adjustAttributeToolTip(bool); - - AbstractNodeView *view_; - TreeNodeModel* model_; - ActionHandler* actionHandler_; - bool needItemsLayout_; - PropertyMapper* prop_; - QMap styleSheet_; - bool setCurrentIsRunning_; - bool setCurrentFromExpandIsRunning_; - bool canRegainCurrentFromExpand_; - VInfo_ptr lastSelection_; - bool inStartUp_; -}; - -#endif - - - diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeWidget.cpp ecflow-4.11.1/Viewer/src/TreeNodeWidget.cpp --- ecflow-4.9.0/Viewer/src/TreeNodeWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,354 +0,0 @@ - -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#include "TreeNodeWidget.hpp" - -#include -#include - -#include "AbstractNodeModel.hpp" -#include "DashboardDock.hpp" -#include "CompactView.hpp" -#include "StandardView.hpp" -#include "NodePathWidget.hpp" -#include "NodeViewBase.hpp" -#include "TreeNodeModel.hpp" -#include "TreeNodeView.hpp" -#include "UiLog.hpp" -#include "VFilter.hpp" -#include "VConfig.hpp" -#include "VModelData.hpp" -#include "VSettings.hpp" -#include "WidgetNameProvider.hpp" - -#include "FilterWidget.hpp" - -AttributeFilter* TreeNodeWidget::lastAtts_=NULL; - -TreeNodeWidget::TreeNodeWidget(ServerFilter* serverFilter,QWidget* parent) : - NodeWidget("tree",serverFilter,parent), - viewLayoutMode_(StandardLayoutMode), - layoutProp_(0) -{ - //Init qt-creator form - setupUi(this); - - if(!lastAtts_) - { - lastAtts_=new AttributeFilter(); - } - - initAtts(); - - //Create the breadcrumbs (it will be reparented) - bcWidget_=new NodePathWidget(this); - - //This defines how to filter the nodes in the tree. We only want to filter according to node status. - filterDef_=new NodeFilterDef(serverFilter_,NodeFilterDef::NodeStateScope); - - //Create the tree model. It uses the datahandler to access the data. - model_=new TreeNodeModel(serverFilter_,filterDef_,atts_,icons_,this); - - //Create the view - QHBoxLayout *hb=new QHBoxLayout(viewHolder_); - hb->setContentsMargins(0,0,0,0); - hb->setSpacing(0); - - layoutProp_=VConfig::instance()->find("view.tree.layoutStyle"); - Q_ASSERT(layoutProp_); - layoutProp_->addObserver(this); - - if(layoutProp_->value() == "compact") - { - viewLayoutMode_=CompactLayoutMode; - } - - Q_ASSERT(view_==0); - setViewLayoutMode(viewLayoutMode_); - - connect(model_,SIGNAL(firstScanEnded(const VTreeServer*)), - this,SLOT(firstScanEnded(const VTreeServer*))); - - connect(bcWidget_,SIGNAL(selected(VInfo_ptr)), - this,SLOT(slotSelectionChangedInBc(VInfo_ptr))); - - connect(atts_,SIGNAL(changed()), - this,SLOT(slotAttsChanged())); - - //This will not emit the trigered signal of the action!! - //Synchronise the action and the breadcrumbs state - actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); - - //The node status filter is exposed via a menu. So we need a reference to it. - states_=filterDef_->nodeState(); - - viewHolder_->setObjectName("h"); - WidgetNameProvider::nameChildren(this); -} - -TreeNodeWidget::~TreeNodeWidget() -{ -} - -void TreeNodeWidget::setViewLayoutMode(TreeNodeWidget::ViewLayoutMode mode) -{ - if(view_ && viewLayoutMode_ == mode) - return; - - viewLayoutMode_ = mode; - - if(view_) - { - QWidget *realW=view_->realWidget(); - viewHolder_->layout()->removeWidget(realW); - delete view_; - view_=0; - delete realW; - model_->data()->deleteExpandState(); - } - - if(viewLayoutMode_ == CompactLayoutMode) - { - TreeNodeModel* realModel=static_cast(model_); - - TreeNodeView* gv=new TreeNodeView(new CompactView(realModel,this), - realModel,filterDef_,this); - viewHolder_->layout()->addWidget(gv->realWidget()); - //Store the pointer to the (non-QObject) base class of the view!!! - view_=gv; - } - else - { - TreeNodeModel* realModel=static_cast(model_); - - TreeNodeView *tv=new TreeNodeView(new StandardView(realModel,this), - realModel,filterDef_,this); - viewHolder_->layout()->addWidget(tv->realWidget()); - //Store the pointer to the (non-QObject) base class of the view!!! - view_=tv; - } - - //Signals-slots - connect(view_->realObject(),SIGNAL(selectionChanged(VInfo_ptr)), - this,SLOT(slotSelectionChangedInView(VInfo_ptr))); - - connect(view_->realObject(),SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(popInfoPanel(VInfo_ptr,QString))); - - connect(view_->realObject(),SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); - - connect(model_,SIGNAL(clearBegun(const VTreeNode*)), - view_->realObject(),SLOT(slotSaveExpand(const VTreeNode*))); - - connect(model_,SIGNAL(scanEnded(const VTreeNode*)), - view_->realObject(),SLOT(slotRestoreExpand(const VTreeNode*))); - - connect(model_,SIGNAL(rerender()), - view_->realObject(),SLOT(slotRerender())); - - connect(model_,SIGNAL(filterUpdateRemoveBegun(const VTreeNode*)), - view_->realObject(),SLOT(slotSaveExpand(const VTreeNode*))); - - connect(model_,SIGNAL(filterUpdateAddEnded(const VTreeNode*)), - view_->realObject(),SLOT(slotRestoreExpand(const VTreeNode*))); - -} - -void TreeNodeWidget::initAtts() -{ - if(VProperty *prop=VConfig::instance()->find("view.tree.attributesPolicy")) - { - if(prop->valueAsStdString() == "last") - { - atts_->setCurrent(lastAtts_->current()); - } - else if(VProperty *propDef=VConfig::instance()->find("view.tree.defaultAttributes")) - { - atts_->setCurrent(propDef->value().toString().split("/")); - } - } -} - -void TreeNodeWidget::populateDockTitleBar(DashboardDockTitleWidget* tw) -{ - //Builds the menu for the settings tool button - QMenu *menu=new QMenu(this); - menu->setTearOffEnabled(true); - - menu->addAction(actionBreadcrumbs); - QMenu *menuState=new QMenu(this); - QMenu *menuType=new QMenu(this); - QMenu *menuIcon=menu->addMenu(tr("Icon")); - - menuState->setTearOffEnabled(true); - menuType->setTearOffEnabled(true); - menuIcon->setTearOffEnabled(true); - - //stateFilterMenu_=new StateFilterMenu(menuState,filter_->menu()); - attrFilterMenu_=new VParamFilterMenu(menuType,atts_,"Show attributes",VParamFilterMenu::ShowMode); - iconFilterMenu_=new VParamFilterMenu(menuIcon,icons_,"Show icons",VParamFilterMenu::ShowMode, - VParamFilterMenu::PixmapDecor); - stateFilterMenu_=new VParamFilterMenu(menuState,states_,"Show statuses", - VParamFilterMenu::ShowMode,VParamFilterMenu::ColourDecor); - - //Sets the menu on the toolbutton - tw->optionsTb()->setMenu(menu); - - //Add the bc to the titlebar (bc will have a new parent) - tw->setBcWidget(bcWidget_); - - QList acLst; - - QAction* acState=new QAction(this); - acState->setIcon(QPixmap(":viewer/status.svg")); - acState->setToolTip("Filter by status"); - acState->setMenu(menuState); - acLst << acState; - - QAction* acAttr=new QAction(this); - acAttr->setIcon(QPixmap(":viewer/attribute.svg")); - acAttr->setToolTip("Show attributes"); - acAttr->setMenu(menuType); - acLst << acAttr; - - tw->addActions(acLst); -} - -void TreeNodeWidget::slotSelectionChangedInView(VInfo_ptr info) -{ - updateActionState(info); - bcWidget_->setPath(info); - if(broadcastSelection()) - Q_EMIT selectionChanged(info); -} - - -void TreeNodeWidget::on_actionBreadcrumbs_triggered(bool b) -{ - if(b) - { - bcWidget_->setMode(NodePathWidget::GuiMode); - } - else - { - bcWidget_->setMode(NodePathWidget::TextMode); - } -} - -void TreeNodeWidget::rerender() -{ - bcWidget_->rerender(); - view_->rerender(); -} - - -bool TreeNodeWidget::initialSelectionInView() -{ - //Seeting the initail selection is probably unsuccessful because at - //this point the servers are probably not fully loaded - VInfo_ptr selInfo=VInfo::createFromPath(firstSelectionPath_); - if(selInfo) - view_->setCurrentSelection(selInfo); - else - view_->selectFirstServer(); - - return true; -} - -//When the first successful scan ended we try to set the initial selection -void TreeNodeWidget::firstScanEnded(const VTreeServer* s) -{ - VInfo_ptr selInfo=VInfo::createFromPath(firstSelectionPath_); - if(selInfo && selInfo->server() == s->realServer()) - { - view_->setCurrentSelection(selInfo); - } -} - -void TreeNodeWidget::slotAttsChanged() -{ - lastAtts_->setCurrent(atts_->current()); -} - -void TreeNodeWidget::notifyChange(VProperty* p) -{ - if(p == layoutProp_) - { - if(layoutProp_->value() == "compact") - { - setViewLayoutMode(CompactLayoutMode); - } - else - { - setViewLayoutMode(StandardLayoutMode); - } - } -} - -void TreeNodeWidget::writeSettings(VComboSettings* vs) -{ - vs->put("type",type_); - vs->put("dockId",id_); - - VInfo_ptr sel=currentSelection(); - if(sel) - { - vs->put("selection",sel->storedPath()); - } - - bcWidget_->writeSettings(vs); - - states_->writeSettings(vs); - atts_->writeSettings(vs); - icons_->writeSettings(vs); - - DashboardWidget::writeSettings(vs); -} - -void TreeNodeWidget::readSettings(VComboSettings* vs) -{ - std::string type=vs->get("type",""); - if(type != type_) - return; - - //The selection on last exit. We will use it later when the server is fully loaded. - firstSelectionPath_=vs->get("selection",""); - - //This will not emit the changed signal. So the "observers" will - //not notice the change. - states_->readSettings(vs); - atts_->readSettings(vs); - icons_->readSettings(vs); - - lastAtts_->setCurrent(atts_->current()); - - //The model at this point is inactive (not using its data). We make it active: - // -it will instruct its data provider to filter the data according - // to the current settings - // -it will load and display the data - model_->active(true); - - //-------------------------- - //Breadcrumbs - //-------------------------- - - bcWidget_->readSettings(vs); - - //Synchronise the action and the breadcrumbs state - //This will not emit the trigered signal of the action!! - actionBreadcrumbs->setChecked(bcWidget_->isGuiMode()); - - attrFilterMenu_->reload(); - iconFilterMenu_->reload(); - stateFilterMenu_->reload(); - - DashboardWidget::readSettings(vs); -} diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeWidget.hpp ecflow-4.11.1/Viewer/src/TreeNodeWidget.hpp --- ecflow-4.9.0/Viewer/src/TreeNodeWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/***************************** LICENSE START *********************************** - - Copyright 2009-2017 ECMWF and INPE. This software is distributed under the terms - of the Apache License version 2.0. In applying this license, ECMWF does not - waive the privileges and immunities granted to it by virtue of its status as - an Intergovernmental Organization or submit itself to any jurisdiction. - - ***************************** LICENSE END *************************************/ - -#ifndef TREENODEWIDGET_HPP_ -#define TREENODEWIDGET_HPP_ - -#include "ui_TreeNodeWidget.h" - -#include "NodeWidget.hpp" -#include "VProperty.hpp" - -class AttributeFilter; -class NodeStateFilter; -class ServerFilter; -class VParamFilterMenu; -class VSettings; -class VTreeServer; -class TreeNodeWidget : public NodeWidget, public VPropertyObserver, protected Ui::TreeNodeWidget -{ -Q_OBJECT - -public: - TreeNodeWidget(ServerFilter*,QWidget* parent=0); - ~TreeNodeWidget(); - - void populateDockTitleBar(DashboardDockTitleWidget* tw); - - void rerender(); - bool initialSelectionInView(); - void writeSettings(VComboSettings*); - void readSettings(VComboSettings*); - - void notifyChange(VProperty*); - -protected Q_SLOTS: - void on_actionBreadcrumbs_triggered(bool b); - void slotSelectionChangedInView(VInfo_ptr info); - void slotAttsChanged(); - void firstScanEnded(const VTreeServer*); - -protected: - enum ViewLayoutMode {StandardLayoutMode,CompactLayoutMode}; - - void initAtts(); - void detachedChanged() {} - void setViewLayoutMode(ViewLayoutMode); - - VParamFilterMenu *stateFilterMenu_; - VParamFilterMenu *attrFilterMenu_; - VParamFilterMenu *iconFilterMenu_; - - ViewLayoutMode viewLayoutMode_; - VProperty* layoutProp_; - - static AttributeFilter* lastAtts_; - - std::string firstSelectionPath_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TreeNodeWidget.ui ecflow-4.11.1/Viewer/src/TreeNodeWidget.ui --- ecflow-4.9.0/Viewer/src/TreeNodeWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeNodeWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ - - - TreeNodeWidget - - - - 0 - 0 - 683 - 491 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 0 - - - - - - - - true - - - Breadcrumbs - - - Sho/hide breadcrumbs - - - - - true - - - Frozen - - - Do not update panel when node changes - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/TreeView.cpp ecflow-4.11.1/Viewer/src/TreeView.cpp --- ecflow-4.9.0/Viewer/src/TreeView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TreeView.hpp" - -#include - -TreeView::TreeView(QWidget* parent) : QTreeView(parent) -{ - //!!!!We need to do it because: - //The background colour between the views left border and the nodes cannot be - //controlled by delegates or stylesheets. It always takes the QPalette::Highlight - //colour from the palette. Here we set this to transparent so that Qt could leave - //this are empty and we will fill it appropriately in our delegate. - - QPalette pal=palette(); - pal.setColor(QPalette::Highlight,QColor(255,255,255,0)); - setPalette(pal); -} - - diff -Nru ecflow-4.9.0/Viewer/src/TreeView.hpp ecflow-4.11.1/Viewer/src/TreeView.hpp --- ecflow-4.9.0/Viewer/src/TreeView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TreeView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ -#ifndef VIEWER_SRC_TREEVIEW_HPP_ -#define VIEWER_SRC_TREEVIEW_HPP_ - -#include - -class TreeView : public QTreeView -{ -public: - explicit TreeView(QWidget* parent=0); -}; - -#endif /* VIEWER_SRC_TREEVIEW_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TriggerCollector.cpp ecflow-4.11.1/Viewer/src/TriggerCollector.cpp --- ecflow-4.9.0/Viewer/src/TriggerCollector.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerCollector.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,197 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggerCollector.hpp" - -#include "UiLog.hpp" -#include "VAttribute.hpp" -#include "VItem.hpp" -#include "VItemPathParser.hpp" -#include "VNode.hpp" - -#include - -#define _UI_TRIGGERCOLLECTOR_DEBUG - -TriggerListCollector::~TriggerListCollector() -{ - clear(); -} - -bool TriggerListCollector::add(VItem* t, VItem* dep,Mode mode) -{ - TriggerListItem *item=new TriggerListItem(t,dep,mode) ; - items_.push_back(item); - return true; - -#if 0 - if(dep) - { - UiLog().dbg() << " dep=" << dep->typeName() << " " + dep->strName(); - UiLog().dbg() << " =" << item->dep_->typeName() << " " << item->dep_->strName()); - } -#endif -} - -void TriggerListCollector::setDependency(bool b) -{ - extended_=b; - clear(); -} - -void TriggerListCollector::clear() -{ - /*for(size_t i=0; i < items_.size(); i++) - { - delete items_[i]; - }*/ - items_.clear(); -} - - -bool TriggerChildCollector::add(VItem* t, VItem*,Mode) -{ - if(!t->isAncestor(node_)) - { - // child_ is a kid of node_ whose trigger is outside its subtree - return collector_->add(t,child_,TriggerCollector::Child); - } - return false; -} - -bool TriggerParentCollector::add(VItem* t, VItem*,Mode) -{ - return collector_->add(t,parent_,TriggerCollector::Parent); -} - -bool TriggeredCollector::add(VItem* trigger, VItem*,Mode) -{ - if(VNode *n=trigger->isNode()) - { - n->addTriggeredData(node_); - } - return false; - - //else if(trigger->isAttribute()) - // trigger->parent()->addTriggeredData(node_,trigger); -} - -const std::set& TriggerTableItem::modes() const -{ - if(modes_.empty()) - { - for(std::size_t i=0; i < deps_.size(); i++) - { - modes_.insert(deps_[i].mode()); - } - } - return modes_; -} - -//===================================== -// TriggerTableCollector -//===================================== - -TriggerTableCollector::~TriggerTableCollector() -{ - clear(); -} - -bool TriggerTableCollector::add(VItem* trigger, VItem* dep,Mode mode) -{ - Q_ASSERT(trigger); - - TriggerTableItem *item=0; - for(std::size_t i=0; i < items_.size(); i++) - { - if(items_[i]->item() == trigger) - { - item=items_[i]; - break; - } - } - - if(!item) - { - item=new TriggerTableItem(trigger); - items_.push_back(item); - } - - item->addDependency(dep,mode); - return true; -} - -void TriggerTableCollector::setDependency(bool b) -{ - extended_=b; - clear(); -} - -void TriggerTableCollector::clear() -{ - for(size_t i=0; i < items_.size(); i++) - { - delete items_[i]; - } - items_.clear(); -} - -bool TriggerTableCollector::contains(TriggerTableItem* item) const -{ - return (std::find(items_.begin(),items_.end(), item) != items_.end()); -} - -bool TriggerTableCollector::contains(const VNode* node,bool attrParents) const -{ - for(size_t i=0; i < items_.size(); i++) - { - if(VItem* it=items_[i]->item()) - { - if(VNode *n=it->isNode()) - { - if(n == node) - return true; - } - else if(attrParents) - { - if (VAttribute *a=it->isAttribute()) - if(a->parent() == node) - return true; - } - } - - } - - return false; -} - -TriggerTableItem* TriggerTableCollector::find(const VItem* item) const -{ - for(size_t i=0; i < items_.size(); i++) - { - if(items_[i]->item() == item) - return items_[i]; - } - return 0; -} - -TriggerTableItem* TriggerTableCollector::findByContents(const VItem* item) const -{ - if(!item) - return 0; - - for(size_t i=0; i < items_.size(); i++) - { - if(item->sameContents(items_[i]->item())) - { - return items_[i]; - } - } - return 0; -} diff -Nru ecflow-4.9.0/Viewer/src/TriggerCollector.hpp ecflow-4.11.1/Viewer/src/TriggerCollector.hpp --- ecflow-4.9.0/Viewer/src/TriggerCollector.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerCollector.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,203 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TRIGGERCOLLECTOR_HPP -#define TRIGGERCOLLECTOR_HPP - -#include -#include -#include - -#include - -class TriggerListItem; -class VItem; - -#include "VNode.hpp" - -class TriggerCollector -{ -public: - TriggerCollector() {} - virtual ~TriggerCollector() {} - - enum Mode { Normal = 0, // Normal trigger_node - Parent = 1, // Through parent - Child = 2, // Through child - Hierarchy = 3 // Through child - }; - - virtual bool add(VItem*, VItem*,Mode) = 0; - virtual bool scanParents() { return false; } - virtual bool scanKids() { return false; } - virtual bool scanSelf() { return true; } - -private: - TriggerCollector(const TriggerCollector&); - TriggerCollector& operator=(const TriggerCollector&); -}; - -class TriggerListCollector : public TriggerCollector -{ -public: - TriggerListCollector(bool extended) : - extended_(extended) {} - - ~TriggerListCollector(); - bool add(VItem*, VItem*,Mode); - bool scanParents() { return extended_; } - bool scanKids() { return extended_; } - void setDependency(bool); - void clear(); - size_t size() const {return items_.size();} - - const std::vector& items() const {return items_;} - -protected: - bool extended_; - std::vector items_; -}; - -class TriggerChildCollector : public TriggerCollector -{ -public: - TriggerChildCollector(VItem *n,VItem* child,TriggerCollector* collector) : - node_(n), child_(child), collector_(collector) {} - - bool add(VItem*, VItem*,Mode); - -private: - VItem* node_; - VItem* child_; - TriggerCollector* collector_; -}; - -class TriggerParentCollector : public TriggerCollector -{ -public: - TriggerParentCollector(VItem* parent,TriggerCollector* collector) : - parent_(parent), collector_(collector) {} - - bool add(VItem*, VItem*,Mode); - -private: - VItem* parent_; - TriggerCollector* collector_; -}; - -class TriggeredCollector : public TriggerListCollector -{ -public: - TriggeredCollector(VNode* n) : - TriggerListCollector(false), node_(n) {} - bool add(VItem*, VItem*,Mode); - -private: - VItem* node_; -}; - -class TriggerListItem -{ -public: - TriggerListItem(VItem* t,VItem* dep,TriggerCollector::Mode mode) : - t_(t), dep_(dep), mode_(mode) {} - - VItem* item() const {return t_;} - VItem* dep() const {return dep_;} - TriggerCollector::Mode mode() const {return mode_;} - -protected: - VItem* t_; //trigger or triggered - VItem* dep_; - TriggerCollector::Mode mode_; -}; - -class TriggerDependencyItem -{ -public: - TriggerDependencyItem(VItem* dep,TriggerCollector::Mode mode) : - dep_(dep), mode_(mode) {} - - VItem* dep() const {return dep_;} - TriggerCollector::Mode mode() const {return mode_;} - -protected: - VItem* dep_; - TriggerCollector::Mode mode_; -}; - -class TriggerTableItem -{ -public: - TriggerTableItem(VItem* t) :t_(t){} - - void addDependency(VItem* dep,TriggerCollector::Mode mode) - {deps_.push_back(TriggerDependencyItem(dep,mode));} - - VItem* item() const {return t_;} - const std::vector& dependencies() const {return deps_;} - const std::set& modes() const; - -protected: - VItem* t_; //trigger or triggered - std::vector deps_; - mutable std::set modes_; -}; - - -class TriggerTableCollector : public TriggerCollector -{ -public: - TriggerTableCollector(bool extended) : - extended_(extended) {} - - ~TriggerTableCollector(); - bool add(VItem*, VItem*,Mode); - bool scanParents() { return extended_; } - bool scanKids() { return extended_; } - void setDependency(bool); - void clear(); - size_t size() const {return items_.size();} - - bool contains(TriggerTableItem*) const; - bool contains(const VNode*,bool attrParents=true) const; - TriggerTableItem* find(const VItem* item) const; - TriggerTableItem* findByContents(const VItem* item) const; - const std::vector& items() const {return items_;} - -protected: - bool extended_; - std::vector items_; -}; - -#if 0 -class nl1 : public trigger_lister { - int n_; - graph_layout& t_; - node* g_; - bool e_; -public: - - nl1(graph_layout& t,node* g,bool e) : n_(0), t_(t), g_(g), e_(e) {} - - void next_node(node& n,node* p,int mode,node* t) { - t_.relation(&n,g_,p,mode,t); - n_++; - } - - Boolean parents() { return e_; } - Boolean kids() { return e_; } - - int count() { return n_; } -}; -#endif - - -#endif // TRIGGERCOLLECTOR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/trigger.css ecflow-4.11.1/Viewer/src/trigger.css --- ecflow-4.9.0/Viewer/src/trigger.css 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/trigger.css 1970-01-01 00:00:00.000000000 +0000 @@ -1,85 +0,0 @@ -/*============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================*/ - -p -{ - border:3px; - border-style:solid; - border-color:#FF0000; - padding: 1em; -} - -a:link -{ - text-decoration:none; - color: #0645AD; -} - -a:hover -{ - text-decoration:bold; - color: #0645AD; -} - -a:link.chp -{ - text-decoration:none; - color: #1a1f20; - font-weight: bold; -} - -th -{ - padding-left: 4px; - padding-top: 4px; - padding-bottom: 4px; - background-color: #5f6e95; - color: #ffffff; - font-size: 12px; - text-align: left; -} - -td.trigger_title { - padding-left: 2px; - padding-top: 1px; - padding-bottom: 1px; - background-color: #5f6e95; - color: #ffffff; -} - -td.direct_title { - padding-left: 2px; - padding-top: 1px; - padding-bottom: 1px; - background-color: #5f6e95; - color: #ffffff; -} - -td.title { - padding-left: 2px; - padding-top: 1px; - padding-bottom: 1px; - background-color: #d9e0ef; - /*background-color: #98a7c2;*/ - color: #1a1f20; -} - -td.trigger { - padding-left: 2px; - padding-top: 2px; - padding-bottom: 10px; - /*background-color: #5f6e95; - color: #ffffff;*/ -} -td -{ - background-color: #F5F5F5; - color: #000000; -} diff -Nru ecflow-4.9.0/Viewer/src/TriggerEditor.cpp ecflow-4.11.1/Viewer/src/TriggerEditor.cpp --- ecflow-4.9.0/Viewer/src/TriggerEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,162 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TriggerEditor.hpp" - -#include - -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "Highlighter.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "SessionHandler.hpp" - -TriggerEditorWidget::TriggerEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); - -#if 0 - QLayoutItem *item; - item=grid_->itemAtPosition(1,0); - Q_ASSERT(item); - item->setAlignment(Qt::AlignLeft|Qt::AlignTop); -#endif - - //The document becomes the owner of the highlighte - new Highlighter(te_->document(),"trigger"); - te_->setShowLineNumbers(false); -} - -TriggerEditor::TriggerEditor(VInfo_ptr info,QWidget* parent) : AttributeEditor(info,"trigger",parent) -{ - w_=new TriggerEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "trigger"); - - //Data is built dynamically so we store it - QStringList data=a->data(); - - if(data.count() != 3) - return; - - QString txt=data[2]; - - oriText_=txt; - w_->te_->setPlainText(txt); - - QString typeInHeader; - if(data[1]=="0") - { - typeInCmd_="trigger"; - typeInHeader="Trigger"; - } - else - { - typeInCmd_="complete"; - typeInHeader="Complete"; - } - typeInHeader+=" expression"; - - header_->setInfo(QString::fromStdString(info_->path()),typeInHeader); - - connect(w_->te_,SIGNAL(textChanged()), - this,SLOT(slotValueChanged())); - - checkButtonStatus(); - - readSettings(); -} - -TriggerEditor::~TriggerEditor() -{ - writeSettings(); -} - -void TriggerEditor::apply() -{ - if(typeInCmd_.isEmpty()) - return; - - std::string txt=w_->te_->toPlainText().toStdString(); - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change",typeInCmd_.toStdString(),txt); - CommandHandler::run(info_,cmd); -} - -void TriggerEditor::resetValue() -{ - w_->te_->setPlainText(oriText_); - checkButtonStatus(); -} - -void TriggerEditor::slotValueChanged() -{ - checkButtonStatus(); -} - -bool TriggerEditor::isValueChanged() -{ - return (oriText_ != w_->te_->toPlainText()); -} - -void TriggerEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("TriggerEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.setValue("fontSize",w_->te_->font().pointSize()); - settings.endGroup(); -} - -void TriggerEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("TriggerEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(310,200)); - } - - if(settings.contains("fontSize")) - { - QFont f=w_->te_->font(); - int fSize=settings.value("fontSize").toInt(); - if(fSize > 0 && fSize < 100) - f.setPointSize(fSize); - - w_->te_->setFont(f); - } - - settings.endGroup(); -} - -static AttributeEditorMaker makerStr("trigger"); - diff -Nru ecflow-4.9.0/Viewer/src/TriggerEditor.hpp ecflow-4.11.1/Viewer/src/TriggerEditor.hpp --- ecflow-4.9.0/Viewer/src/TriggerEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -#ifndef TRIGGEREDITOR_HPP -#define TRIGGEREDITOR_HPP - -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ui_TriggerEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -class LabelEditor; - -class TriggerEditorWidget : public QWidget, protected Ui::TriggerEditorWidget -{ -friend class TriggerEditor; -public: - TriggerEditorWidget(QWidget *parent=0); -}; - -class TriggerEditor : public AttributeEditor -{ -Q_OBJECT -public: - TriggerEditor(VInfo_ptr,QWidget* parent=0); - ~TriggerEditor(); - -protected Q_SLOTS: - void slotValueChanged(); - -protected: - void apply(); - void resetValue(); - bool isValueChanged(); - void readSettings(); - void writeSettings(); - - TriggerEditorWidget* w_; - QString oriText_; - QString typeInCmd_; -}; - -#endif // TRIGGEREDITOR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/TriggerEditorWidget.ui ecflow-4.11.1/Viewer/src/TriggerEditorWidget.ui --- ecflow-4.9.0/Viewer/src/TriggerEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ - - - TriggerEditorWidget - - - - 0 - 0 - 329 - 227 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - 1 - - - - - - - - - PlainTextEdit - QPlainTextEdit -
      PlainTextEdit.hpp
      -
      -
      - - -
      diff -Nru ecflow-4.9.0/Viewer/src/TriggeredScanner.cpp ecflow-4.11.1/Viewer/src/TriggeredScanner.cpp --- ecflow-4.9.0/Viewer/src/TriggeredScanner.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggeredScanner.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggeredScanner.hpp" - -#include - -#include "TriggerCollector.hpp" -#include "VNode.hpp" - -void TriggeredScanner::clear() -{ - total_=0; - current_=0; -} - -void TriggeredScanner::start(VServer* s) -{ - clear(); - assert(s); - total_=s->totalNum(); - current_=0; - Q_EMIT scanStarted(); - scan(s); - Q_EMIT scanFinished(); - clear(); -} - -//Scan the the whole tree to find for each node all the nodes that it or its -//attributes trigger. -void TriggeredScanner::scan(VNode *n) -{ - TriggeredCollector tc(n); - n->triggers(&tc); - - updateProgress(); - - for(int i=0; i < n->numOfChildren(); i++) - scan(n->childAt(i)); -} - -void TriggeredScanner::updateProgress() -{ - current_++; - if(current_ > 0 && current_ % batchSize_ == 0) - { - Q_EMIT scanProgressed(progress()); - } -} - -int TriggeredScanner::progress() const -{ - if(total_ > 0) - { - return static_cast(100.*static_cast(current_)/static_cast(total_)); - } - return 0; -} diff -Nru ecflow-4.9.0/Viewer/src/TriggeredScanner.hpp ecflow-4.11.1/Viewer/src/TriggeredScanner.hpp --- ecflow-4.9.0/Viewer/src/TriggeredScanner.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggeredScanner.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TRIGGEREDSCANNER_HPP -#define TRIGGEREDSCANNER_HPP - -#include - -class VNode; -class VServer; - -class TriggeredScanner : public QObject -{ -Q_OBJECT - -public: - TriggeredScanner(QObject* parent) : QObject(parent), total_(0), current_(0), batchSize_(100) {} - - void clear(); - void start(VServer*); - -Q_SIGNALS: - void scanStarted(); - void scanFinished(); - void scanProgressed(int percent); - -private: - void scan(VNode*); - void updateProgress(); - int progress() const; - - int total_; - int current_; - int batchSize_; -}; - -#endif // TRIGGEREDSCANNER_HPP - diff -Nru ecflow-4.9.0/Viewer/src/TriggerItemWidget.cpp ecflow-4.11.1/Viewer/src/TriggerItemWidget.cpp --- ecflow-4.9.0/Viewer/src/TriggerItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,399 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggerItemWidget.hpp" - -#include "Highlighter.hpp" -#include "ServerHandler.hpp" -#include "TriggerCollector.hpp" -#include "TriggeredScanner.hpp" -#include "VNode.hpp" -#include "VSettings.hpp" - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) -#include -#endif - -//======================================================== -// -// TriggerItemWidget -// -//======================================================== - -TriggerItemWidget::TriggerItemWidget(QWidget *parent) : QWidget(parent) -{ - //This item will listen to any changes in nodes - handleAnyChange_=true; - - //We will not keep the contents when the item becomes unselected - unselectedFlags_.clear(); - - setupUi(this); - - //The collectors - triggerCollector_=new TriggerTableCollector(false); - triggeredCollector_=new TriggerTableCollector(false); - - //Scanner - scanner_=new TriggeredScanner(this); - - connect(scanner_,SIGNAL(scanStarted()), - this,SLOT(scanStarted())); - - connect(scanner_,SIGNAL(scanFinished()), - this,SLOT(scanFinished())); - - connect(scanner_,SIGNAL(scanProgressed(int)), - this,SLOT(scanProgressed(int))); - - //Messages - messageLabel_->hide(); - messageLabel_->setShowTypeTitle(false); - - //Dependency info inti must precede dependency init - //since they depend on each other - - //dependency info is off by default - dependInfoTb_->setChecked(false); - on_dependInfoTb__toggled(false); - - //Dependency is off by default - dependTb_->setProperty("triggerDepend","1"); - dependTb_->setChecked(false); - on_dependTb__toggled(false); - - //Expression - exprTb_->setChecked(true); - - //The document becomes the owner of the highlighter - new Highlighter(exprTe_->document(),"trigger"); - exprTe_->setReadOnly(true); - exprTe_->setBackgroundVisible(true); - - //Set the height of the trigger expression display area - QFont fTe; - fTe.setBold(true); - QFontMetrics fm(fTe); - exprTe_->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); - exprTe_->setFixedHeight(fm.size(0,"A\nA\nA").height()+fm.height()/2); - - connect(triggerTable_,SIGNAL(depInfoWidgetClosureRequested()), - this,SLOT(slotHandleDefInfoWidgetClosure())); - - connect(triggerTable_,SIGNAL(linkSelected(VInfo_ptr)), - this,SLOT(slotLinkSelected(VInfo_ptr))); - - connect(triggerTable_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SLOT(slotInfoPanelCommand(VInfo_ptr,QString))); - - connect(triggerTable_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SLOT(slotDashboardCommand(VInfo_ptr,QString))); -} - -TriggerItemWidget::~TriggerItemWidget() -{ - clearContents(); - delete triggerCollector_; - delete triggeredCollector_; -} - -QWidget* TriggerItemWidget::realWidget() -{ - return this; -} - -void TriggerItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - - //set the info - adjust(info); - - //messageLabel_->hide(); - - //Info must be a node - load(); -} - -void TriggerItemWidget::load() -{ - clearTriggers(); - - if(info_ && info_->isNode() && info_->node()) - { - VNode* n=info_->node(); - Q_ASSERT(n); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); -#endif - - //Display trigger expression - std::string te,ce; - n->triggerExpr(te,ce); - QString txt=QString::fromStdString(te); - if(txt.isEmpty()) txt=tr("No trigger expression is available for the selected node!"); - exprTe_->setPlainText(txt); - - triggerTable_->setInfo(info_); - - //Load table - triggerTable_->beginTriggerUpdate(); - - //collect the list of triggers of this node - triggerCollector_->setDependency(dependency()); - n->triggers(triggerCollector_); - - triggeredCollector_->setDependency(dependency()); - n->triggered(triggeredCollector_,triggeredScanner()); - - triggerTable_->setTriggerCollector(triggerCollector_,triggeredCollector_); - triggerTable_->endTriggerUpdate(); - - triggerTable_->resumeSelection(); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QGuiApplication::restoreOverrideCursor(); -#endif - } -} - -void TriggerItemWidget::clearContents() -{ - InfoPanelItem::clear(); - exprTe_->clear(); - triggerTable_->clear(); - - if(!active_) - triggerTable_->clearSelection(); - - //At this point the tables are cleared so it is safe to clear the collectors - triggerCollector_->clear(); - triggeredCollector_->clear(); -} - -void TriggerItemWidget::clearTriggers() -{ - exprTe_->clear(); - triggerTable_->clear(); - - //At this point the tables are cleared so it is safe to clear the collectors - triggerCollector_->clear(); - triggeredCollector_->clear(); -} - -void TriggerItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - //If we are here this item is active but not selected! - - //When it becomes suspended we need to clear everything since the - //tree is probably cleared at this point - if(suspended_) - { - clearTriggers(); - } - //When we leave the suspended state we need to reload everything - else - { - load(); - } - } - - Q_ASSERT(!flags.isSet(SelectedChanged)); - - checkActionState(); -} - -void TriggerItemWidget::checkActionState() -{ - if(suspended_) - { - dependTb_->setEnabled(false); - return; - } - - dependTb_->setEnabled(true); -} - -void TriggerItemWidget::on_dependTb__toggled(bool b) -{ - load(); - - //when we activate the dependencies we always show the - //dependency details as well - if(b) - { - dependInfoTb_->setEnabled(true); - if(dependInfoTb_->isChecked() == false) - { - dependInfoTb_->setChecked(true); - } - } - else - { - dependInfoTb_->setChecked(false); - dependInfoTb_->setEnabled(false); - } -} - -void TriggerItemWidget::on_dependInfoTb__toggled(bool b) -{ - triggerTable_->slotShowDependencyInfo(b); -} - -void TriggerItemWidget::slotHandleDefInfoWidgetClosure() -{ - dependInfoTb_->setChecked(false); -} - -void TriggerItemWidget::on_exprTb__toggled(bool b) -{ - exprTe_->setVisible(b); -} - -bool TriggerItemWidget::dependency() const -{ - return dependTb_->isChecked(); -} - - -void TriggerItemWidget::slotLinkSelected(VInfo_ptr info) -{ - InfoPanelItem::linkSelected(info); -} - -void TriggerItemWidget::slotInfoPanelCommand(VInfo_ptr info,QString cmd) -{ - InfoPanelItem::relayInfoPanelCommand(info,cmd); -} - -void TriggerItemWidget::slotDashboardCommand(VInfo_ptr info,QString cmd) -{ - InfoPanelItem::relayDashboardCommand(info,cmd); -} - -#if 0 -void TriggerItemWidget::infoProgressStart(const std::string& text,int max) -{ - messageLabel_->showInfo(QString::fromStdString(text)); - messageLabel_->startProgress(max); -} - -void TriggerItemWidget::infoProgress(const std::string& text,int value) -{ - messageLabel_->progress(QString::fromStdString(text),value); -} - -#endif - -void TriggerItemWidget::scanStarted() -{ - messageLabel_->showInfo("Mapping trigger connections in the whole tree ..."); - messageLabel_->startProgress(100); -} - -void TriggerItemWidget::scanFinished() -{ - messageLabel_->stopProgress(); - messageLabel_->hide(); -} - -void TriggerItemWidget::scanProgressed(int value) -{ - std::string text=""; - messageLabel_->progress(QString::fromStdString(text),value); -} - -void TriggerItemWidget::writeSettings(VComboSettings* vs) -{ - vs->beginGroup("triggers"); - vs->putAsBool("dependency",dependency()); - vs->putAsBool("dependencyInfo",dependInfoTb_->isChecked()); - vs->putAsBool("expression",exprTb_->isChecked()); - - triggerTable_->writeSettings(vs); - vs->endGroup(); -} - -void TriggerItemWidget::readSettings(VComboSettings* vs) -{ - vs->beginGroup("triggers"); - - dependTb_->setChecked(vs->getAsBool("dependency",dependency())); - -// dependInfoTb_ is initialised by dependTb_ !! -#if 0 - if(dependTb_->isChecked()) - { - dependInfoTb_->setChecked(vs->getAsBool("dependencyInfo",dependInfoTb_->isChecked())); - } -#endif - - exprTb_->setChecked(vs->getAsBool("expression",exprTb_->isChecked())); - triggerTable_->readSettings(vs); - vs->endGroup(); -} - -//------------------------- -// Update -//------------------------- - -void TriggerItemWidget::nodeChanged(const VNode* n, const std::vector& aspect) -{ - //We do not track changes when the item is not selected - if(!selected_ || !active_) - return; - - if(!info_ || !info_->isNode()) - return; - - //If the triggers are not scanned there must have been a major change and - //we need to reload the item - if(!info_->node()->root()->triggeredScanned()) - { - load(); - return; - } - - //For certain changes we need to reload the triggers - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::ADD_REMOVE_ATTR || *it == ecf::Aspect::EXPR_TRIGGER) - { - load(); - return; - } - } - - //For other changes we only reload the triggers if the change happened to an item in the collected triggers - for(std::vector::const_iterator it=aspect.begin(); it != aspect.end(); ++it) - { - if(*it == ecf::Aspect::NODE_VARIABLE || *it == ecf::Aspect::METER || *it == ecf::Aspect::LIMIT || - *it == ecf::Aspect::EVENT) - { - if(triggerCollector_->contains(n,true) || triggeredCollector_->contains(n,true)) - { - load(); - return; - } - } - } - - //For the rest of the changes in we rerender the collected items that might have changed - triggerTable_->nodeChanged(n,aspect); -} - -static InfoPanelItemMaker maker1("triggers"); diff -Nru ecflow-4.9.0/Viewer/src/TriggerItemWidget.hpp ecflow-4.11.1/Viewer/src/TriggerItemWidget.hpp --- ecflow-4.9.0/Viewer/src/TriggerItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,72 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TRIGGERITEMWIDGET_HPP_ -#define TRIGGERITEMWIDGET_HPP_ - -#include - -#include "InfoPanelItem.hpp" -#include "VInfo.hpp" - -#include "ui_TriggerItemWidget.h" - -class QActionGroup; -class TriggeredScanner; -class TriggerTableCollector; - -class TriggerItemWidget : public QWidget, public InfoPanelItem, protected Ui::TriggerItemWidget -{ - friend class TriggerBrowser; - -Q_OBJECT - -public: - explicit TriggerItemWidget(QWidget *parent=0); - ~TriggerItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - - void nodeChanged(const VNode*, const std::vector&); - void defsChanged(const std::vector&) {} - - bool dependency() const; - - void writeSettings(VComboSettings* vs); - void readSettings(VComboSettings* vs); - -protected Q_SLOTS: - void on_dependTb__toggled(bool); - void on_dependInfoTb__toggled(bool b); - void on_exprTb__toggled(bool b); - void scanStarted(); - void scanFinished(); - void scanProgressed(int); - void slotHandleDefInfoWidgetClosure(); - void slotLinkSelected(VInfo_ptr info); - void slotInfoPanelCommand(VInfo_ptr info,QString cmd); - void slotDashboardCommand(VInfo_ptr info,QString cmd); - -protected: - void load(); - void updateState(const ChangeFlags&); - TriggeredScanner* triggeredScanner() const {return scanner_;} - void checkActionState(); - void clearTriggers(); - - TriggerTableCollector* triggerCollector_; - TriggerTableCollector* triggeredCollector_; - TriggeredScanner *scanner_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/TriggerItemWidget.ui ecflow-4.11.1/Viewer/src/TriggerItemWidget.ui --- ecflow-4.9.0/Viewer/src/TriggerItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,143 +0,0 @@ - - - TriggerItemWidget - - - - 0 - 0 - 620 - 728 - - - - Form - - - - 1 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Show trigger expression - - - Expression - - - true - - - Qt::ToolButtonTextOnly - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show dependencies (with darker background) - - - Dependencies - - - true - - - Qt::ToolButtonTextOnly - - - - - - - Show dependency details - - - Text - - - - :/viewer/dependency.svg:/viewer/dependency.svg - - - true - - - - - - - - - - - - - - - - - - true - - - Text - - - - - true - - - Table - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      - - TriggerTableWidget - QWidget -
      TriggerTableWidget.hpp
      - 1 -
      -
      - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableModel.cpp ecflow-4.11.1/Viewer/src/TriggerTableModel.cpp --- ecflow-4.9.0/Viewer/src/TriggerTableModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,299 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TriggerTableModel.hpp" - -#include "IconProvider.hpp" -#include "ServerHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VIcon.hpp" -#include "VNode.hpp" - -#include - -TriggerTableModel::TriggerTableModel(Mode mode,QObject *parent) : - QAbstractItemModel(parent), - tc_(0), - mode_(mode) -{ -} - -TriggerTableModel::~TriggerTableModel() -{ -} - - -void TriggerTableModel::clearData() -{ - beginResetModel(); - tc_=0; - endResetModel(); -} - -void TriggerTableModel::beginUpdate() -{ - beginResetModel(); -} - -void TriggerTableModel::endUpdate() -{ - endResetModel(); -} - - -void TriggerTableModel::setTriggerCollector(TriggerTableCollector *tc) -{ - //beginResetModel(); - tc_ = tc; - //endResetModel(); -} - -bool TriggerTableModel::hasData() const -{ - if(tc_) - return tc_->size() > 0; - else - return false; -} - -int TriggerTableModel::columnCount( const QModelIndex& /*parent */) const -{ - return 1; -} - -int TriggerTableModel::rowCount( const QModelIndex& parent) const -{ - if(!hasData()) - return 0; - - //Parent is the root: - if(!parent.isValid()) - { - return tc_->size(); - } - - return 0; -} - -Qt::ItemFlags TriggerTableModel::flags ( const QModelIndex & index) const -{ - return Qt::ItemIsEnabled | Qt::ItemIsSelectable; -} - -QVariant TriggerTableModel::data( const QModelIndex& index, int role ) const -{ - if(!index.isValid() || !hasData() || (role < Qt::UserRole && - role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::TextAlignmentRole && - role != Qt::ToolTipRole && role != Qt::ForegroundRole)) - { - return QVariant(); - } - - int row=index.row(); - if(row < 0 || row >= static_cast(tc_->size())) - return QVariant(); - - //QString id=columns_->id(index.column()); - - const std::vector& items=tc_->items(); - VItem *t=items[row]->item(); - Q_ASSERT(t); - - if(index.column() == 0) - { - if(VAttribute* a=t->isAttribute()) - { - if(role == Qt::DisplayRole) - { - QStringList d=a->data(); - if(VNode* pn=a->parent()) - d.append(QString::fromStdString(pn->absNodePath())); - return d; - } - else if(role == Qt::ToolTipRole) - { - return a->toolTip(); - } - - } - else if(VNode* vnode=t->isNode()) - { - if(role == Qt::DisplayRole) - { - return QString::fromStdString(vnode->absNodePath()); - } - else if(role == Qt::BackgroundRole) - { - if(vnode->isSuspended()) - { - QVariantList lst; - lst << vnode->stateColour() << vnode->realStateColour(); - return lst; - } - else - return vnode->stateColour() ; - } - else if(role == Qt::ForegroundRole) - return vnode->stateFontColour(); - - else if(role == Qt::ToolTipRole) - { - QString txt=vnode->toolTip(); - //txt+=VIcon::toolTip(vnode,icons_); - return txt; - } - else if(role == IconRole) - { - return VIcon::pixmapList(vnode,0); - } - else if(role == NodeTypeRole) - { - if(vnode->isTask()) return 2; - else if(vnode->isSuite()) return 0; - else if(vnode->isFamily()) return 1; - else if(vnode->isAlias()) return 3; - return 0; - } - else if(role == NodeTypeForegroundRole) - { - return vnode->typeFontColour(); - } - else if(role == NodePointerRole) - { - return qVariantFromValue((void *) vnode); - } - - else if(role == Qt::TextAlignmentRole) - { - return( mode_==NodeMode)?Qt::AlignCenter:Qt::AlignLeft; - } - - } - } - - //We express the table cell background colour through the UserRole. The - //BackgroundRole is already used for the node rendering - if(role == Qt::UserRole && mode_ != NodeMode) - { - const std::set& modes=items[row]->modes(); - if(modes.find(TriggerCollector::Normal) != modes.end()) - return QVariant(); - else - return QColor(233,242,247); - } - - return QVariant(); -} - -QVariant TriggerTableModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal) - return QAbstractItemModel::headerData( section, orient, role ); - - return QVariant(); -} - -QModelIndex TriggerTableModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { - return createIndex(row,column); - } - - return QModelIndex(); - -} - -QModelIndex TriggerTableModel::parent(const QModelIndex &child) const -{ - return QModelIndex(); -} - -VInfo_ptr TriggerTableModel::nodeInfo(const QModelIndex& index) -{ - //For invalid index no info is created. - if(!index.isValid()) - { - VInfo_ptr res; - return res; - } - - if(index.row() >=0 && index.row() <= static_cast(tc_->items().size())) - { - TriggerTableItem* d=tc_->items()[index.row()]; - return VInfo::createFromItem(d->item()); - } - - VInfo_ptr res; - return res; -} - -QModelIndex TriggerTableModel::itemToIndex(TriggerTableItem *item) -{ - if(item) - { - for(std::size_t i=0; i < tc_->items().size(); i++) - if(tc_->items()[i] == item) - return index(i,0); - } - - return QModelIndex(); -} - -TriggerTableItem* TriggerTableModel::indexToItem(const QModelIndex& index) const -{ - if(!hasData()) - return 0; - - int row=index.row(); - if(row < 0 || row >= static_cast(tc_->size())) - return 0; - - const std::vector& items=tc_->items(); - return items[row]; -} - -void TriggerTableModel::nodeChanged(const VNode* node, const std::vector&) -{ - if(!hasData()) - return; - - int num=tc_->items().size(); - for(int i=0; i < num; i++) - { - if(VItem* item=tc_->items()[i]->item()) - { - if(VNode* n=item->isNode()) - { - if(n == node) - { - QModelIndex idx=index(i,0); - Q_EMIT dataChanged(idx,idx); - } - } - - else if(VAttribute* a=item->isAttribute()) - { - if(a->parent() == node) - { - QModelIndex idx=index(i,0); - Q_EMIT dataChanged(idx,idx); - } - } - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableModel.hpp ecflow-4.11.1/Viewer/src/TriggerTableModel.hpp --- ecflow-4.9.0/Viewer/src/TriggerTableModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,69 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ -#define VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ - -#include -#include - -#include "VInfo.hpp" -#include "TriggerCollector.hpp" - -#include "Aspect.hpp" - -class NodeQueryResult; - -class TriggerTableModel : public QAbstractItemModel -{ -public: - enum Mode {TriggerMode,TriggeredMode,NodeMode}; - - explicit TriggerTableModel(Mode mode,QObject *parent=0); - ~TriggerTableModel(); - - //The custom roles must have the same numerical value as in AbstractNodeModel.hpp because the - //core delegate was written to only handle the custom roles it defines! - enum CustomItemRole {FilterRole = Qt::UserRole+1, IconRole = Qt::UserRole+2, - NodeTypeRole = Qt::UserRole + 13, - NodeTypeForegroundRole = Qt::UserRole + 14, - NodePointerRole = Qt::UserRole + 17}; - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - TriggerTableCollector* triggerCollector() const {return tc_;} - void setTriggerCollector(TriggerTableCollector *tc); - - void clearData(); - bool hasData() const; - - void beginUpdate(); - void endUpdate(); - - TriggerTableItem* indexToItem(const QModelIndex&) const; - VInfo_ptr nodeInfo(const QModelIndex&); - QModelIndex itemToIndex(TriggerTableItem*); - - void nodeChanged(const VNode* node, const std::vector&); - -protected: - TriggerTableCollector* tc_; - Mode mode_; -}; - -#endif /* VIEWER_SRC_TRIGGERTABLEMODEL_HPP_ */ diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableView.cpp ecflow-4.11.1/Viewer/src/TriggerTableView.cpp --- ecflow-4.9.0/Viewer/src/TriggerTableView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,248 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "TriggerTableView.hpp" - -#include -#include -#include -#include -#include - -#include "ActionHandler.hpp" -#include "AttributeEditor.hpp" -#include "NodeQueryResultModel.hpp" -#include "NodeQueryViewDelegate.hpp" -#include "TriggerTableModel.hpp" -#include "TriggerViewDelegate.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "VNode.hpp" - -TriggerTableView::TriggerTableView(QWidget* parent) : - QTreeView(parent), - model_(NULL), - needItemsLayout_(false) -{ - setProperty("view","trigger"); - - actionHandler_=new ActionHandler(this,this); - - //Set the model - setModel(model_); - - //Create delegate to the view - delegate_=new TriggerViewDelegate(this); - setItemDelegate(delegate_); - - connect(delegate_,SIGNAL(sizeHintChangedGlobal()), - this,SLOT(slotSizeHintChangedGlobal())); - - setHeaderHidden(true); - setRootIsDecorated(false); - setAllColumnsShowFocus(true); - setUniformRowHeights(true); - setMouseTracking(true); - setSortingEnabled(false); - setSelectionMode(QAbstractItemView::ExtendedSelection); - - //!!!!We need to do it because: - //The background colour between the view's left border and the nodes cannot be - //controlled by delegates or stylesheets. It always takes the QPalette::Highlight - //colour from the palette. Here we set this to transparent so that Qt could leave - //this area empty and we will fill it appropriately in our delegate. - QPalette pal=palette(); - pal.setColor(QPalette::Highlight,QColor(128,128,128,0));//Qt::transparent); - setPalette(pal); - - //Context menu - enableContextMenu(true); - - //Selection - connect(this,SIGNAL(clicked(const QModelIndex&)), - this,SLOT(slotSelectItem(const QModelIndex&))); - - connect(this,SIGNAL(doubleClicked(const QModelIndex&)), - this,SLOT(slotDoubleClickItem(const QModelIndex&))); - -} - -TriggerTableView::~TriggerTableView() -{ - delete actionHandler_; -} - -//We should only call it once!!! -void TriggerTableView::setModel(TriggerTableModel* model) -{ - Q_ASSERT(model_==0); - model_=model; - QTreeView::setModel(model); -} - -void TriggerTableView::enableContextMenu(bool enable) -{ - if (enable) - { - setContextMenuPolicy(Qt::CustomContextMenu); - - connect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - } - else - { - setContextMenuPolicy(Qt::NoContextMenu); - - disconnect(this, SIGNAL(customContextMenuRequested(const QPoint &)), - this, SLOT(slotContextMenu(const QPoint &))); - } -} - - -//Collects the selected list of indexes -QModelIndexList TriggerTableView::selectedList() -{ - QModelIndexList lst; - Q_FOREACH(QModelIndex idx,selectedIndexes()) - { - if(idx.column() == 0) - lst << idx; - } - return lst; -} - -// this is called even if the user clicks outside of the node list to deselect all -void TriggerTableView::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) -{ - QModelIndexList lst=selectedIndexes(); - if(lst.count() > 0) - { - TriggerTableItem* item=model_->indexToItem(lst.front()); - if(item && item->item()) - { - Q_EMIT selectionChanged(item); - } - } - - QTreeView::selectionChanged(selected, deselected); - - //Q_EMIT selectionChanged(); -} - -void TriggerTableView::slotSelectItem(const QModelIndex&) -{ - QModelIndexList lst=selectedIndexes(); - - if(lst.count() > 0) - { - TriggerTableItem* item=model_->indexToItem(lst.front()); - if(item && item->item()) - { - Q_EMIT clicked(item); - } - } -} - -void TriggerTableView::setCurrentItem(TriggerTableItem* item) -{ - QModelIndex idx=model_->itemToIndex(item); - if(idx.isValid()) - { - setCurrentIndex(idx); - } -} - -void TriggerTableView::slotDoubleClickItem(const QModelIndex& index) -{ - VInfo_ptr info=model_->nodeInfo(index); - if(info) - { - Q_EMIT linkSelected(info); - } -} - -void TriggerTableView::slotContextMenu(const QPoint &position) -{ - QModelIndexList lst=selectedList(); - //QModelIndex index=indexAt(position); - QPoint scrollOffset(horizontalScrollBar()->value(),verticalScrollBar()->value()); - - handleContextMenu(indexAt(position),lst,mapToGlobal(position),position+scrollOffset,this); -} - - -void TriggerTableView::handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget) -{ - //Node actions - if(indexClicked.isValid()) //indexLst[0].isValid() && indexLst[0].column() == 0) - { - std::vector nodeLst; - for(int i=0; i < indexLst.count(); i++) - { - VInfo_ptr info=model_->nodeInfo(indexLst[i]); - if(info && !info->isEmpty()) - nodeLst.push_back(info); - } - - actionHandler_->contextMenu(nodeLst,globalPos); - } - - //Desktop actions - else - { - } -} - -void TriggerTableView::slotViewCommand(VInfo_ptr info,QString cmd) -{ - if(cmd == "lookup") - { - Q_EMIT linkSelected(info); - } - - else if(cmd == "edit") - { - if(info && info->isAttribute()) - { - AttributeEditor::edit(info,this); - } - } -} - -void TriggerTableView::reload() -{ - //model_->reload(); - //expandAll(); -} - -void TriggerTableView::rerender() -{ - if(needItemsLayout_) - { - doItemsLayout(); - needItemsLayout_=false; - } - else - { - viewport()->update(); - } -} - -void TriggerTableView::slotRerender() -{ - rerender(); -} - - -void TriggerTableView::slotSizeHintChangedGlobal() -{ - needItemsLayout_=true; -} - diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableView.hpp ecflow-4.11.1/Viewer/src/TriggerTableView.hpp --- ecflow-4.9.0/Viewer/src/TriggerTableView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TRIGGERTABLEVIEW_HPP -#define TRIGGERTABLEVIEW_HPP - -#include - -#include "VInfo.hpp" - -class ActionHandler; -class NodeQueryResultModel; -class TriggerViewDelegate; -class TriggerTableItem; -class TriggerTableModel; - -class TriggerTableView : public QTreeView -{ -Q_OBJECT - -public: - explicit TriggerTableView(QWidget *parent=0); - ~TriggerTableView(); - - void setModel(TriggerTableModel* model); - - void reload(); - void rerender(); - void setCurrentItem(TriggerTableItem*); - void enableContextMenu(bool enable); - -public Q_SLOTS: - void slotSelectItem(const QModelIndex&); - void slotDoubleClickItem(const QModelIndex&); - void slotContextMenu(const QPoint &position); - void slotViewCommand(VInfo_ptr,QString); - void slotRerender(); - void slotSizeHintChangedGlobal(); - void selectionChanged (const QItemSelection &selected, const QItemSelection &deselected); - -Q_SIGNALS: - void selectionChanged(TriggerTableItem*); - void clicked(TriggerTableItem*); - void linkSelected(VInfo_ptr); - void selectionChanged(); - void infoPanelCommand(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - -protected: - QModelIndexList selectedList(); - void handleContextMenu(QModelIndex indexClicked,QModelIndexList indexLst,QPoint globalPos,QPoint widgetPos,QWidget *widget); - - TriggerTableModel* model_; - ActionHandler* actionHandler_; - bool needItemsLayout_; - TriggerViewDelegate* delegate_; -}; - -#endif // TRIGGERTABLEVIEW_HPP diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableWidget.cpp ecflow-4.11.1/Viewer/src/TriggerTableWidget.cpp --- ecflow-4.9.0/Viewer/src/TriggerTableWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,373 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggerTableWidget.hpp" - -#include "Highlighter.hpp" -#include "TriggerItemWidget.hpp" -#include "TriggerTableModel.hpp" -#include "TriggerViewDelegate.hpp" -#include "VSettings.hpp" - -TriggerTableWidget::TriggerTableWidget(QWidget *parent) : - QWidget(parent) -{ - setupUi(this); - - nodeCollector_=new TriggerTableCollector(false); - - depInfoCloseTb_->setProperty("triggertitle","1"); - depInfoCloseTb_->parent()->setProperty("triggertitle","1"); - - //Format labels - triggerLabel_->setProperty("triggertitle","1"); - triggeredLabel_->setProperty("triggertitle","1"); - depLabel_->setProperty("triggertitle","1"); - - depLabelText_=tr(" Dependency details"); - depLabel_->setText(depLabelText_); - - //Node - model + view - nodeModel_ = new TriggerTableModel(TriggerTableModel::NodeMode,this); - nodeView_->setModel(nodeModel_); - - //Set the height of the node display area - QFont fNode; - QFontMetrics fm(fNode); - nodeView_->setSizePolicy(QSizePolicy::Preferred,QSizePolicy::Fixed); - nodeView_->setFixedHeight(fm.size(0,"A").height()+fm.height()/3); - - //Set the size of the left and right arrow labels - leftArrowLabel_->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); - leftArrowLabel_->setFixedHeight(nodeView_->height()); - leftArrowLabel_->setFixedWidth(nodeView_->height()); - - rightArrowLabel_->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed); - rightArrowLabel_->setFixedHeight(nodeView_->height()); - rightArrowLabel_->setFixedWidth(nodeView_->height()); - - //Trigger - model + view - triggerModel_ = new TriggerTableModel(TriggerTableModel::TriggerMode,this); - triggerView_->setModel(triggerModel_); - - //triggered - model + view - triggeredModel_ = new TriggerTableModel(TriggerTableModel::TriggeredMode,this); - triggeredView_->setModel(triggeredModel_); - - //normal selection - connect(triggerView_,SIGNAL(selectionChanged(TriggerTableItem*)), - this,SLOT(slotTriggerSelection(TriggerTableItem*))); - - connect(triggerView_,SIGNAL(clicked(TriggerTableItem*)), - this,SLOT(slotTriggerClicked(TriggerTableItem*))); - - connect(triggeredView_,SIGNAL(selectionChanged(TriggerTableItem*)), - this,SLOT(slotTriggeredSelection(TriggerTableItem*))); - - connect(triggeredView_,SIGNAL(clicked(TriggerTableItem*)), - this,SLOT(slotTriggeredClicked(TriggerTableItem*))); - - //lookup selection - connect(triggerView_,SIGNAL(linkSelected(VInfo_ptr)), - this,SIGNAL(linkSelected(VInfo_ptr))); - - connect(triggeredView_,SIGNAL(linkSelected(VInfo_ptr)), - this,SIGNAL(linkSelected(VInfo_ptr))); - - //relay commands - connect(nodeView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); - - connect(nodeView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); - - connect(triggerView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); - - connect(triggerView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); - - connect(triggeredView_,SIGNAL(infoPanelCommand(VInfo_ptr,QString)), - this,SIGNAL(infoPanelCommand(VInfo_ptr,QString))); - - connect(triggeredView_,SIGNAL(dashboardCommand(VInfo_ptr,QString)), - this,SIGNAL(dashboardCommand(VInfo_ptr,QString))); - - //anchor clicked in text browser - connect(depBrowser_,SIGNAL(anchorClicked(const QUrl&)), - this,SLOT(anchorClicked(const QUrl&))); -} - -TriggerTableWidget::~TriggerTableWidget() -{ - delete nodeCollector_; -} - -void TriggerTableWidget::clear() -{ - info_.reset(); - - nodeModel_->clearData(); - triggerModel_->clearData(); - triggeredModel_->clearData(); - - depLabel_->setText(depLabelText_); - depBrowser_->clear(); -} - -void TriggerTableWidget::clearSelection() -{ - lastSelectedItem_.reset(); -} - -void TriggerTableWidget::setInfo(VInfo_ptr info) -{ - info_=info; - - nodeModel_->beginUpdate(); - nodeCollector_->clear(); - if(info_) - { - nodeCollector_->add(info_->item(),0,TriggerCollector::Normal); - } - nodeModel_->setTriggerCollector(nodeCollector_); - nodeModel_->endUpdate(); -} - -void TriggerTableWidget::slotTriggerClicked(TriggerTableItem* item) -{ - Q_ASSERT(item); - - if(!depBrowser_->document()->isEmpty() && lastSelectedItem_ && lastSelectedItem_->hasData()) - { - if(item->item() && item->item()->sameContents(lastSelectedItem_->item())) - return; - } - - slotTriggerSelection(item); -} - -void TriggerTableWidget::slotTriggerSelection(TriggerTableItem* item) -{ - Q_ASSERT(item); - Q_ASSERT(item->item()); - - lastSelectedItem_=VInfo::createFromItem(item->item()); - - if(!depInfoWidget_->isVisible()) - return; - - QString txt=tr("  triggers these parents/children of "); - QString tgName,tgType; - QColor col(255,255,255); - - VItem* currentItem=0; - if(info_) - currentItem=info_->item(); - - if(currentItem) - { - tgName=currentItem->name(); - tgType=QString::fromStdString(currentItem->typeName()); - } - txt.replace("","" + tgName + ""); - txt.replace("",tgType); - - tgName.clear(); - tgType.clear(); - if(item->item()) - { - tgName=item->item()->name(); - tgType=QString::fromStdString(item->item()->typeName()); - } - txt.replace("","" + tgName + ""); - txt.replace("",tgType); - - depLabel_->setText(txt); - depBrowser_->reload(item); -} - - -void TriggerTableWidget::slotTriggeredClicked(TriggerTableItem* item) -{ - Q_ASSERT(item); - - if(!depBrowser_->document()->isEmpty() && lastSelectedItem_ && lastSelectedItem_->hasData()) - { - if(item->item() && item->item()->sameContents(lastSelectedItem_->item())) - return; - } - - slotTriggeredSelection(item); -} - -void TriggerTableWidget::slotTriggeredSelection(TriggerTableItem* item) -{ - Q_ASSERT(item); - Q_ASSERT(item->item()); - - lastSelectedItem_=VInfo::createFromItem(item->item()); - - if(!depInfoWidget_->isVisible()) - return; - - QString txt=tr("  is triggered by these parents/children of "); - QString tgType,tgName; - QColor col(255,255,255); - - VItem* currentItem=0; - if(info_) - currentItem=info_->item(); - - if(currentItem) - { - tgName=currentItem->name(); - tgType=QString::fromStdString(currentItem->typeName()); - } - txt.replace("","" + tgName + ""); - txt.replace("",tgType); - - tgName.clear(); - tgType.clear(); - if(item->item()) - { - tgName=item->item()->name(); - tgType=QString::fromStdString(item->item()->typeName()); - } - txt.replace("","" + tgName + ""); - txt.replace("",tgType); - - depLabel_->setText(txt); - depBrowser_->reload(item); -} - -void TriggerTableWidget::slotShowDependencyInfo(bool b) -{ - depInfoWidget_->setVisible(b); - - //When the depinfo panel becomes visible we need to update - //its contents - if(b && lastSelectedItem_ && lastSelectedItem_->hasData()) - { - if(TriggerTableCollector *tc=triggerModel_->triggerCollector()) - { - if(TriggerTableItem *ti=tc->find(lastSelectedItem_->item())) - { - slotTriggerSelection(ti); - return; - } - } - if(TriggerTableCollector *tc=triggeredModel_->triggerCollector()) - { - if(TriggerTableItem *ti=tc->find(lastSelectedItem_->item())) - { - slotTriggeredSelection(ti); - return; - } - } - } -} - -void TriggerTableWidget::on_depInfoCloseTb__clicked() -{ - if(depInfoWidget_->isVisible()) - Q_EMIT depInfoWidgetClosureRequested(); -} - -void TriggerTableWidget::anchorClicked(const QUrl& link) -{ - VInfo_ptr info=VInfo::createFromPath(info_->server(),link.toString().toStdString()); - if(info) - Q_EMIT linkSelected(info); -} - -void TriggerTableWidget::beginTriggerUpdate() -{ - triggerModel_->beginUpdate(); - triggeredModel_->beginUpdate(); -} - -void TriggerTableWidget::endTriggerUpdate() -{ - triggerModel_->endUpdate(); - triggeredModel_->endUpdate(); -} - -void TriggerTableWidget::setTriggerCollector(TriggerTableCollector *tc1,TriggerTableCollector *tc2) -{ - triggerModel_->setTriggerCollector(tc1); - triggeredModel_->setTriggerCollector(tc2); -} - -void TriggerTableWidget::resumeSelection() -{ - //try to reselect the last selected item - if(lastSelectedItem_) - { - lastSelectedItem_->regainData(); - if(lastSelectedItem_->hasData()) - { - if(TriggerTableCollector* tc=triggerModel_->triggerCollector()) - { - if(TriggerTableItem* ti=tc->findByContents(lastSelectedItem_->item())) - { - triggerView_->setCurrentItem(ti); - return; - } - } - - if(TriggerTableCollector* tc=triggeredModel_->triggerCollector()) - { - if(TriggerTableItem* ti=tc->findByContents(lastSelectedItem_->item())) - { - triggeredView_->setCurrentItem(ti); - return; - } - } - } - else - { - lastSelectedItem_.reset(); - } - } -} - -void TriggerTableWidget::nodeChanged(const VNode* node, const std::vector& aspect) -{ - //The node view only contains one item (=info_) so we simply rerender it to get the - //update - if(info_ && info_->node() == node) - nodeView_->rerender(); - - triggerModel_->nodeChanged(node,aspect); - triggeredModel_->nodeChanged(node,aspect); -} - - -void TriggerTableWidget::writeSettings(VComboSettings* vs) -{ - vs->beginGroup("triggerTable"); - vs->putQs("splitter1",splitter1_->saveState()); - vs->putQs("splitter2",splitter2_->saveState()); - vs->endGroup(); -} - -void TriggerTableWidget::readSettings(VComboSettings* vs) -{ - vs->beginGroup("triggerTable"); - if(vs->containsQs("splitter1")) - { - splitter1_->restoreState(vs->getQs("splitter1").toByteArray()); - } - if(vs->containsQs("splitter2")) - { - splitter2_->restoreState(vs->getQs("splitter2").toByteArray()); - } - vs->endGroup(); -} diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableWidget.hpp ecflow-4.11.1/Viewer/src/TriggerTableWidget.hpp --- ecflow-4.9.0/Viewer/src/TriggerTableWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - - -#ifndef TRIGGERTABLEWIDGET_HPP_ -#define TRIGGERTABLEWIDGET_HPP_ - -#include -#include "VInfo.hpp" - -#include "Aspect.hpp" - -#include "ui_TriggerTableWidget.h" - -class TriggerTableItem; -class TriggerTableModel; -class TriggerTableCollector; -class TriggerItemWidget; -class VComboSettings; - -class TriggerTableWidget : public QWidget, private Ui::triggerTableWidget -{ - Q_OBJECT - -public: - explicit TriggerTableWidget(QWidget *parent = 0); - ~TriggerTableWidget(); - - void setInfo(VInfo_ptr); - void setTriggerCollector(TriggerTableCollector *tc1,TriggerTableCollector *tc2); - void clear(); - void clearSelection(); - void resumeSelection(); - void beginTriggerUpdate(); - void endTriggerUpdate(); - void nodeChanged(const VNode* node, const std::vector& aspect); - void writeSettings(VComboSettings* vs); - void readSettings(VComboSettings* vs); - -public Q_SLOTS: - void slotShowDependencyInfo(bool); - -protected Q_SLOTS: - void slotTriggerSelection(TriggerTableItem* item); - void slotTriggerClicked(TriggerTableItem*); - void slotTriggeredSelection(TriggerTableItem* item); - void slotTriggeredClicked(TriggerTableItem*); - void on_depInfoCloseTb__clicked(); - void anchorClicked(const QUrl& link); - -Q_SIGNALS: - void linkSelected(VInfo_ptr); - void infoPanelCommand(VInfo_ptr,QString); - void dashboardCommand(VInfo_ptr,QString); - void depInfoWidgetClosureRequested(); - -private: - VInfo_ptr info_; - TriggerTableCollector* nodeCollector_; - TriggerTableModel *nodeModel_; - TriggerTableModel *triggerModel_; - TriggerTableModel *triggeredModel_; - VInfo_ptr lastSelectedItem_; - QString depLabelText_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/TriggerTableWidget.ui ecflow-4.11.1/Viewer/src/TriggerTableWidget.ui --- ecflow-4.9.0/Viewer/src/TriggerTableWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTableWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,243 +0,0 @@ - - - triggerTableWidget - - - - 0 - 0 - 940 - 579 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - :/viewer/trigger_left_arrow.svg - - - true - - - - - - - - - - - - - :/viewer/trigger_right_arrow.svg - - - true - - - - - - - - - - Qt::Vertical - - - 2 - - - true - - - - Qt::Horizontal - - - 2 - - - false - - - - - 2 - - - - - <html><head/><body><p><span style=" font-weight:600;">Triggers of</span> the selected node</p></body></html> - - - - - - - false - - - - - - - - - 2 - - - - - &nbsp;Nodes <b>triggered by</b> the selected node - - - - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 0 - 0 - - - - - 16 - 16 - - - - - - - :/viewer/dock_dependency.svg - - - true - - - - - - - <html><head/><body><p><br/></p></body></html> - - - - - - - Close - - - - - - - :/viewer/images/dock_close.svg:/viewer/images/dock_close.svg - - - - 16 - 16 - - - - true - - - - - - - - - - - - - - - - - TriggerTextWidget - QTextBrowser -
      TriggerTextWidget.hpp
      -
      - - TriggerTableView - QTreeView -
      TriggerTableView.hpp
      -
      -
      - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/TriggerTextWidget.cpp ecflow-4.11.1/Viewer/src/TriggerTextWidget.cpp --- ecflow-4.9.0/Viewer/src/TriggerTextWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTextWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggerTextWidget.hpp" - -#include -#include - -#include "TriggerCollector.hpp" -#include "VItemPathParser.hpp" - -TriggerTextWidget::TriggerTextWidget(QWidget* parent) : QTextBrowser(parent) -{ - setOpenExternalLinks(false); - setOpenLinks(false); - setReadOnly(true); - - setProperty("trigger","1"); - - //Set css for the text formatting - QString cssDoc; - QFile f(":/viewer/trigger.css"); - //QTextStream in(&f); - if(f.open(QIODevice::ReadOnly | QIODevice::Text)) - { - cssDoc=QString(f.readAll()); - } - f.close(); - document()->setDefaultStyleSheet(cssDoc); -} - -void TriggerTextWidget::reload(TriggerTableItem* item) -{ - QString s=""; - s+=makeHtml(item,"Triggers directly triggering the selected node","Triggers"); - s+="
      "; - setHtml(s); -} - -QString TriggerTextWidget::makeHtml(TriggerTableItem *ti,QString directTitle,QString modeText) const -{ - QString s; - const std::vector& items=ti->dependencies(); - - for(unsigned int i=0; i < items.size(); i++) - { - VItem *t=items[i].dep(); - TriggerCollector::Mode mode=items[i].mode(); - - if(!t) - continue; - - s+=""; - if(mode == TriggerCollector::Parent) - s+="parent"; - else - s+="child"; - - QString type=QString::fromStdString(t->typeName()); - QString path=QString::fromStdString(t->fullPath()); - QString anchor=QString::fromStdString(VItemPathParser::encode(t->fullPath(),t->typeName())); - - s+=" " + type; - //s+=" " + path +""; - s+=" " + path +""; - s+=""; - } - - return s; -} - - diff -Nru ecflow-4.9.0/Viewer/src/TriggerTextWidget.hpp ecflow-4.11.1/Viewer/src/TriggerTextWidget.hpp --- ecflow-4.9.0/Viewer/src/TriggerTextWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerTextWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef TRIGGERTEXTWIDGET_HPP -#define TRIGGERTEXTWIDGET_HPP - -#include - -class TriggerTableItem; - -class TriggerTextWidget : public QTextBrowser -{ -public: - explicit TriggerTextWidget(QWidget *parent=0); - void reload(TriggerTableItem* item); - -private: - QString makeHtml(TriggerTableItem *ti,QString directTitle,QString modeText) const; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/TriggerViewDelegate.cpp ecflow-4.11.1/Viewer/src/TriggerViewDelegate.cpp --- ecflow-4.9.0/Viewer/src/TriggerViewDelegate.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerViewDelegate.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,176 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "TriggerViewDelegate.hpp" - -#include -#include -#include -#include -#include - -#include "AbstractNodeModel.hpp" -#include "Animation.hpp" -#include "FontMetrics.hpp" -#include "IconProvider.hpp" -#include "PropertyMapper.hpp" -#include "ServerHandler.hpp" -#include "UiLog.hpp" - -static std::vector propVec; - -#if 0 -static QColor typeFgColourClassic=QColor(Qt::white); -static QColor typeBgColourClassic=QColor(150,150,150); -static QColor childCountColour=QColor(90,91,92); -#endif - -struct TriggerNodeDelegateBox : public NodeDelegateBox -{ - TriggerNodeDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=2; - rightMargin=2; - topPadding=0; - bottomPadding=0; - leftPadding=2; - rightPadding=2; - } -}; - -struct TriggerAttrDelegateBox : public AttrDelegateBox -{ - TriggerAttrDelegateBox() { - topMargin=2; - bottomMargin=2; - leftMargin=2; - rightMargin=2; - topPadding=0; - bottomPadding=0; - leftPadding=2; - rightPadding=0; - } -}; - -TriggerViewDelegate::TriggerViewDelegate(QWidget *parent) : - TreeNodeViewDelegate(0,parent) -{ - //borderPen_=QPen(QColor(230,230,230)); - borderPen_=QPen(QColor(220,220,220)); - - nodeBox_=new TriggerNodeDelegateBox; - attrBox_=new TriggerAttrDelegateBox; - - nodeBox_->adjust(font_); - attrBox_->adjust(attrFont_); - - //Property - if(propVec.empty()) - { - //Base settings - addBaseSettings(propVec); - } - - prop_=new PropertyMapper(propVec,this); - - updateSettings(); -} - -TriggerViewDelegate::~TriggerViewDelegate() -{ -} - -void TriggerViewDelegate::updateSettings() -{ - //Update the settings handled by the base class - updateBaseSettings(); -} - -QSize TriggerViewDelegate::sizeHint(const QStyleOptionViewItem&, const QModelIndex & index ) const -{ - return nodeBox_->sizeHintCache; -} - -void TriggerViewDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QStyleOptionViewItem vopt(option); -#else - QStyleOptionViewItemV4 vopt(option); -#endif - - initStyleOption(&vopt, index); - - //const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); - //const QWidget* widget = vopt.widget; - - //Save painter state - painter->save(); - - //Background - QColor bgcol=index.data(Qt::UserRole).value(); - if(bgcol.isValid()) - painter->fillRect(vopt.rect,bgcol); - - if(index.column() == 0) - { - QVariant tVar=index.data(Qt::DisplayRole); - painter->setFont(font_); - - //Node - if(tVar.type() == QVariant::String) - { - QString text=index.data(Qt::DisplayRole).toString(); - - //If the textalignment is AlignCenter we node is displayed - //as centered in the opt rect! - QVariant vTa=index.data(Qt::TextAlignmentRole); - if(!vTa.isNull()) - { - if(vTa.toInt() == Qt::AlignCenter) - { - int w=nodeWidth(index,text); - int dw=(vopt.rect.width()-w)/2; - if(dw > 0) - vopt.rect.moveLeft(dw); - } - } - - renderNode(painter,index,vopt,text); - } - - //Render attributes - else if(tVar.type() == QVariant::StringList) - { - QStringList lst=tVar.toStringList(); - if(lst.count() > 0) - { - QMap::const_iterator it=attrRenderers_.find(lst.at(0)); - if(it != attrRenderers_.end()) - { - QSize size; - AttributeRendererProc a=it.value(); - (this->*a)(painter,lst,vopt,size); - } - } - } - } - - //Render the horizontal border for rows. We only render the top border line. - //With this technique we miss the bottom border line of the last row!!! - //QRect fullRect=QRect(0,option.rect.y(),painter->device()->width(),option.rect.height()); - QRect bgRect=option.rect; - painter->setPen(borderPen_); - painter->drawLine(bgRect.topLeft(),bgRect.topRight()); - - painter->restore(); -} - diff -Nru ecflow-4.9.0/Viewer/src/TriggerViewDelegate.hpp ecflow-4.11.1/Viewer/src/TriggerViewDelegate.hpp --- ecflow-4.9.0/Viewer/src/TriggerViewDelegate.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/TriggerViewDelegate.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef TRIGGERVIEWDELEGATE_HPP -#define TRIGGERVIEWDELEGATE_HPP - -#include -#include -#include -#include -#include - -#include "TreeNodeViewDelegate.hpp" -#include "VProperty.hpp" - -#include - -class ModelColumn; - -class TriggerViewDelegate : public TreeNodeViewDelegate -{ -public: - explicit TriggerViewDelegate(QWidget *parent=0); - ~TriggerViewDelegate(); - - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - -protected: - void updateSettings(); - - QPen borderPen_; - -}; - -#endif // TRIGGERVIEWDELEGATE_HPP diff -Nru ecflow-4.9.0/Viewer/src/UIDebug.cpp ecflow-4.11.1/Viewer/src/UIDebug.cpp --- ecflow-4.9.0/Viewer/src/UIDebug.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UIDebug.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - -#include "UIDebug.hpp" - - -void UIDebug::uiAssert(char const* expr, char const* file, long line, const std::string& message) -{ - std::stringstream ss; - ss << "ASSERT failure: " << expr << " at " << file << ":" << line << " " << message; - std::string assert_msg = ss.str(); - std::cerr << assert_msg << "\n"; - exit(1); -} - -std::string UIDebug::longToString(long num) -{ - char buf[64]; - sprintf(buf, "%ld", num); - return std::string(buf); -} diff -Nru ecflow-4.9.0/Viewer/src/UIDebug.hpp ecflow-4.11.1/Viewer/src/UIDebug.hpp --- ecflow-4.9.0/Viewer/src/UIDebug.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UIDebug.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef UIDEBUG_HPP_ -#define UIDEBUG_HPP_ - - -#include "Log.hpp" // from ACore - - -class UIDebug -{ -public: - UIDebug() {} - ~UIDebug() {} - - static void uiAssert(char const* expr, char const* file, long line, const std::string& message); - static std::string longToString(long num); -}; - - -#define UI_ASSERT(expr,EXPRESSION) ((expr)? ((void)0): UIDebug::uiAssert(#expr, __FILE__, __LINE__, STRINGIZE(EXPRESSION))) - -#endif diff -Nru ecflow-4.9.0/Viewer/src/UiLog.cpp ecflow-4.11.1/Viewer/src/UiLog.cpp --- ecflow-4.9.0/Viewer/src/UiLog.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UiLog.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,217 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "UiLog.hpp" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "DirectoryHandler.hpp" -#include "LogTruncator.hpp" -#include "ServerHandler.hpp" -#include "TimeStamp.hpp" - -static LogTruncator *truncator=0; - -//--------------------------------- -// UiFunctionLog -//--------------------------------- - -UiFunctionLog::UiFunctionLog(ServerHandler* server,const std::string& funcName) : - funcName_(funcName) -{ - if(server) - serverName_=server->longName(); - - init(); - UiLog(serverName_).dbg() << logEnter(); -} - -UiFunctionLog::UiFunctionLog(const std::string& funcName) : - funcName_(funcName) -{ - init(); - UiLog(serverName_).dbg() << logEnter(); -} - -UiFunctionLog::~UiFunctionLog() -{ - UiLog(serverName_).dbg() << logLeave(); -} - -void UiFunctionLog::init() -{ - std::size_t pos; - if((pos=funcName_.find_first_of("(")) != std::string::npos) - { - std::size_t pos1=funcName_.rfind(" ",pos); - if(pos1 != std::string::npos && pos1+1 < pos) - funcName_=funcName_.substr(pos1+1,pos-pos1-1); - } -} - -std::string UiFunctionLog::logEnter() const -{ - return funcName_ + " -->"; -} - -std::string UiFunctionLog::logLeave() const -{ - return "<-- " + funcName_; -} - -//--------------------------------- -// UiLog -//--------------------------------- - -UiLog::UiLog(ServerHandler* sh) : - type_(INFO), server_(sh->longName()) -{} - -UiLog::UiLog(const std::string& server) : - type_(INFO), server_(server) -{} - - -UiLog::~UiLog() -{ - output(os_.str()); -} - -void UiLog::appendType(std::string& s,Type t) const -{ - switch(t) - { - case DBG: s.append("DBG:"); break; - case INFO: s.append("INF:"); break; - case WARN: s.append("WAR:"); break; - case ERROR: s.append("ERR:"); break; - default: assert(false); break; - } -} - -std::ostringstream& UiLog::info() -{ - type_=INFO; - return os_; -} - -std::ostringstream& UiLog::err() -{ - type_=ERROR; - return os_; -} - -std::ostringstream& UiLog::warn() -{ - type_=WARN; - return os_; -} - -std::ostringstream& UiLog::dbg() -{ - type_=DBG; - return os_; -} - -void UiLog::output(const std::string& msg) -{ - std::string ts; - ecf::TimeStamp::now_in_brief(ts); - - std::string s; - appendType(s,type_); - - if(server_.empty()) - s+=" " + ts + msg; - else - s+=" " + ts + "[" + server_ + "] " + msg; - - std::cout << s << std::endl; - -} - -void UiLog::enableTruncation() -{ - if(!truncator) - truncator=new LogTruncator(QString::fromStdString(DirectoryHandler::uiLogFileName()), - 86400*1000,10*1024*1024,1000); -} - - -//------------------------------------------ -// Overload ostringstream for qt objects -//------------------------------------------ -std::ostream& operator <<(std::ostream &stream,const QString &str) -{ - stream << str.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QModelIndex& idx) -{ - QString s; - QDebug ts(&s); - ts << idx; - stream << s.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QVariant &v) -{ - QString s; - QDebug ts(&s); - ts << v; - stream << s.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QStringList &lst) -{ - QString s; - QDebug ts(&s); - ts << lst; - stream << s.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QRegion &r) -{ - QString s; - QDebug ts(&s); - ts << r; - stream << s.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QRect &r) -{ - QString s; - QDebug ts(&s); - ts << r; - stream << s.toStdString(); - return stream; -} - -std::ostream& operator <<(std::ostream &stream,const QPoint &p) -{ - QString s; - QDebug ts(&s); - ts << p; - stream << s.toStdString(); - return stream; -} diff -Nru ecflow-4.9.0/Viewer/src/UiLog.hpp ecflow-4.11.1/Viewer/src/UiLog.hpp --- ecflow-4.9.0/Viewer/src/UiLog.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UiLog.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,93 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef UILOG_HPP -#define UILOG_HPP - -#include -#include -#include - -class QString; -class QModelIndex; -class QStringList; -class QVariant; -class QPoint; -class QRegion; -class QRect; - -class ServerHandler; - -#define UI_FUNCTION_LOG UiFunctionLog __fclog(BOOST_CURRENT_FUNCTION); -#define UI_FUNCTION_LOG_S(server) UiFunctionLog __fclog(server,BOOST_CURRENT_FUNCTION); - -class UiFunctionLog -{ -public: - UiFunctionLog(ServerHandler* server,const std::string& funcName); - UiFunctionLog(const std::string& funcName); - ~UiFunctionLog(); - - std::string logEnter() const; - std::string logLeave() const; -protected: - void init(); - - std::string serverName_; - std::string funcName_; -}; - -class UiLog -{ -public: - enum Type {INFO,WARN,ERROR,DBG}; - - UiLog() : type_(INFO) {} - UiLog(ServerHandler* server); - UiLog(const std::string& server); - ~UiLog(); - - std::ostringstream& info(); - std::ostringstream& err(); - std::ostringstream& warn(); - std::ostringstream& dbg(); - - static void enableTruncation(); - - static std::string toString(int i) { - std::stringstream ss; - ss << i; - return ss.str(); - } - - -protected: - void output(const std::string& msg); - void appendType(std::string& s,Type t) const; - - Type type_; - std::ostringstream os_; - std::string server_; - -private: - UiLog(const UiLog&); - UiLog& operator =(const UiLog&); -}; - -//Overload ostringstream for qt objects -std::ostream& operator <<(std::ostream&,const QString &); -std::ostream& operator <<(std::ostream&,const QModelIndex&); -std::ostream& operator <<(std::ostream&,const QVariant&); -std::ostream& operator <<(std::ostream&,const QStringList&); -std::ostream& operator <<(std::ostream&,const QRegion&); -std::ostream& operator <<(std::ostream&,const QRect&); -std::ostream& operator <<(std::ostream&,const QPoint&); - -#endif // UILOG_HPP - diff -Nru ecflow-4.9.0/Viewer/src/UpdateTimer.cpp ecflow-4.11.1/Viewer/src/UpdateTimer.cpp --- ecflow-4.9.0/Viewer/src/UpdateTimer.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UpdateTimer.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "UpdateTimer.hpp" - -void UpdateTimer::drift(int dValSec, int maxValMin) -{ - double v=interval() + dValSec*1000; - if( v > maxValMin*1000*60) - v=maxValMin*1000*60; - - setInterval(v); -} - diff -Nru ecflow-4.9.0/Viewer/src/UpdateTimer.hpp ecflow-4.11.1/Viewer/src/UpdateTimer.hpp --- ecflow-4.9.0/Viewer/src/UpdateTimer.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UpdateTimer.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,22 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef UPDATETIMER_HPP_ -#define UPDATETIMER_HPP_ - -#include - -class UpdateTimer : public QTimer -{ -public: - UpdateTimer(QObject* parent=0) : QTimer(parent) {} - void drift(int,int); -}; - -#endif // UPDATETIMER_HPP_ diff -Nru ecflow-4.9.0/Viewer/src/UserMessage.cpp ecflow-4.11.1/Viewer/src/UserMessage.cpp --- ecflow-4.9.0/Viewer/src/UserMessage.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UserMessage.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ - -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - -#include - -#include "UserMessage.hpp" - -#include "UiLog.hpp" - -bool UserMessage::echoToCout_ = true; // XXX should be false to start with - -UserMessage::UserMessage() -{ -} - -void UserMessage::message(MessageType type, bool popup, const std::string& message) -{ - if (echoToCout_) - { -#if 0 - switch (type) - { - case INFO: std::cout << "INFO : "; break; - case WARN: std::cout << "WARN : "; break; - case ERROR: std::cout << "ERROR : "; break; - case DBG: std::cout << "DEBUG : "; break; - default: std::cout << " : "; break; - } - - std::cout << message << std::endl; -#endif - - switch (type) - { - case DBG: UiLog().dbg() << message; break; - case INFO: UiLog().info() << message ; break; - case WARN: UiLog().warn() << message; break; - case ERROR: UiLog().err() << message; break; - default: break; - } - } - - if (popup) - { - QMessageBox::Icon icon; - QString title; - switch (type) - { - case INFO: icon = QMessageBox::Information; title="Info"; break; - case WARN: icon = QMessageBox::Warning; title="Warning"; break; - case ERROR: icon = QMessageBox::Critical; title="Error"; break; - case DBG: icon = QMessageBox::NoIcon; title="Debug"; break; - default: icon = QMessageBox::NoIcon; title="Info"; break; - } - title="ecFlowUI - " + title; - QString qmsg = QString::fromStdString(message); - - QMessageBox box(icon, title, qmsg); - box.exec(); - } - -} - -bool UserMessage::confirm(const std::string& message) -{ - bool ok = true; - QMessageBox msgBox; - msgBox.setText(QString::fromStdString(message)); - msgBox.setTextFormat(Qt::RichText); - msgBox.setIcon(QMessageBox::Question); - msgBox.setStandardButtons(QMessageBox::Ok | QMessageBox::Cancel); - if (msgBox.exec() == QMessageBox::Cancel) - { - ok=false; - } - return ok; -} - - -void UserMessage::debug(const std::string& message) -{ - UiLog().dbg() << message; -} - -std::string UserMessage::toString(int v) -{ - return QString::number(v).toStdString(); -} diff -Nru ecflow-4.9.0/Viewer/src/UserMessage.hpp ecflow-4.11.1/Viewer/src/UserMessage.hpp --- ecflow-4.9.0/Viewer/src/UserMessage.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/UserMessage.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef USER_MESSAGE_HPP_ -#define USER_MESSAGE_HPP_ - -#include -#include - -#include - -class UserMessage -{ -//Q_OBJECT -public: - UserMessage(); - - enum MessageType {INFO, WARN, ERROR, DBG}; // note: cannot have DEBUG because of possible -DDEBUG in cpp! - - static void setEchoToCout(bool toggle) {echoToCout_ = toggle;} - static void message(MessageType type, bool popup, const std::string& message); - static bool confirm(const std::string& message); - - static void debug(const std::string& message); - static std::string toString(int); - -private: - static bool echoToCout_; - - -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VariableAddDialog.ui ecflow-4.11.1/Viewer/src/VariableAddDialog.ui --- ecflow-4.9.0/Viewer/src/VariableAddDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableAddDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ - - - VariableAddDialog - - - - 0 - 0 - 286 - 161 - - - - Add variable - - - true - - - - - - - 0 - 0 - - - - Add new variable for - - - - - - - - - - - - - &Name: - - - nameEdit_ - - - - - - - false - - - - - - - &Value: - - - valueEdit_ - - - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      -
      - - - - buttonBox_ - accepted() - VariableAddDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox_ - rejected() - VariableAddDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/VariableEditor.cpp ecflow-4.11.1/Viewer/src/VariableEditor.cpp --- ecflow-4.9.0/Viewer/src/VariableEditor.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableEditor.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VariableEditor.hpp" - -#include - -#include "AttributeEditorFactory.hpp" -#include "CommandHandler.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VGenVarAttr.hpp" -#include "SessionHandler.hpp" - -VariableEditorWidget::VariableEditorWidget(QWidget* parent) : QWidget(parent) -{ - setupUi(this); - - QLayoutItem *item; - item=grid_->itemAtPosition(1,0); - Q_ASSERT(item); - item->setAlignment(Qt::AlignLeft|Qt::AlignTop); -} - -VariableEditor::VariableEditor(VInfo_ptr info,QWidget* parent) : - AttributeEditor(info,"variable",parent), - readOnly_(false) -{ - w_=new VariableEditorWidget(this); - addForm(w_); - - VAttribute* a=info_->attribute(); - - Q_ASSERT(a); - Q_ASSERT(a->type()); - Q_ASSERT(a->type()->name() == "var" || a->type()->name() == "genvar"); - - if(a->data().count() < 2) - return; - - QString name=a->data().at(1); - QString val; - if(a->data().count() > 2) - val=a->data().at(2); - - oriVal_=val; - - w_->nameLabel_->setText(name); - w_->valueTe_->setPlainText(val); - w_->valueTe_->setFocus(); - - QString typeLabel=(a->type()->name() == "var")?"User variable":"Generated variable"; - readOnly_=VGenVarAttr::isReadOnly(name.toStdString()); - if(readOnly_) - { - w_->valueTe_->setReadOnly(true); - typeLabel+=" (read only)"; - } - else - { - connect(w_->valueTe_,SIGNAL(textChanged()), - this,SLOT(slotValueChanged())); - } - - header_->setInfo(QString::fromStdString(info_->path()),typeLabel); - - checkButtonStatus(); - - readSettings(); -} - -VariableEditor::~VariableEditor() -{ - writeSettings(); -} - -void VariableEditor::apply() -{ - if(!readOnly_) - { - std::string val=w_->valueTe_->toPlainText().toStdString(); - std::string name=w_->nameLabel_->text().toStdString(); - - if(val != oriVal_.toStdString()) - { - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","variable",name,val); - CommandHandler::run(info_,cmd); - } - } -} - -void VariableEditor::resetValue() -{ - w_->valueTe_->setPlainText(oriVal_); - checkButtonStatus(); -} - -void VariableEditor::slotValueChanged() -{ - checkButtonStatus(); -} - -bool VariableEditor::isValueChanged() -{ - return (oriVal_ != w_->valueTe_->toPlainText()); -} - -void VariableEditor::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableEditor")), - QSettings::NativeFormat); - - //We have to clear it so that should not remember all the previous values - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void VariableEditor::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableEditor")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(310,200)); - } - - settings.endGroup(); -} - -static AttributeEditorMaker makerStrVar("var"); -static AttributeEditorMaker makerStrGenvar("genvar"); diff -Nru ecflow-4.9.0/Viewer/src/VariableEditor.hpp ecflow-4.11.1/Viewer/src/VariableEditor.hpp --- ecflow-4.9.0/Viewer/src/VariableEditor.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableEditor.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VARIABLEEDITOR_HPP -#define VARIABLEEDITOR_HPP - -#include "ui_VariableEditorWidget.h" - -#include "AttributeEditor.hpp" -#include "VInfo.hpp" - -class VariableEditor; - -class VariableEditorWidget : public QWidget, protected Ui::VariableEditorWidget -{ -friend class VariableEditor; -public: - VariableEditorWidget(QWidget *parent=0); -}; - -class VariableEditor : public AttributeEditor -{ -Q_OBJECT -public: - VariableEditor(VInfo_ptr,QWidget* parent=0); - ~VariableEditor(); - -protected Q_SLOTS: - void slotValueChanged(); - -protected: - void apply(); - void resetValue(); - bool isValueChanged(); - void readSettings(); - void writeSettings(); - - VariableEditorWidget* w_; - QString oriVal_; - bool readOnly_; -}; - -#endif // VARIABLEEDITOR_HPP diff -Nru ecflow-4.9.0/Viewer/src/VariableEditorWidget.ui ecflow-4.11.1/Viewer/src/VariableEditorWidget.ui --- ecflow-4.9.0/Viewer/src/VariableEditorWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableEditorWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,64 +0,0 @@ - - - VariableEditorWidget - - - - 0 - 0 - 329 - 227 - - - - Form - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - TextLabel - - - - - - - Value: - - - - - - - - 0 - 1 - - - - - - - - Name: - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/VariableItemWidget.cpp ecflow-4.11.1/Viewer/src/VariableItemWidget.cpp --- ecflow-4.9.0/Viewer/src/VariableItemWidget.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableItemWidget.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,1258 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VariableItemWidget.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "IconProvider.hpp" -#include "LineEdit.hpp" -#include "SessionHandler.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "VariableModel.hpp" -#include "VariableModelData.hpp" -#include "VariableSearchLine.hpp" -#include "VConfig.hpp" -#include "VProperty.hpp" -#include "WidgetNameProvider.hpp" - -#define _UI_VARIABLEITEMWIDGET_DEBUG -#define _UI_VARIABLESORTMODELTEST_DEBUG - -//====================================== -// -// VariablePropDialog -// -//====================================== - -VariablePropDialog::VariablePropDialog(VariableModelDataHandler *data,int defineIndex,QString name, QString value,bool frozen,QWidget *parent) : - QDialog(parent), - genVar_(false), - data_(data), - defineIndex_(defineIndex), - oriName_(name), - cleared_(false), - suspended_(false) -{ - setupUi(this); - setAttribute(Qt::WA_DeleteOnClose); - setModal(false); - - QString wt="Edit variable"; - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - Q_ASSERT(data_); - Q_ASSERT(data_->count() > 0); - Q_ASSERT(data_->count() > defineIndex_); - - nodeName_=QString::fromStdString(data_->data(0)->name()); - nodeType_=QString::fromStdString(data_->data(0)->type()); - nodeTypeCapital_=nodeType_; - if(nodeTypeCapital_.size() > 0) - { - QChar s=nodeTypeCapital_.at(0); - s=s.toUpper(); - nodeTypeCapital_.replace(0,1,s); - } - - data_->addObserver(this); - - genVar_=data_->data(defineIndex)->isGenVar(name.toStdString()); - - QString path=QString::fromStdString(data_->data(0)->fullPath()); - QString h=EditorInfoLabel::formatKeyLabel("Node to modify: ") + "" + - EditorInfoLabel::formatNodeName(nodeName_) + "
      "; - h+= EditorInfoLabel::formatKeyLabel("Node path: ") + EditorInfoLabel::formatNodePath(path) + "
      "; - - VariableModelData* defineData=data_->data(defineIndex_); - Q_ASSERT(defineData); - defineNodeName_=QString::fromStdString(defineData->name()); - defineNodeType_=QString::fromStdString(defineData->type()); - - genVar_=defineData->isGenVar(name.toStdString()); - h+=EditorInfoLabel::formatKeyLabel("Variable type: "); - h+=(genVar_)?tr("generated variable"):tr("user variable"); - - bool readOnly=defineData->isReadOnly(name.toStdString()); - if(readOnly) - { - h+=" (read only)"; - } - - if(defineIndex_ > 0) - { - QString definePath=QString::fromStdString(defineData->fullPath()); - h+="
      " + EditorInfoLabel::formatKeyLabel("Inherited from: ") + EditorInfoLabel::formatNodePath(definePath); - } - header_->setText(h); - valueEdit_->setProperty("form","1"); - nameEdit_->setText(name); - valueEdit_->setPlainText(value); - valueEdit_->setFocus(); - - if(frozen || readOnly) - { - nameEdit_->setReadOnly(true); - valueEdit_->setReadOnly(true); - - QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); - Q_ASSERT(sb); - sb->setEnabled(false); - } - - messageLabel_->hide(); - - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -VariablePropDialog::~VariablePropDialog() -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - Q_ASSERT(data_); - data_->removeObserver(this); - writeSettings(); -} - -void VariablePropDialog::accept() -{ - QString name=nameEdit_->text(); - //QString value=valueEdit_->toPlainText(); - - Q_ASSERT(data_); - Q_ASSERT(data_->count() > 0); - Q_ASSERT(data_->count() > defineIndex_); - - //var does not exists in SELECTED node - if(!data_->data(0)->hasName(name.toStdString())) - { - for(int i=1; i < data_->count(); i++) - { - //but exists in one of the parents - if(data_->data(i)->hasName(name.toStdString())) - { - QString type=QString::fromStdString(data_->data(i)->type()); - QString path =QString::fromStdString(data_->data(i)->fullPath()); - if(QMessageBox::question(0,tr("Confirm: create new variable"), - tr("Variable ") + name + tr(" is originally defined in ") + type + " " + path + - tr(". A new user variable will be created for ") + nodeType_ + " " + nodeName_ + - tr(" and shadow the original one.

      Do you want to proceed?"), - QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return; - } - else - { - QDialog::accept(); - return; - } - } - } - - //It is a ne variable - if(QMessageBox::question(0,tr("Confirm: create new variable"), - tr("You are about to create a new variable in ") + nodeType_ + " " + nodeName_ + "." + - tr("
      Do you want to proceed?"), - QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return; - } - else - { - QDialog::accept(); - return; - } - } - else if(data_->data(0)->isGenVar(name.toStdString())) - { - if(QMessageBox::question(0,QObject::tr("Confirm: change variable"), - tr("You are about to modify a generated variable.
      Do you want to proceed?"), - QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return; - } - else - { - QDialog::accept(); - return; - } - } - - QDialog::accept(); -} - -void VariablePropDialog::on_nameEdit__textEdited(QString) -{ - messageLabel_->hide(); -} - -void VariablePropDialog::on_valueEdit__textChanged() -{ - messageLabel_->hide(); -} - -QString VariablePropDialog::name() const -{ - return nameEdit_->text(); -} - -QString VariablePropDialog::value() const -{ - return valueEdit_->toPlainText(); -} - -void VariablePropDialog::notifyCleared(VariableModelDataHandler*) -{ - //When we are not in suspended mode and the data_ is cleared - //we need to close the dialogue - if(!suspended_) - close(); - - //However, when the suspended mode finished the data_ is cleared and reloaded before - //this dialogue gets the notification about the suspended mode change. So - //we delay the decision on what to do unitl we receieve this notification in - //slotSuspendedChanged() -} - -void VariablePropDialog::notifyUpdated(VariableModelDataHandler*) -{ - Q_ASSERT(data_); - - QString name=nameEdit_->text(); - QString value=valueEdit_->toPlainText(); - - bool st=false; - QString v=QString::fromStdString(data_->value(defineNodeName_.toStdString(),name.toStdString(),st)); - if(!st) - { - messageLabel_->showWarning("Variable " + name + " is not defined any more in " + defineNodeType_ + - " " + defineNodeName_ + "!"); - } - else if(v != value) - { - messageLabel_->showWarning("The value of variable " + name + " changed in " + defineNodeType_ + - " " + defineNodeName_ + "!"); - } - else - { - messageLabel_->hide(); - } -} - -void VariablePropDialog::slotSuspendedChanged(bool s) -{ - if(cleared_) - return; - - if(s) - { - messageLabel_->showWarning("The server holding " + nodeType_ + " " + nodeName_ + - " is being reloaded. \ - Until it is finished variables cannot be edited!"); - - suspendEdit(true); - } - else - { - messageLabel_->clear(); - messageLabel_->hide(); - suspendEdit(false); - - //We we have just left the suspended mode we need to chek if the data we - //show is still available - notifyUpdated(data_); - } -} - -void VariablePropDialog::suspendEdit(bool st) -{ - suspended_=st; - - if(st) - { - QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); - Q_ASSERT(sb); - sb->setEnabled(false); - form_->setEnabled(false); - } - else - { - QPushButton* sb=buttonBox_->button(QDialogButtonBox::Save); - Q_ASSERT(sb); - sb->setEnabled(true); - form_->setEnabled(true); - } -} - -void VariablePropDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariablePropDialog")), - QSettings::NativeFormat); - - //We have to clear it not to remember all the previous windows - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void VariablePropDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariablePropDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(350,250)); - } - - settings.endGroup(); -} - - -//====================================== -// -// VariableAddDialog -// -//====================================== - -VariableAddDialog::VariableAddDialog(VariableModelDataHandler *data,QWidget *parent) : - QDialog(parent), - data_(data), - cleared_(false), - suspended_(false) -{ - setupUi(this); - - init(); - nameEdit_->setFocus(); - - QString wt="Add variable"; - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - readSettings(); - - WidgetNameProvider::nameChildren(this); -} - -VariableAddDialog::VariableAddDialog(VariableModelDataHandler *data,QString name, QString value,QWidget *parent) : - QDialog(parent), - data_(data), - cleared_(false) -{ - setupUi(this); - - init(); - - nameEdit_->setText(name + "_copy"); - valueEdit_->setText(value); - nameEdit_->setFocus(); - - QString wt="Add variable"; - wt+=" - " + QString::fromStdString(VConfig::instance()->appLongName()); - setWindowTitle(wt); - - readSettings(); -} - -VariableAddDialog::~VariableAddDialog() -{ - data_->removeObserver(this); - writeSettings(); -} - -void VariableAddDialog::init() -{ - setAttribute(Qt::WA_DeleteOnClose); - setModal(false); - - Q_ASSERT(data_); - Q_ASSERT(data_->count() > 0); - - nodeName_=QString::fromStdString(data_->data(0)->name()); - nodeType_=QString::fromStdString(data_->data(0)->type()); - nodeTypeCapital_=nodeType_; - if(nodeTypeCapital_.size() > 0) - { - QChar s=nodeTypeCapital_.at(0); - s=s.toUpper(); - nodeTypeCapital_.replace(0,1,s); - } - data_->addObserver(this); - - label_->setText(tr("Add new variable to ") + - nodeType_ + " " + - nodeName_ + "") ; - //+ "
      Path: " + - // QString::fromStdString(data_->data(0)->fullPath())); - - messageLabel_->hide(); -} - - -void VariableAddDialog::accept() -{ - QString name=nameEdit_->text(); - - if(name.simplified().isEmpty()) - { - QMessageBox::critical(0,tr("Invalid variable name"), - tr("Variable name cannot be empty! Please specify a valid name!"), - QMessageBox::Ok,QMessageBox::Ok); - return; - } - - Q_ASSERT(data_); - Q_ASSERT(data_->count() > 0); - - if(data_->data(0)->hasName(name.toStdString())) - { - QString q; - if(data_->data(0)->isGenVar(name.toStdString())) - { - q=tr("Generated variable ") + name + tr(" is already defined in ") + - nodeType_ + " " + nodeName_ + "" + - tr("
      . A new user variable will be created and the original variable will be hidden. \ -
      Do you want to proceed?"); - } - else - { - q=tr("User variable ") + name + tr(" is already defined in ") + - nodeType_ + " " + nodeName_ + "" + - tr(".
      Do you want to overwrite it?"); - } - - if(QMessageBox::question(0,tr("Confirm: overwrite variable"),q, - QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return; - } - else - { - QDialog::accept(); - } - return; - } - - for(int i=1; i count(); i++) - { - if(data_->data(i)->hasName(name.toStdString())) - { - QString nodeName=QString::fromStdString(data_->data(i)->name()); - QString nodeType=QString::fromStdString(data_->data(i)->type()); - - QString q; - if(data_->data(i)->isGenVar(name.toStdString())) - { - q=tr("Generated variable"); - } - else - { - q=tr("User variable"); - } - q+=" " + name + tr(" is already defined in ") + - nodeType + " " + nodeName + "" + - tr("
      . A new user variable will be created for ") + nodeType_ + " " + - nodeName_ + tr(" and shadow the original one. \ -
      Do you want to proceed?"); - - if(QMessageBox::question(0,tr("Confirm: overwrite variable"),q, - QMessageBox::Ok|QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Cancel) - { - return; - } - else - { - QDialog::accept(); - } - return; - } - } - - QDialog::accept(); -} - -QString VariableAddDialog::name() const -{ - return nameEdit_->text(); -} - -QString VariableAddDialog::value() const -{ - return valueEdit_->text(); -} - -void VariableAddDialog::notifyCleared(VariableModelDataHandler*) -{ - //When we are not in suspended mode and the data_ is cleared - //we need to close the dialogue - if(!suspended_) - close(); - - //However, when the suspended mode finished the data_ is cleared and reloaded before - //this dialogue gets the notification about the suspended mode change. So - //we delay the decision on what to do unitl we receieve this notification in - //slotSuspendedChanged() - -#if 0 - messageLabel_->showWarning(nodeTypeCapital_ + " " + nodeName_ + - " is not the node to modify any more in the Variables panel. Please close the dialog!"); - - suspendEdit(true); - - data_->removeObserver(this); - cleared_=true; -#endif -} - -void VariableAddDialog::slotSuspendedChanged(bool s) -{ - if(cleared_) - return; - - if(s) - { - messageLabel_->showWarning("The server holding " + nodeType_ + " " + nodeName_ + - " is being reloaded. \ - Until it is finished variables cannot be added!"); - - suspendEdit(true); - } - else - { - messageLabel_->clear(); - messageLabel_->hide(); - suspendEdit(false); - - //We we have just left the suspended mode, so we need to chek if the data_ we - //show is still available - if(!data_ || data_->count() == 0 || - nodeName_ != QString::fromStdString(data_->data(0)->name()) || - nodeType_ != QString::fromStdString(data_->data(0)->type())) - { - close(); - } - } -} - -void VariableAddDialog::suspendEdit(bool st) -{ - suspended_=st; - - if(st) - { - QPushButton* sb=buttonBox_->button(QDialogButtonBox::Ok); - Q_ASSERT(sb); - sb->setEnabled(false); - form_->setEnabled(false); - } - else - { - QPushButton* sb=buttonBox_->button(QDialogButtonBox::Ok); - Q_ASSERT(sb); - sb->setEnabled(true); - form_->setEnabled(true); - } -} - -void VariableAddDialog::writeSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableAddDialog")), - QSettings::NativeFormat); - - //We have to clear it not to remember all the previous windows - settings.clear(); - - settings.beginGroup("main"); - settings.setValue("size",size()); - settings.endGroup(); -} - -void VariableAddDialog::readSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - Q_ASSERT(cs); - QSettings settings(QString::fromStdString(cs->qtSettingsFile("VariableAddDialog")), - QSettings::NativeFormat); - - settings.beginGroup("main"); - if(settings.contains("size")) - { - resize(settings.value("size").toSize()); - } - else - { - resize(QSize(320,220)); - } - - settings.endGroup(); -} - -//======================================================== -// -// VariableItemWidget -// -//======================================================== - -VariableItemWidget::VariableItemWidget(QWidget *parent) : - shadowProp_(0), - canSaveLastSelection_(true) -{ - //This item displays all the ancestors of the info object - useAncestors_=true; - - setupUi(this); - - data_=new VariableModelDataHandler(); - - //The model and the sort-filter model - model_=new VariableModel(data_,this); - sortModel_= new VariableSortModel(model_,this); - sortModel_->setDynamicSortFilter(true); - - //Set the model on the view - varView->setModel(sortModel_); - - varView->setSelectionBehavior(QAbstractItemView::SelectRows); - varView->setSelectionMode(QAbstractItemView::SingleSelection); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - varView->header()->setSectionResizeMode(1,QHeaderView::ResizeToContents); - varView->header()->setStretchLastSection(false); -#endif - - //Show shadowed variables by default - bool showShadowed = true; - - //But we overwrite it with the config settings. This property is directly edited - //via the toolbutton, so we do not need to observe it. - shadowProp_=VConfig::instance()->find("panel.variable.showShadowed"); - if(shadowProp_) - { - showShadowed=shadowProp_->value().toBool(); - } - sortModel_->slotShowShadowed(showShadowed); - shadowTb->setChecked(showShadowed); - - //Search and filter interface: we have a menu attached to a toolbutton and a - //stackedwidget connected up. - - //Populate the toolbuttons menu - findModeTb->addAction(actionFilter); - findModeTb->addAction(actionSearch); - - //The filter line editor - filterLine_=new LineEdit; - stackedWidget->addWidget(filterLine_); - - //The search line editor. It is a custom widget handling its own signals and slots. - searchLine_=new VariableSearchLine(this); - stackedWidget->addWidget(searchLine_); - searchLine_->setView(varView); - - //The filter editor changes - connect(filterLine_,SIGNAL(textChanged(QString)), - this,SLOT(slotFilterTextChanged(QString))); - - //The selection changes in the view - connect(varView->selectionModel(),SIGNAL(currentChanged(QModelIndex,QModelIndex)), - this,SLOT(slotItemSelected(QModelIndex,QModelIndex))); - - //Init the find mode selection - if(sortModel_->matchMode() == VariableSortModel::FilterMode) - { - actionFilter->trigger(); - } - else - { - actionSearch->trigger(); - } - - //Add context menu actions to the view - QAction* sep1=new QAction(this); - sep1->setSeparator(true); - QAction* sep2=new QAction(this); - sep2->setSeparator(true); - QAction* sep3=new QAction(this); - sep3->setSeparator(true); - - //Build context menu - varView->addAction(actionAdd); - varView->addAction(sep1); - varView->addAction(actionCopy); - varView->addAction(actionCopyFull); - //varView->addAction(actionPaste); - varView->addAction(sep2); - varView->addAction(actionDelete); - varView->addAction(sep3); - varView->addAction(actionProp); - - //Add actions for the toolbuttons - addTb->setDefaultAction(actionAdd); - deleteTb->setDefaultAction(actionDelete); - propTb->setDefaultAction(actionProp); - exportTb->setDefaultAction(actionExport); - - //TODO: implement it - actionExport->setEnabled(false); - exportTb->setVisible(false); - - //Initialise action state (it depends on the selection) - checkActionState(); -} - -VariableItemWidget::~VariableItemWidget() -{ - -} - -QWidget* VariableItemWidget::realWidget() -{ - return this; -} - -//A new info object is set -void VariableItemWidget::reload(VInfo_ptr info) -{ - assert(active_); - - if(suspended_) - return; - - clearContents(); - adjust(info); - - data_->reload(info); - varView->expandAll(); - varView->resizeColumnToContents(0); - - if(data_->count() > 0) - { - actionAdd->setText(tr("Add &new variable to ") + - QString::fromStdString(data_->data(0)->name())); - - actionAdd->setToolTip(tr("Add new variable to ") + - "" + QString::fromStdString(data_->data(0)->name()) + ""); - } - - checkActionState(); -} - -void VariableItemWidget::clearContents() -{ - InfoPanelItem::clear(); - data_->clear(); - lastSelection_.reset(); - expanded_.clear(); - actionAdd->setText(tr("Add &new variable")); -} - - -void VariableItemWidget::slotItemSelected(const QModelIndex& idx,const QModelIndex& /*prevIdx*/) -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - - //remembers the last clicked variable - UiLog().dbg() << idx; - if(canSaveLastSelection_) - lastSelection_=model_->indexToInfo(sortModel_->mapToSource(idx)); - - checkActionState(); -} - -void VariableItemWidget::updateState(const FlagSet& flags) -{ - if(flags.isSet(SuspendedChanged)) - { - if(info_) - { - //If it just became non-suspended we need to refresh all the data!!! - if(!suspended_) - { - canSaveLastSelection_=false; - data_->reload(info_); - - restoreExpandState(); - - //After any change done we need to reselect the current row. See issue ECFLOW-613. - regainSelection(); - canSaveLastSelection_=true; - } - else - { - saveExpandState(); - } - } - - //It is very important to only emit the signal when the - //data has been updated! In this way the dialogues can check if their contents are - //in sync with what data_ stores. - Q_EMIT suspendedChanged(suspended_); - } - checkActionState(); -} - -void VariableItemWidget::checkActionState() -{ - QModelIndex index=sortModel_->mapToSource(varView->currentIndex()); - - if(suspended_ || !info_) - { - actionAdd->setEnabled(false); - actionProp->setEnabled(false); - actionDelete->setEnabled(false); - actionCopy->setEnabled(false); - actionCopyFull->setEnabled(false); - return; - } - - //The index is invalid (no selection) - if(!index.isValid()) - { - actionProp->setEnabled(false); - actionDelete->setEnabled(false); - actionCopy->setEnabled(false); - actionCopyFull->setEnabled(false); - } - else - { - //Variables - if(model_->isVariable(index)) - { - if(frozen_) - { - actionDelete->setEnabled(false); - } - else - { - int block=-1; - if(model_->indexToData(index,block)) - { - if(block==0) - actionDelete->setEnabled(true); - else - actionDelete->setEnabled(false); - } - else - actionDelete->setEnabled(false); - } - actionProp->setEnabled(true); - actionCopy->setEnabled(true); - actionCopyFull->setEnabled(true); - } - //Server or nodes - else - { - if(frozen_) - { - actionDelete->setEnabled(false); - } - else - { - actionDelete->setEnabled(false); - } - actionProp->setEnabled(false); - actionCopy->setEnabled(false); - actionCopyFull->setEnabled(false); - } - } - - if(frozen_) - { - actionAdd->setEnabled(false); - } - else - { - actionAdd->setEnabled(true); - } -} - -void VariableItemWidget::editItem(const QModelIndex& index) -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG - UiLog().dbg() << " index=" << index; -#endif - - QString name; - QString value; - bool genVar; - - QModelIndex vIndex=sortModel_->mapToSource(index); -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UiLog().dbg() << "vIndex=" << vIndex; -#endif - - int block=-1; - if(model_->indexToData(vIndex,block)) - { - Q_ASSERT(data_->count() > 0); - Q_ASSERT(block >=0); - -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UiLog().dbg() << " block=" << block; -#endif - - if(model_->variable(vIndex,name,value,genVar)) - { - //Start the edit dialog (will be deleted on close - deleteOnClose is set) - VariablePropDialog* d=new VariablePropDialog(data_,block,name,value,frozen_,this); - connect(d,SIGNAL(accepted()), - this,SLOT(slotVariableEdited())); - connect(this,SIGNAL(suspendedChanged(bool)), - d,SLOT(slotSuspendedChanged(bool))); - d->show(); - } - } -} - -void VariableItemWidget::duplicateItem(const QModelIndex& index) -{ - if(frozen_) - return; - -#if 0 - QString name; - QString value; - bool genVar; - - QModelIndex vIndex=sortModel_->mapToSource(index); - - VariableModelData* data=model_->indexToData(vIndex); - - //Get the data from the model - if(data && model_->variable(vIndex,name,value,genVar)) - { - //Start add dialog - VariableAddDialog d(data,name,value,this); - - if(d.exec() == QDialog::Accepted) - { - data->alter(d.name().toStdString(),d.value().toStdString()); - //data->add(d.name().toStdString(),d.value().toStdString()); - } - } -#endif - -} - -void VariableItemWidget::addItem(const QModelIndex& index) -{ - if(frozen_) - return; - - if(data_->count() > 0) - { - //Start add dialog (will be deleted on close - deleteOnClose is set) - VariableAddDialog* d=new VariableAddDialog(data_,this); - connect(d,SIGNAL(accepted()), - this,SLOT(slotVariableAdded())); - connect(this,SIGNAL(suspendedChanged(bool)), - d,SLOT(slotSuspendedChanged(bool))); - d->show(); - - } -} - -void VariableItemWidget::removeItem(const QModelIndex& index) -{ - if(frozen_) - return; - - QString name; - QString value; - bool genVar; - - QModelIndex vIndex=sortModel_->mapToSource(index); - - VariableModelData* data=model_->indexToData(vIndex); - - //Get the data from the model - if(data && model_->variable(vIndex,name,value,genVar)) - { - std::string nodePath=data->fullPath(); - std::string nodeName=data->name(); - - if(QMessageBox::question(0,tr("Confirm: delete variable"), - tr("Are you sure that you want to delete variable ") + name + " from " + - QString::fromStdString(data->type()) + " " + QString::fromStdString(data->name()) + "?", - QMessageBox::Ok | QMessageBox::Cancel,QMessageBox::Cancel) == QMessageBox::Ok) - { - //data might have been changed while the dialog was open - //so we cant use the data object but need to look up the variable again - int block=-1; - int row=-1; - data_->findVariable(name.toStdString(),nodePath,genVar,block,row); - if(block != -1 && row != -1) - { - data_->data(block)->remove(name.toStdString()); - } - else - { - std::string msg="Could not delete variable=" + name.toStdString() + - " from node " + nodeName + "!"; - - if(block == -1) - msg+=" Node does not exist."; - else - msg+=" Variable does not exist."; - - UserMessage::message(UserMessage::ERROR,true,msg); - } - } - } -} - -//Called when the variable has been edited in the dialogue -void VariableItemWidget::slotVariableEdited() -{ - VariablePropDialog* d=static_cast(sender()); - Q_ASSERT(d); - - if(data_->count() > 0) - { - //This will call the ServerComThread so we - //do not know if it was successful or not. The model will be - //updated through the observer when the value will actually - //change. - //We always perform the alter variable operation on the selected - //node i.e. on block 0 = data(0) !!! - data_->data(0)->alter(d->name().toStdString(),d->value().toStdString()); - } -} - -void VariableItemWidget::slotVariableAdded() -{ - VariableAddDialog* d=static_cast(sender()); - Q_ASSERT(d); - Q_ASSERT(data_->count() > 0); - //We always perform the alter variable operation on the selected - //node i.e. on block 0 = data(0) !!! - data_->data(0)->alter(d->name().toStdString(),d->value().toStdString()); -} - -void VariableItemWidget::on_varView_doubleClicked(const QModelIndex& index) -{ - if(!suspended_) - editItem(index); -} - -void VariableItemWidget::on_actionProp_triggered() -{ - QModelIndex index=varView->currentIndex(); - editItem(index); -} - -void VariableItemWidget::on_actionAdd_triggered() -{ - QModelIndex index=varView->currentIndex(); - addItem(index); -} - -void VariableItemWidget::on_actionDelete_triggered() -{ - QModelIndex index=varView->currentIndex(); - removeItem(index); -} - -void VariableItemWidget::on_actionFilter_triggered() -{ - findModeTb->setIcon(actionFilter->icon()); - sortModel_->setMatchMode(VariableSortModel::FilterMode); - filterLine_->clear(); - searchLine_->clear(); - - //Notify stackedwidget - stackedWidget->setCurrentIndex(0); -} - -void VariableItemWidget::on_actionSearch_triggered() -{ - findModeTb->setIcon(actionSearch->icon()); - sortModel_->setMatchMode(VariableSortModel::SearchMode); - filterLine_->clear(); - searchLine_->clear(); - - //Notify stackedwidget - stackedWidget->setCurrentIndex(1); - -} - -void VariableItemWidget::on_actionCopy_triggered() -{ - QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); - QString name, val; - bool gen; - - if(model_->variable(idx,name,val,gen)) - { - QString txt; - if(idx.column() == 0) - toClipboard(name); - else if(idx.column() == 1) - toClipboard(val); - } -} - -void VariableItemWidget::on_actionCopyFull_triggered() -{ - QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); - QString name, val; - bool gen; - - if(model_->variable(idx,name,val,gen)) - { - toClipboard(name + "=" + val); - } -} - -void VariableItemWidget::on_shadowTb_clicked(bool showShadowed) -{ - if(shadowProp_) - { - shadowProp_->setValue(showShadowed); - } - sortModel_->slotShowShadowed(showShadowed); -} - -void VariableItemWidget::toClipboard(QString txt) const -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QClipboard* cb=QGuiApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#else - QClipboard* cb=QApplication::clipboard(); - cb->setText(txt, QClipboard::Clipboard); - cb->setText(txt, QClipboard::Selection); -#endif -} - -void VariableItemWidget::slotFilterTextChanged(QString text) -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - sortModel_->setMatchText(text); - regainSelection(); -} - -void VariableItemWidget::nodeChanged(const VNode* node, const std::vector& aspect) -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - canSaveLastSelection_=false; - if(data_->nodeChanged(node,aspect)) - { - //After any change done we need to reselect the current row. See issue ECFLOW-613. - regainSelection(); - } - canSaveLastSelection_=true; -} - -void VariableItemWidget::defsChanged(const std::vector& aspect) -{ -#ifdef _UI_VARIABLEITEMWIDGET_DEBUG - UI_FUNCTION_LOG -#endif - canSaveLastSelection_=false; - if(data_->defsChanged(aspect)) - { - //After any change we need to reselect the current row. See issue ECFLOW-613. - regainSelection(); - } - canSaveLastSelection_=true; -} - -//Try to regain the selection stored in lastSelection_ potentailly after a -//full model reset!!! -void VariableItemWidget::regainSelection() -{ - if(lastSelection_) - { - lastSelection_->regainData(); - if(lastSelection_->hasData()) - { - QModelIndex idx=model_->infoToIndex(lastSelection_); - if(idx.isValid()) - { - varView->setCurrentIndex(sortModel_->mapFromSource(idx)); - } - } - else - { - lastSelection_.reset(); - } - } - -#if 0 - QModelIndex idx=sortModel_->mapToSource(varView->currentIndex()); - if(idx.column() == 1) - { - idx=model_->index(idx.row(),0,idx.parent()); - } - varView->selectionModel()->clear(); - QModelIndex sortIdx=sortModel_->mapFromSource(idx); - varView->selectionModel()->setCurrentIndex(sortIdx,QItemSelectionModel::Rows| - QItemSelectionModel::Select); -#endif -} - -void VariableItemWidget::saveExpandState() -{ - expanded_.clear(); - for(int i=0; i< varView->model()->rowCount(); i++) - { - expanded_ << varView->isExpanded(varView->model()->index(i,0)); - } -} - -void VariableItemWidget::restoreExpandState() -{ - if(expanded_.isEmpty()) - varView->expandAll(); - else - { - for(int i=0; i< varView->model()->rowCount() && i < expanded_.count(); i++) - { - varView->setExpanded(varView->model()->index(i,0),expanded_[i]); - } - } -} - - -//Register at the factory -static InfoPanelItemMaker maker1("variable"); diff -Nru ecflow-4.9.0/Viewer/src/VariableItemWidget.hpp ecflow-4.11.1/Viewer/src/VariableItemWidget.hpp --- ecflow-4.9.0/Viewer/src/VariableItemWidget.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableItemWidget.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,166 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VARIABLEITEMWIDGET_HPP_ -#define VARIABLEITEMWIDGET_HPP_ - -#include "ui_VariablePropDialog.h" -#include "ui_VariableAddDialog.h" -#include "ui_VariableItemWidget.h" - -#include "InfoPanelItem.hpp" -#include "VariableModelDataObserver.hpp" -#include "VInfo.hpp" - -class LineEdit; -class VariableModel; -class VariableModelData; -class VariableModelDataHandler; -class VariableSortModel; -class VariableSearchLine; -class VProperty; - -class VariablePropDialog : public QDialog, public VariableModelDataObserver, private Ui::VariablePropDialog -{ -Q_OBJECT - -public: - VariablePropDialog(VariableModelDataHandler* data,int defineIndex,QString name,QString value,bool frozen,QWidget* parent=0); - ~VariablePropDialog(); - - QString name() const; - QString value() const; - - void notifyCleared(VariableModelDataHandler*); - void notifyUpdated(VariableModelDataHandler*); - -public Q_SLOTS: - void accept(); - void slotSuspendedChanged(bool s); - -protected Q_SLOTS: - void on_nameEdit__textEdited(QString); - void on_valueEdit__textChanged(); - -protected: - void suspendEdit(bool); - void readSettings(); - void writeSettings(); - - bool genVar_; - VariableModelDataHandler* data_; - int defineIndex_; - QString oriName_; - QString nodeName_; - QString nodeType_; - QString nodeTypeCapital_; - QString defineNodeName_; - QString defineNodeType_; - bool cleared_; - bool suspended_; - -}; - -class VariableAddDialog : public QDialog, public VariableModelDataObserver, private Ui::VariableAddDialog -{ -Q_OBJECT - -public: - VariableAddDialog(VariableModelDataHandler* data,QWidget* parent=0); - VariableAddDialog(VariableModelDataHandler* data,QString name,QString value,QWidget* parent=0); - ~VariableAddDialog(); - - QString name() const; - QString value() const; - - void notifyCleared(VariableModelDataHandler*); - void notifyUpdated(VariableModelDataHandler*) {} - -public Q_SLOTS: - void accept(); - void slotSuspendedChanged(bool s); - -protected: - void init(); - void suspendEdit(bool); - void readSettings(); - void writeSettings(); - - VariableModelDataHandler* data_; - QString nodeName_; - QString nodeType_; - QString nodeTypeCapital_; - bool cleared_; - bool suspended_; -}; - - -class VariableItemWidget : public QWidget, public InfoPanelItem, protected Ui::VariableItemWidget -{ -Q_OBJECT - -public: - explicit VariableItemWidget(QWidget *parent=0); - ~VariableItemWidget(); - - void reload(VInfo_ptr); - QWidget* realWidget(); - void clearContents(); - -public Q_SLOTS: - void slotFilterTextChanged(QString text); - void slotItemSelected(const QModelIndex& idx,const QModelIndex& prevIdx); - -protected Q_SLOTS: - void on_actionProp_triggered(); - void on_actionAdd_triggered(); - void on_actionDelete_triggered(); - void on_varView_doubleClicked(const QModelIndex& index); - void on_actionFilter_triggered(); - void on_actionSearch_triggered(); - void on_actionCopy_triggered(); - void on_actionCopyFull_triggered(); - void on_shadowTb_clicked(bool showShadowed); - void slotVariableEdited(); - void slotVariableAdded(); - -Q_SIGNALS: - void suspendedChanged(bool); - -protected: - void checkActionState(); - void editItem(const QModelIndex& index); - void duplicateItem(const QModelIndex& index); - void addItem(const QModelIndex& index); - void removeItem(const QModelIndex& index); - void updateState(const ChangeFlags&); - void toClipboard(QString txt) const; - void regainSelection(); - void saveExpandState(); - void restoreExpandState(); - - void nodeChanged(const VNode*, const std::vector&); - void defsChanged(const std::vector&); - - VariableModelDataHandler* data_; - VariableModel* model_; - VariableSortModel* sortModel_; - - LineEdit* filterLine_; - VariableSearchLine *searchLine_; - - VProperty* shadowProp_; - VInfo_ptr lastSelection_; - bool canSaveLastSelection_; - QList expanded_; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/VariableItemWidget.ui ecflow-4.11.1/Viewer/src/VariableItemWidget.ui --- ecflow-4.9.0/Viewer/src/VariableItemWidget.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableItemWidget.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,263 +0,0 @@ - - - VariableItemWidget - - - - 0 - 0 - 615 - 482 - - - - Form - - - - 2 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - ... - - - QToolButton::InstantPopup - - - Qt::ToolButtonIconOnly - - - true - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Show <b>shadowed</b> variables. A variable is shadowed when it is redefined in one of the descendants of its node shown in this panel. - - - - - - - :/viewer/show_shadowed.svg:/viewer/show_shadowed.svg - - - true - - - - - - - ... - - - true - - - - - - - ... - - - true - - - - - - - ... - - - true - - - - - - - ... - - - true - - - - - - - - - - - - - :/viewer/images/add.svg:/viewer/images/add.svg - - - Add &new variable - - - Add new variable - - - Ctrl+N - - - - - - :/viewer/edit.svg:/viewer/edit.svg - - - &Edit variable - - - See/edit variable's properties - - - Ctrl+E - - - - - - :/viewer/close.svg:/viewer/close.svg - - - &Delete variable - - - Delete variable - - - Del - - - - - - :/viewer/filesave.svg:/viewer/filesave.svg - - - E&xport - - - Export variables - - - Ctrl+S - - - - - - :/viewer/editcopy.svg:/viewer/editcopy.svg - - - &Copy item text under cursor - - - Copy variable - - - Ctrl+C - - - - - - :/viewer/editpaste.svg:/viewer/editpaste.svg - - - Pa&ste as new - - - Paste as new variable - - - Ctrl+V - - - - - - :/viewer/filter_decor.svg:/viewer/filter_decor.svg - - - &Filter mode - - - Filter mode - - - - - - :/viewer/search_decor.svg:/viewer/search_decor.svg - - - &Search mode - - - Search mode - - - - - Copy text for both Name and Value - - - Copy variable's name and value - - - - - - VariableView - QTreeView -
      VariableView.hpp
      -
      -
      - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/VariableModel.cpp ecflow-4.11.1/Viewer/src/VariableModel.cpp --- ecflow-4.9.0/Viewer/src/VariableModel.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableModel.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,777 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VariableModel.hpp" - -#include - -#include "ServerHandler.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VariableModelData.hpp" - -QColor VariableModel::varCol_=QColor(40,41,42); -//QColor VariableModel::genVarCol_=QColor(34,51,136); -QColor VariableModel::genVarCol_=QColor(0,115,48); -QColor VariableModel::shadowCol_=QColor(130,130,130); -QColor VariableModel::blockBgCol_=QColor(122,122,122); -QColor VariableModel::blockFgCol_=QColor(255,255,255); - -#define _UI_VARIABLEMODEL_DEBUG - -//======================================================================= -// -// VariabletModel -// -//======================================================================= - -VariableModel::VariableModel(VariableModelDataHandler* data,QObject *parent) : - QAbstractItemModel(parent), - data_(data) -{ - connect(data_,SIGNAL(reloadBegin()), - this,SLOT(slotReloadBegin())); - - connect(data_,SIGNAL(reloadEnd()), - this,SLOT(slotReloadEnd())); - - connect(data_,SIGNAL(clearBegin(int,int)), - this,SLOT(slotClearBegin(int,int))); - - connect(data_,SIGNAL(clearEnd(int,int)), - this,SLOT(slotClearEnd(int,int))); - - connect(data_,SIGNAL(loadBegin(int,int)), - this,SLOT(slotLoadBegin(int,int))); - - connect(data_,SIGNAL(loadEnd(int,int)), - this,SLOT(slotLoadEnd(int,int))); - - connect(data_,SIGNAL(dataChanged(int)), - this,SLOT(slotDataChanged(int))); - - connect(data_,SIGNAL(rerunFilter()), - this,SIGNAL(rerunFilter())); -} - -bool VariableModel::hasData() const -{ - return (data_->count() > 0); -} - -int VariableModel::columnCount( const QModelIndex& /*parent */ ) const -{ - return 2; -} - -int VariableModel::rowCount( const QModelIndex& parent) const -{ - - //Parent is the root: the item must be a node or a server - if(!parent.isValid()) - { - return data_->count(); - } - //The parent is a server or a node - else if(!isVariable(parent)) - { - int row=parent.row(); - return data_->varNum(row); - } - - //parent is a variable - return 0; -} - -Qt::ItemFlags VariableModel::flags ( const QModelIndex & index) const -{ - Qt::ItemFlags defaultFlags; - - defaultFlags=Qt::ItemIsEnabled | Qt::ItemIsSelectable; - - return defaultFlags; -} - -QVariant VariableModel::data( const QModelIndex& index, int role ) const -{ - if( !index.isValid()) - { - return QVariant(); - } - - //Data lookup can be costly so we immediately return a default value for all - //the cases where the default should be used. - if(role != Qt::DisplayRole && role != Qt::BackgroundRole && role != Qt::ForegroundRole && - role != ReadOnlyRole && role != Qt::ToolTipRole && role != GenVarRole && role != Qt::FontRole && - role != ShadowRole ) - { - return QVariant(); - } - - int row=index.row(); - int level=indexToLevel(index); - - //Server or node - if(level == 1) - { - if(role == ReadOnlyRole) - return QVariant(); - - if(role == Qt:: BackgroundRole) - return blockBgCol_; - - else if(role == Qt::ForegroundRole) - return blockFgCol_; - - - VariableModelData *d=data_->data(row); - if(!d) - { - return QVariant(); - } - - if(index.column() == 0) - { - if(role == Qt::DisplayRole) - { - if(index.row() ==0) - return "defined in " + QString::fromStdString(d->type()) + " " + QString::fromStdString(d->name()); - else - return "inherited from " + QString::fromStdString(d->type()) + " " + QString::fromStdString(d->name()); - } - } - - return QVariant(); - } - - //Variables - else if (level == 2) - { - VariableModelData *d=data_->data(index.parent().row()); - if(!d) - { - return QVariant(); - } - - if(role == Qt::ForegroundRole) - { - if(d->isShadowed(row)) - { - return shadowCol_; - } - - //Generated variable - if(d->isGenVar(row) && index.column() == 0) - return genVarCol_; - else - return varCol_; - } - else if(role == Qt::DisplayRole) - { - if(index.column() == 0) - { - QString s=QString::fromStdString(d->name(row)); - //if(d->isGenVar(row)) - // s+=" (g)"; - return s; - } - else if(index.column() == 1) - { - return QString::fromStdString(d->value(row)); - } - } - else if(role == Qt::ToolTipRole) - { - QString s="User defined variable"; - if(d->isGenVar(row)) - { - s="Generated variable"; - } - if(d->isReadOnly(row)) - s+= " (read only)"; - - if(d->isShadowed(row)) - { - s+=".
      Please note that this variable is shadowed i.e. \ - overwritten in one of the descendants of this node shown in this panel!"; - } - return s; - } - else if(role == ReadOnlyRole) - { - return (d->isReadOnly(row))?true:false; - } - - else if(role == GenVarRole) - { - return (d->isGenVar(row))?true:false; - } - - else if(role == ShadowRole) - { - return (d->isShadowed(row))?true:false; - } - - return QVariant(); - } - - return QVariant(); -} - -bool VariableModel::variable(const QModelIndex& idx, QString& name,QString& value,bool& genVar) const -{ - int block=-1; - int row=-1; - - identify(idx,block,row); - - if(row < 0) - return false; - - if(block >=0 && block < data_->count()) - { - name=QString::fromStdString(data_->data(block)->name(row)); - value=QString::fromStdString(data_->data(block)->value(row)); - genVar=data_->data(block)->isGenVar(row); - return true; - } - - return false; -} - -QVariant VariableModel::headerData( const int section, const Qt::Orientation orient , const int role ) const -{ - if ( orient != Qt::Horizontal || role != Qt::DisplayRole ) - return QAbstractItemModel::headerData( section, orient, role ); - - switch ( section ) - { - case 0: return tr("Name"); - case 1: return tr("Value"); - default: return QVariant(); - } - - return QVariant(); -} - -QModelIndex VariableModel::index( int row, int column, const QModelIndex & parent ) const -{ - if(!hasData() || row < 0 || column < 0) - { - return QModelIndex(); - } - - //When parent is the root this index refers to a node or server - if(!parent.isValid()) - { -#ifdef ECFLOW_QT5 - return createIndex(row,column,quintptr(0)); -#else - return createIndex(row,column,0); -#endif - } - - //We are under one of the nodes - else - { - return createIndex(row,column,(parent.row()+1)*1000); - } - - return QModelIndex(); - -} - -QModelIndex VariableModel::parent(const QModelIndex &child) const -{ - if(!child.isValid()) - return QModelIndex(); - - int level=indexToLevel(child); - if(level == 1) - return QModelIndex(); - else if(level == 2) - { - int id=child.internalId(); - int r=id/1000-1; -#ifdef ECFLOW_QT5 - return createIndex(r,child.column(),quintptr(0)); -#else - return createIndex(r,child.column(),0); -#endif - } - - return QModelIndex(); -} - -//---------------------------------------------- -// -// Server to index mapping and lookup -// -//---------------------------------------------- - -int VariableModel::indexToLevel(const QModelIndex& index) const -{ - if(!index.isValid()) - return 0; - - int id=index.internalId(); - if(id >=0 && id < 1000) - { - return 1; - } - return 2; -} - -VariableModelData* VariableModel::indexToData(const QModelIndex& index) const -{ - int block=-1; - return indexToData(index,block); -} - -VariableModelData* VariableModel::indexToData(const QModelIndex& index,int& block) const -{ - int row; - - identify(index,block,row); - - if(block != -1) - return data_->data(block); - - return NULL; -} - -VInfo_ptr VariableModel::indexToInfo(const QModelIndex& index) const -{ - if(VariableModelData* d=indexToData(index)) - { - //It is a block - QModelIndex p=index.parent(); - - //It is a block - //if(!index.parent().isValid()) <-- this did not work - if(!p.isValid()) - return d->info(); - //it is a variable within a block - else - return d->info(index.row()); - } - return VInfo_ptr(); -} - -QModelIndex VariableModel::infoToIndex(VInfo_ptr info) const -{ - if(!info) - return QModelIndex(); - - int block=-1; - int row=-1; - data_->findVariable(info,block,row); - - if(block != -1) - { - QModelIndex blockIndex=index(block,0); - if(row != -1) - { - return index(row,0,blockIndex); - } - return blockIndex; - } - - return QModelIndex(); -} - -//---------------------------------------------- -// -// Server to index mapping and lookup -// -//---------------------------------------------- - -bool VariableModel::isVariable(const QModelIndex & index) const -{ - return (indexToLevel(index) == 2); -} - -void VariableModel::identify(const QModelIndex& index,int& block,int& row) const -{ - block=-1; - row=-1; - - if(!index.isValid()) - { - return; - } - - int level=indexToLevel(index); - - if(level == 1) - { - block=index.row(); - row=-1; - } - else if(level == 2) - { - block=parent(index).row(); - row=index.row(); - } -} - -void VariableModel::slotReloadBegin() -{ - //Reset the whole model - beginResetModel(); -} - -void VariableModel::slotReloadEnd() -{ - endResetModel(); -} - -void VariableModel::slotClearBegin(int block,int num) -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndex parent=index(block,0); - if(!parent.isValid()) - return; - - int rc=rowCount(parent); - UI_ASSERT(num >= 0," num=" << num); - UI_ASSERT(num == rc," num=" << num << - " rowCount=" << rowCount(parent)); - - if(num > 0) - { - beginRemoveRows(parent,0,num-1); - } -} - -void VariableModel::slotClearEnd(int block,int num) -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UI_FUNCTION_LOG -#endif - - QModelIndex parent=index(block,0); - if(!parent.isValid()) - return; - - UI_ASSERT(num >= 0,"num=" << num); - if(num > 0) - { - endRemoveRows(); - } -} - -void VariableModel::slotLoadBegin(int block,int num) -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndex parent=index(block,0); - if(!parent.isValid()) - return; - - int rc=rowCount(parent); - UI_ASSERT(num >= 0,"num=" << num); - UI_ASSERT(rc==0,"rowCount=" << rowCount(parent)); - - if(num > 0) - { - beginInsertRows(parent,0,num-1); - } -} - -void VariableModel::slotLoadEnd(int block,int num) -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndex parent=index(block,0); - if(!parent.isValid()) - return; - - UI_ASSERT(num >= 0,"num=" << num); - if(num > 0) - { - endInsertRows(); - } -} - -//It must be called after any data change -void VariableModel::slotDataChanged(int block) -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UI_FUNCTION_LOG -#endif - QModelIndex blockIndex0=index(block,0); - QModelIndex blockIndex1=index(block,1); - -#ifdef _UI_VARIABLEMODEL_DEBUG - UiLog().dbg() << " emit dataChanged:" << " " << blockIndex0 << " " << blockIndex1; -#endif - - //This will sort and filter the block - Q_EMIT dataChanged(blockIndex0,blockIndex1); -} - -//======================================================================= -// -// VariableSortModel -// -//======================================================================= - -VariableSortModel::VariableSortModel(VariableModel *varModel,QObject* parent) : - QSortFilterProxyModel(parent), - varModel_(varModel), - showShadowed_(true), - matchMode_(FilterMode), - ignoreDuplicateNames_(false) -{ - QSortFilterProxyModel::setSourceModel(varModel_); - setDynamicSortFilter(true); - - connect(varModel_,SIGNAL(filterChanged()), - this,SLOT(slotFilterChanged())); - - connect(varModel_,SIGNAL(rerunFilter()), - this,SLOT(slotRerunFilter())); -} - -void VariableSortModel::setMatchMode(MatchMode mode) -{ - if(matchMode_ == mode) - return; - - matchMode_=mode; - - matchLst_.clear(); - matchText_.clear(); - - //reload the filter model - invalidate(); -} - -void VariableSortModel::setMatchText(QString txt) -{ - matchText_=txt; - - if(matchMode_ == FilterMode) - { - //reload the filter model - invalidate(); - } -} - -void VariableSortModel::print(const QModelIndex idx) -{ - if(rowCount(idx) > 0) - UiLog().dbg() << "--> " << idx << " " << mapToSource(idx) << " " << data(idx); - else - UiLog().dbg() << idx << " " << mapToSource(idx) << " " << data(idx); - - if(rowCount(idx) > 0) UiLog().dbg() << "children: "; - for(int i=0; i < rowCount(idx); i++) - { - print(index(i,0,idx)); - } -} - -void VariableSortModel::slotFilterChanged() -{ - if(matchMode_ == FilterMode) - { - invalidate(); - } -} - -void VariableSortModel::slotRerunFilter() -{ -#ifdef _UI_VARIABLEMODEL_DEBUG - UiLog().dbg() << "VariableSortModel::slotRerunFilter-->"; -#endif - invalidate(); -} - -void VariableSortModel::slotShowShadowed(bool b) -{ - if(showShadowed_ != b) - { - showShadowed_=b; - invalidate(); - } -} - -bool VariableSortModel::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const -{ - //Node or server. Here we want the nodes and server to stay unsorted. That is the order should stay as - //it is defined in the data handler: the selected node stays on top and its ancestors and the server - //follow each other downwards. This order is reflected in the row index of these items in - //the varModel: the selected node's row is 0, its parent's row is 1, etc. - if(!varModel_->isVariable(sourceLeft)) - { - if(sortOrder() == Qt::AscendingOrder) - return (sourceLeft.row() < sourceRight.row()); - else - return (sourceLeft.row() > sourceRight.row()); - } - //For variables we simply sort according to the string - else - { - //UiLog().dbg() << varModel_->data(sourceLeft,Qt::DisplayRole).toString() << " " << varModel_->data(sourceRight,Qt::DisplayRole).toString(); - return varModel_->data(sourceLeft,Qt::DisplayRole).toString() < varModel_->data(sourceRight,Qt::DisplayRole).toString(); - } - return true; -} - -bool VariableSortModel::filterAcceptsRow(int sourceRow,const QModelIndex& sourceParent) const -{ - if(!sourceParent.isValid()) - return true; - - QModelIndex idx=varModel_->index(sourceRow,0,sourceParent); - - if(!showShadowed_) - { - if(varModel_->data(idx,VariableModel::ShadowRole).toBool()) - return false; - } - - if(matchMode_ != FilterMode || matchText_.simplified().isEmpty()) - return true; - - QModelIndex idx2=varModel_->index(sourceRow,1,sourceParent); - - QString s=varModel_->data(idx,Qt::DisplayRole).toString(); - QString s2=varModel_->data(idx2,Qt::DisplayRole).toString(); - - if(s.contains(matchText_,Qt::CaseInsensitive) || s2.contains(matchText_,Qt::CaseInsensitive)) - { - return true; - } - - return false; -} - -QVariant VariableSortModel::data(const QModelIndex& idx,int role) const -{ - if(role != Qt::UserRole) - { - return QSortFilterProxyModel::data(idx,role); - } - - //We highlight the matching items (the entire row). - if(matchMode_ == SearchMode && matchLst_.count() >0) - { - int col2=(idx.column()==0)?1:0; - QModelIndex idx2=index(idx.row(),col2,idx.parent()); - - if(matchLst_.contains(idx) || matchLst_.contains(idx2)) - return QColor(169,210,176); - } - - return QSortFilterProxyModel::data(idx,role); -} - - -QModelIndexList VariableSortModel::match(const QModelIndex& start,int role,const QVariant& value,int hits,Qt::MatchFlags flags) const -{ - if(matchMode_ != SearchMode) - return QModelIndexList(); - - QModelIndex root; - matchText_=value.toString(); - - matchLst_.clear(); - - if(matchText_.simplified().isEmpty()) - return matchLst_; - - for(int i=0; i < rowCount(); i++) - { - QModelIndex idx=index(i,0); - for(int row=0; row < rowCount(idx);row++) - { - //Name column - QModelIndex colIdx=index(row,0,idx); - QString s=data(colIdx,Qt::DisplayRole).toString(); - - if(s.contains(matchText_,Qt::CaseInsensitive)) - { - matchLst_ << colIdx; - continue; - } - - //Value columns - QModelIndex colIdx1=index(row,1,idx); - s=data(colIdx1,Qt::DisplayRole).toString(); - if(s.contains(matchText_,Qt::CaseInsensitive)) - { - matchLst_ << colIdx; - } - } - } - - return matchLst_; -} - -#if 0 -void VariableSortModel::test() -{ - UI_FUNCTION_LOG - QModelIndex idx; - test(idx); - testSource(idx); -} - -void VariableSortModel::test(const QModelIndex& p) -{ - int num=rowCount(p); - for(int i=0; i < num; i++) - { - QModelIndex idx=index(i,0,p); - QModelIndex sIdx=mapToSource(idx); - if(!sIdx.isValid()) - { - UiLog().dbg() << " idx=" << idx; - Q_ASSERT(sIdx.isValid()); - } - //UiLog().dbg() << idx.data().toString() << " " << sIdx.data().toString(); - - if(idx.data().toString() != sIdx.data().toString()) - { - UI_ASSERT(0,"filter=" << idx.data().toString() << - " source=" << sIdx.data().toString()); - } - - test(idx); - } -} - -void VariableSortModel::testSource(const QModelIndex& p) -{ - int num=varModel_->rowCount(p); - for(int i=0; i < num; i++) - { - QModelIndex sIdx=varModel_->index(i,0,p); - QModelIndex idx=mapFromSource(sIdx); - if(!idx.isValid()) - { - UiLog().dbg() << " idx=" << idx; - Q_ASSERT(sIdx.isValid()); - } - if(idx.data().toString() != sIdx.data().toString()) - { - UI_ASSERT(0,"filter=" << idx.data().toString() << - " source=" << sIdx.data().toString()); - } - testSource(idx); - } -} -#endif diff -Nru ecflow-4.9.0/Viewer/src/VariableModelData.cpp ecflow-4.11.1/Viewer/src/VariableModelData.cpp --- ecflow-4.9.0/Viewer/src/VariableModelData.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableModelData.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,982 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VariableModelData.hpp" - -#include "CommandHandler.hpp" -#include "UserMessage.hpp" -#include "UiLog.hpp" -#include "UIDebug.hpp" -#include "VariableModelDataObserver.hpp" -#include "VAttribute.hpp" -#include "VGenVarAttr.hpp" -#include "VItemPathParser.hpp" -#include "VNode.hpp" -#include "VNState.hpp" - -#include - -static std::string defaultStr(""); - -#define _UI_VARIABLEMODELDATA_DEBUG - -//========================================== -// -// VariableModelData -// -//========================================== - -VariableModelData::VariableModelData(VInfo_ptr info) : - info_(info) -{ - reload(); -} - -VariableModelData::~VariableModelData() -{ -} - -void VariableModelData::clear() -{ - vars_.clear(); - genVars_.clear(); -} - -void VariableModelData::reload() -{ - clear(); - - if(info_ && info_->node()) - { - info_->node()->variables(vars_); - info_->node()->genVariables(genVars_); - removeDuplicates(vars_,genVars_); - } -} - -//When this function called duplicates must have already been removed!! -void VariableModelData::reset(const std::vector& vars,const std::vector& genVars) -{ - clear(); - - if(info_ && info_->node()) - { - vars_=vars; - genVars_=genVars; - } -} - -//Remove the generated variables that have the same name as a user variable. User -//variables take precedence over generated variables. -void VariableModelData::removeDuplicates(const std::vector& vars,std::vector& genVars) -{ - std::vector gvOri=genVars; - genVars.clear(); - - for(std::vector::const_iterator it=gvOri.begin(); it != gvOri.end(); ++it) - { - bool hasIt=false; - for(std::vector::const_iterator itV=vars.begin(); itV != vars.end(); ++itV) - { - if((*it).name() == (*itV).name()) - { - hasIt=true; - break; - } - } - if(!hasIt) - genVars.push_back(*it); - } -} - -std::string VariableModelData::fullPath() -{ - if(info_ && info_->node()) - return info_->path(); - - return std::string(); -} - -std::string VariableModelData::name() -{ - return info_->name(); -} - -std::string VariableModelData::type() -{ - if(info_) - { - if(info_->isServer()) - return "server"; - else if(info_->node()) - return info_->node()->nodeType(); - } - - return std::string(); -} - -VNode* VariableModelData::node() const -{ - if(info_ && info_->isNode()) - return info_->node(); - - return NULL; -} - -VInfo_ptr VariableModelData::info(int index) const -{ - if(info_) - { - if(index < 0 || index >= varNum()) - return VInfo_ptr(); - - std::string p=info_->storedPath(); - if(!isGenVar(index)) - { - p=VItemPathParser::encodeAttribute(p,vars_[index].name(),"var"); - } - else - { - p=VItemPathParser::encodeAttribute(p,genVars_[index-vars_.size()].name(),"genvar"); - } - - return VInfo::createFromPath(p); - } - - return VInfo_ptr(); -} - -const std::string& VariableModelData::name(int index) const -{ - if(index < 0 || index >= varNum()) - return defaultStr; - - if(!isGenVar(index)) - { - return vars_.at(index).name(); - } - else - { - return genVars_.at(index-vars_.size()).name(); - } - - return defaultStr; -} - -const std::string& VariableModelData::value(int index) const -{ - if(index < 0 || index >= varNum()) - return defaultStr; - - if(!isGenVar(index)) - { - return vars_.at(index).theValue(); - } - else - { - return genVars_.at(index-vars_.size()).theValue(); - } - - return defaultStr; -} - -const std::string& VariableModelData::value(const std::string n,bool& hasIt) const -{ - hasIt=false; - if(n.empty()) - return defaultStr; - - for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) - { - if((*it).name() == n) - { - hasIt=true; - return (*it).theValue(); - } - } - for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) - { - if((*it).name() == n) - { - hasIt=true; - return (*it).theValue(); - } - } - - return defaultStr; -} - -bool VariableModelData::hasName(const std::string& n) const -{ - for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) - { - if((*it).name() == n) - { - return true; - } - } - - for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) - { - if((*it).name() == n) - { - return true; - } - } - - return false; - -} - -int VariableModelData::indexOf(const std::string& varName,bool genVar) const -{ - int idx=-1; - for(std::vector::const_iterator it=vars_.begin(); it != vars_.end(); ++it) - { - idx++; - if(!genVar && (*it).name() == varName) - { - return idx; - } - } - - if(!genVar) - return -1; - - for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) - { - idx++; - if((*it).name() == varName) - { - return idx; - } - } - - return -1; -} - -#if 0 -void VariableModelData::buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& name,const std::string& value) -{ - cmd.push_back("ecflow_client"); - cmd.push_back("--alter"); - cmd.push_back(action); - cmd.push_back(type); - - if(!name.empty()) - { - cmd.push_back(name); - cmd.push_back(value); - } - - cmd.push_back(""); - -} -#endif - -void VariableModelData::setValue(int index,const std::string& val) -{ - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"change","variable",name(index),val); - - CommandHandler::run(info_,cmd); -} - -void VariableModelData::alter(const std::string& name,const std::string& val) -{ - std::string mode="add"; - - //Existing name - if(hasName(name)) - { - //Generated variables cannot be changed. We instead add user variable with the same - //name. It will shadow the original gen variable. - if(isGenVar(name)) - { - mode="add"; - } - else - { - mode="change"; - } - } - - std::vector cmd; - VAttribute::buildAlterCommand(cmd,mode,"variable",name,val); - CommandHandler::run(info_,cmd); -} - - -void VariableModelData::add(const std::string& name,const std::string& val) -{ - std::vector cmd; - VAttribute::buildAlterCommand(cmd,(hasName(name))?"change":"add","variable",name,val); - CommandHandler::run(info_,cmd); -} - -void VariableModelData::remove(const std::string& varName) -{ - std::vector cmd; - VAttribute::buildAlterCommand(cmd,"delete","variable",varName,""); - CommandHandler::run(info_,cmd); -} - -bool VariableModelData::isGenVar(int index) const -{ - return (index >= static_cast(vars_.size())); -} - -bool VariableModelData::isGenVar(const std::string& n) const -{ - for(std::vector::const_iterator it=genVars_.begin(); it != genVars_.end(); ++it) - { - if((*it).name() == n) - { - return true; - } - } - return false; -} - -bool VariableModelData::isReadOnly(int index) const -{ - return isReadOnly(name(index)); -} - -bool VariableModelData::isReadOnly(const std::string& varName) const -{ - return VGenVarAttr::isReadOnly(varName); -} - -bool VariableModelData::isShadowed(int index) const -{ - return (shadowed_.find(name(index)) != shadowed_.end()); -} - -int VariableModelData::varNum() const -{ - return vars_.size() + genVars_.size(); -} - -void VariableModelData::latestVars(std::vector& v,std::vector& vg) -{ - if(info_ && info_->node()) - { - info_->node()->variables(v); - info_->node()->genVariables(vg); - removeDuplicates(v,vg); - } -} - -bool VariableModelData::updateShadowed(std::set& names) -{ - std::set ori=shadowed_; - shadowed_.clear(); - bool changed=false; - -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " ori shadowed:"; - for(std::set::const_iterator it=ori.begin(); it != ori.end(); ++it) - { - UiLog().dbg() << " " << *it; - } -#endif - - for(std::set::const_iterator it = names.begin(); it != names.end(); ++it) - { - if(hasName(*it)) - { - shadowed_.insert(*it); - if(ori.find(*it) == ori.end()) - changed=true; - } - } - - for(std::size_t i=0; i < vars_.size(); i++) - { - names.insert(vars_[i].name()); - } - for(std::size_t i=0; i < genVars_.size(); i++) - { - names.insert(genVars_[i].name()); - } - -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " shadowed:"; - for(std::set::const_iterator it=shadowed_.begin(); it != shadowed_.end(); ++it) - { - UiLog().dbg() << " " << *it; - } - - UiLog().dbg() << " changed: " << changed; -#endif - - return changed; -} - -bool VariableModelData::checkUpdateNames(const std::vector& v,const std::vector& vg) -{ - for(unsigned int i=0; i < vars_.size(); i++) - { - if(vars_[i].name() != v[i].name()) - { - return true; - } - } - - for(unsigned int i=0; i < genVars_.size(); i++) - { - if(genVars_[i].name() != vg[i].name()) - { - return true; - } - } - - return false; -} - -//Check if the total number of variables will change. It does not update the local data! -int VariableModelData::checkUpdateDiff(std::vector& v,std::vector& vg) -{ - if(info_ && info_->node() && v.empty() && vg.empty()) - { - //get the current set of variables from the node/server. This might be different - //to the ones we store. - latestVars(v,vg); - } - - //Return the change in the total size of variables - return v.size()+vg.size() -(vars_.size() + genVars_.size()); -} - - -//Check if any of the names or values has changed. We suppose that the number of current and new -//variables are the same but some of their names or values have been changed. -bool VariableModelData::update(const std::vector& v,const std::vector& vg) -{ -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UI_FUNCTION_LOG -#endif - -#if 0 - if(info_ && info_->node() && v.empty() && vg.empty()) - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " call latestVars"; -#endif - latestVars(v,vg); - } -#endif - -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " new list of variables:"; - for(std::size_t i=0; i < v.size(); i++) - UiLog().dbg() << " " << v[i].name() << "=" << v[i].theValue(); - UiLog().dbg() << " new list of generated variables:"; - for(std::size_t i=0; i < vg.size(); i++) - UiLog().dbg() << " " << vg[i].name() << "=" << vg[i].theValue(); -#endif - - //We must have the same number of variables - UI_ASSERT(v.size() + vg.size() == vars_.size() + genVars_.size(), - "v.size()=" << v.size() << " vg.size()=" << vg.size() << - " vars_.size()=" << vars_.size() << " genVars_.size()" << genVars_.size()); - - bool changed=false; - if(v.size() != vars_.size() || vg.size() != genVars_.size()) - { - changed=true; -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " variables size changed! var: " << vars_.size() << - " -> " << v.size() << "gen var: " << - genVars_.size() << " -> " << vg.size(); -#endif - } - else - { - UI_ASSERT(v.size() == vars_.size(), - "v.size()=" << v.size() << " vars_.size()=" << vars_.size()); - - UI_ASSERT(vg.size() == genVars_.size(), - "vg.size()=" << vg.size() << " genVars_.size()=" << genVars_.size()); - - for(std::size_t i=0; i < vars_.size(); i++) - { - if(vars_[i].name() != v[i].name() || vars_[i].theValue() != v[i].theValue()) - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " variable changed! name: " << vars_[i].name() << " -> " << - v[i].name() << " value: " << vars_[i].theValue() << " -> " << v[i].theValue(); -#endif - changed=true; - break; - } - } - - if(changed == false) - { - for(std::size_t i=0; i < genVars_.size(); i++) - { - if(genVars_[i].name() != vg[i].name() || genVars_[i].theValue() != vg[i].theValue()) - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " generated variable changed! name: " << genVars_[i].name() << " -> " << - vg[i].name() << " value: " << genVars_[i].theValue() << " -> " << vg[i].theValue(); -#endif - changed=true; - break; - } - } - } - } - - if(changed) - { - vars_=v; - genVars_=vg; -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " updated vars and genvars"; -#endif - } - - return changed; -} - -//========================================== -// -// VariableModelDataHandler -// -//========================================== - -VariableModelDataHandler::VariableModelDataHandler() : server_(0) -{ -} - -VariableModelDataHandler::~VariableModelDataHandler() -{ - clear(); -} - -void VariableModelDataHandler::reload(VInfo_ptr info) -{ - //Notifies the model that a change will happen - Q_EMIT reloadBegin(); - - clear(false); - - if(info && info->node()) - { - server_=info->server(); - - std::vector nodes=info->node()->ancestors(VNode::ChildToParentSort); - - for(std::vector::iterator it=nodes.begin(); it != nodes.end(); ++it) - { - VNode* n=*it; - - if(n->isServer()) - { - VInfo_ptr ptr=VInfoServer::create(n->server()); - data_.push_back(new VariableModelData(ptr)); - } - else - { - VInfo_ptr ptr=VInfoNode::create(n); - data_.push_back(new VariableModelData(ptr)); - } - } - - updateShadowed(); - } - - Q_EMIT reloadEnd(); -} - -#if 0 -void VariableModelDataHandler::reload() -{ - //Notifies the model that a change will happen - Q_EMIT reloadBegin(); - - for(std::vector::iterator it=data_.begin(); it != data_.end(); ++it) - { - (*it)->reload(); - } - - updateShadowed(); - - Q_EMIT reloadEnd(); -} -#endif - -bool VariableModelDataHandler::updateShadowed() -{ -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UI_FUNCTION_LOG -#endif - - bool shadowChanged=false; - - names_.clear(); - - if(data_.size()== 0) - return shadowChanged; - - //There are no shadowed vars in the first node - std::size_t num=data_[0]->varNum(); - for(std::size_t i=0; i < num; i++) - { - names_.insert(data_[0]->name(i)); - } - - std::size_t dataNum=data_.size(); - for(std::size_t i=1; i < dataNum; i++) - { - if(data_[i]->updateShadowed(names_)) - shadowChanged=true; - } - -//#ifdef _UI_VARIABLEMODELDATA_DEBUG -#if 0 - UiLog().dbg() << " names:"; - for(std::set::const_iterator it=names_.begin(); it != names_.end(); ++it) - { - UiLog().dbg() << " " + *it; - } -#endif - - return shadowChanged; -} - -void VariableModelDataHandler::clear(bool emitSignal) -{ - if(emitSignal) - Q_EMIT reloadBegin(); - - for(std::vector::iterator it=data_.begin(); it != data_.end(); ++it) - { - delete *it; - } - - server_=0; - data_.clear(); - names_.clear(); - - broadcastClear(); - - if(emitSignal) - Q_EMIT reloadEnd(); -} - -int VariableModelDataHandler::varNum(int index) const -{ - if(index >=0 && index < static_cast(data_.size())) - return data_.at(index)->varNum(); - - return -1; -} - -VariableModelData* VariableModelDataHandler::data(int index) const -{ - if(index >=0 && index < static_cast(data_.size())) - return data_.at(index); - - return 0; -} - -//It is called when a node changed. -bool VariableModelDataHandler::nodeChanged(const VNode* node, const std::vector& aspect) -{ -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UI_FUNCTION_LOG -#endif - int dataIndex=-1; - for(std::size_t i=0; i < data_.size(); i++) - { - if(data_[i]->node() == node) - { - dataIndex=i; - break; - } - } - -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " dataIndex=" << dataIndex; -#endif - Q_ASSERT(dataIndex != -1); - - //Update the given block - bool retVal=updateVariables(dataIndex); - - Q_ASSERT(data_[dataIndex]->node()); - - //Always update the suite as well!! - if(!data_[dataIndex]->node()->isSuite()) - { - for(std::size_t i=dataIndex+1; i < data_.size(); i++) - { - if(data_[i]->node()->isSuite()) - { - if(updateVariables(i)) - retVal=true; - - break; - } - } - } - - if(retVal) - broadcastUpdate(); - - return retVal; -} - -//It is called when the server defs was changed -bool VariableModelDataHandler::defsChanged(const std::vector& aspect) -{ -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UI_FUNCTION_LOG -#endif - - if(data_.size() == 0) - { - return false; - } - - int dataIndex=data_.size()-1; - Q_ASSERT(dataIndex >=0 && dataIndex < static_cast(data_.size())); - VariableModelData* d=data_.at(data_.size()-1); - Q_ASSERT(d); - Q_ASSERT(d->type() == "server"); - - bool retVal=updateVariables(dataIndex); - - if(retVal) - broadcastUpdate(); - - return retVal; -} - -bool VariableModelDataHandler::updateVariables(int dataIndex) -{ -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UI_FUNCTION_LOG - UiLog().dbg() << " dataIndex=" << dataIndex; -#endif - - bool retVal=false; - - //There is no notification about generated variables. Basically they can - //change at any update!! So we have to check all the variables at every update!! - std::vector v; - std::vector vg; - - //Get the current set of variables and check if the total number of variables - //has changed. At this point v and vg do not contain any duplicates (within the - //same block the user variables take precedence over the generated variables) - int cntDiff=data_[dataIndex]->checkUpdateDiff(v,vg); - - //If the number of the variables is not the same that we store - //we reset the given block in the model - if(cntDiff != 0) - { - const int numOld=data_[dataIndex]->varNum(); //the current num in the model - const int numNew=v.size()+vg.size(); //the new num - -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " cntDiff=" << cntDiff << " numOld=" << numOld << - " numNew=" << numNew; -#endif - - //Clear the block's contents in the model - Q_EMIT clearBegin(dataIndex,numOld); - data_[dataIndex]->clear(); - Q_EMIT clearEnd(dataIndex,numOld); - - //Load the new data for the block in the model - Q_EMIT loadBegin(dataIndex,numNew); - data_[dataIndex]->reset(v,vg); - Q_EMIT loadEnd(dataIndex,numNew); - - Q_ASSERT(data_[dataIndex]->varNum() == numNew); - - //At this point the block is filtered and sorted!!!! - - //Check if the shadowed list of variables changed - if(updateShadowed()) - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " emit rerunFilter"; -#endif - //We need to rerun the filter!! - Q_EMIT rerunFilter(); - } -#if 0 - else - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " emit dataChanged"; -#endif - //Update the data item in the model - //Q_EMIT dataChanged(dataIndex); - } -#endif - - retVal=true; - } - //Check if some variables' name or value changed - else - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " cntDiff=" << cntDiff; -#endif - Q_ASSERT(cntDiff==0); - //At this point we must have the same number of vars - const int numNew=v.size()+vg.size(); - Q_ASSERT(data_[dataIndex]->varNum() == numNew); - - //Find out if any names changed - bool nameChanged=data_[dataIndex]->checkUpdateNames(v,vg); - - //Update the names/values - if(data_[dataIndex]->update(v,vg)) - { -#ifdef _UI_VARIABLEMODELDATA_DEBUG - UiLog().dbg() << " Variable name or value changed"; -#endif - //At this point the stored variables are already updated - if(nameChanged) - { - if(updateShadowed()) - { - Q_EMIT rerunFilter(); - } - else - { - //Update the data item in the model - Q_EMIT dataChanged(dataIndex); - } - } - else - { - //Update the data item in the model - Q_EMIT dataChanged(dataIndex); - } - } - retVal=true; - } - - return retVal; -} - -const std::string& VariableModelDataHandler::value(const std::string& node,const std::string& name, bool& hasIt) const -{ - hasIt=false; - for(unsigned int i=0; i < data_.size(); i++) - { - if(data_.at(i)->name() == node) - { - return data_.at(i)->value(name,hasIt); - } - } - - return defaultStr; -} - -void VariableModelDataHandler::findVariable(const std::string& name,const std::string& nodePath, - bool genVar,int& block,int& row) const -{ - block=-1; - row=-1; - for(size_t i=0; i < data_.size(); i++) - { - if(data_[i]->fullPath() == nodePath) - { - block=i; - row=data_[i]->indexOf(name,genVar); - return; - } - } -} - -void VariableModelDataHandler::findVariable(VInfo_ptr info,int& block,int& row) const -{ - block=-1; - row=-1; - - findBlock(info,block); - if(block!= -1 && info && info->isAttribute()) - { - if(VAttribute *a=info->attribute()) - { - std::string name=a->strName(); - std::string tName=a->typeName(); - if(!name.empty() && - (tName=="var" || tName == "genvar") ) - { - row=data_[block]->indexOf(name,(tName == "genvar")); - } - } - } -} - -void VariableModelDataHandler::findBlock(VInfo_ptr info,int& block) const -{ - block=-1; - - if(!info) - return; - - std::string p=info->nodePath(); - if(!p.empty()) - { - int n=count(); - for(int i=0; i < n; i++) - { - if(data_[i]->info_ && data_[i]->info_->nodePath() == p) - { - block=i; - return; - } - } - } -} - -void VariableModelDataHandler::addObserver(VariableModelDataObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - observers_.push_back(o); -} - -void VariableModelDataHandler::removeObserver(VariableModelDataObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - observers_.erase(it); -} - -void VariableModelDataHandler::broadcastClear() -{ - std::vector obsCopy=observers_; - for(std::vector::const_iterator it=obsCopy.begin(); it != obsCopy.end(); ++it) - { - (*it)->notifyCleared(this); - } -} - -void VariableModelDataHandler::broadcastUpdate() -{ - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - { - (*it)->notifyUpdated(this); - } -} diff -Nru ecflow-4.9.0/Viewer/src/VariableModelData.hpp ecflow-4.11.1/Viewer/src/VariableModelData.hpp --- ecflow-4.9.0/Viewer/src/VariableModelData.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableModelData.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,134 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VARIABLEMODELDATA_H -#define VARIABLEMODELDATA_H - -#include -#include - -#include -#include - -#include "NodeObserver.hpp" -#include "VInfo.hpp" - -class Node; -class ServerHandler; -class VariableModelDataHandler; -class VariableModelDataObserver; - -class VariableModelData -{ - friend class VariableModelDataHandler; - -public: - explicit VariableModelData(VInfo_ptr info); - virtual ~VariableModelData(); - - std::string fullPath(); - std::string name(); - std::string type(); - const std::string& name(int index) const; - const std::string& value(int index) const; - const std::string& value(const std::string name,bool&) const; - VInfo_ptr info() const {return info_;} - VInfo_ptr info(int index) const; - int indexOf(const std::string& varName,bool genVar) const; - bool isGenVar(const std::string& varName) const; - bool isGenVar(int index) const; - bool isReadOnly(int index) const; - bool isReadOnly(const std::string& varName) const; - bool isShadowed(int index) const; - int varNum() const; - bool hasName(const std::string& n) const; - VNode* node() const; - -#if 0 - void buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& name,const std::string& value); -#endif - void clear(); - void setValue(int index,const std::string& val); - void alter(const std::string& name,const std::string& val); - void add(const std::string& name,const std::string& val); - void remove(const std::string& val); - - //const std::vector& vars() const {return vars_;} - //const std::vector& genVars() const {return genVars_;} - -protected: - void reload(); - void removeDuplicates(const std::vector& vars,std::vector& genVars); - void latestVars(std::vector& v,std::vector& gv); - int checkUpdateDiff(std::vector& v,std::vector& gv); - bool checkUpdateNames(const std::vector& v,const std::vector& vg); - void reset(const std::vector& v,const std::vector& gv); - bool update(const std::vector& v,const std::vector& gv); - bool updateShadowed(std::set& names); - - std::vector vars_; - std::vector genVars_; - std::set shadowed_; - VInfo_ptr info_; -}; - -class VariableModelDataHandler : public QObject -{ -Q_OBJECT - -public: - VariableModelDataHandler(); - ~VariableModelDataHandler(); - - void reload(VInfo_ptr info); - void clear(bool emitSignal=true); - int count() const {return static_cast(data_.size());} - int varNum(int index) const; - VariableModelData* data(int index) const; - void findVariable(const std::string& name,const std::string& nodePath, - bool genVar,int& block,int& row) const; - - void findVariable(VInfo_ptr info,int& block,int& row) const; - void findBlock(VInfo_ptr info,int& block) const; - - bool nodeChanged(const VNode* node, const std::vector&); - bool defsChanged(const std::vector&); - const std::string& value(const std::string& node,const std::string& name,bool&) const; - void addObserver(VariableModelDataObserver*); - void removeObserver(VariableModelDataObserver*); - - -Q_SIGNALS: - void reloadBegin(); - void reloadEnd(); - void clearBegin(int,int); - void clearEnd(int,int); - void loadBegin(int,int); - void loadEnd(int,int); - void addRemoveBegin(int,int); - void addRemoveEnd(int); - void dataChanged(int); - void rerunFilter(); - -protected: - //void reload(); - bool updateVariables(int); - bool updateShadowed(); - void broadcastClear(); - void broadcastUpdate(); - - std::vector data_; - ServerHandler* server_; - std::set names_; - std::vector observers_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VariableModelDataObserver.hpp ecflow-4.11.1/Viewer/src/VariableModelDataObserver.hpp --- ecflow-4.9.0/Viewer/src/VariableModelDataObserver.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableModelDataObserver.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VARIABLEMODELDATAOBSERVER_HPP -#define VARIABLEMODELDATAOBSERVER_HPP - -class VariableModelDataHandler; - -class VariableModelDataObserver -{ -public: - VariableModelDataObserver() {} - virtual void notifyCleared(VariableModelDataHandler*)=0; - virtual void notifyUpdated(VariableModelDataHandler*)=0; -}; - -#endif // VARIABLEMODELDATAOBSERVER_HPP diff -Nru ecflow-4.9.0/Viewer/src/VariableModel.hpp ecflow-4.11.1/Viewer/src/VariableModel.hpp --- ecflow-4.9.0/Viewer/src/VariableModel.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableModel.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -#ifndef VARIABLEMODEL_H -#define VARIABLEMODEL_H - -#include -#include - -#include - -#include "NodeObserver.hpp" -#include "VInfo.hpp" - -class Node; -class VariableModelData; -class VariableModelDataHandler; -class VariableSortModel; - -class VariableModel : public QAbstractItemModel -{ -Q_OBJECT - -friend class VariableSortModel; - -public: - VariableModel(VariableModelDataHandler* data,QObject *parent=0); - - enum CustomItemRole {ReadOnlyRole = Qt::UserRole+1,GenVarRole = Qt::UserRole+2,ShadowRole = Qt::UserRole+3}; - - int columnCount (const QModelIndex& parent = QModelIndex() ) const; - int rowCount (const QModelIndex& parent = QModelIndex() ) const; - - Qt::ItemFlags flags ( const QModelIndex & index) const; - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - QVariant headerData(int,Qt::Orientation,int role = Qt::DisplayRole ) const; - - QModelIndex index (int, int, const QModelIndex& parent = QModelIndex() ) const; - QModelIndex parent (const QModelIndex & ) const; - - bool variable(const QModelIndex& index, QString& name,QString& value,bool& genVar) const; - - VariableModelData* indexToData(const QModelIndex& index) const; - VariableModelData* indexToData(const QModelIndex& index,int& block) const; - VInfo_ptr indexToInfo(const QModelIndex& index) const; - QModelIndex infoToIndex(VInfo_ptr info) const; - - bool isVariable(const QModelIndex & index) const; - -public Q_SLOTS: - void slotReloadBegin(); - void slotReloadEnd(); - void slotClearBegin(int block,int num); - void slotClearEnd(int block,int num); - void slotLoadBegin(int block,int num); - void slotLoadEnd(int block,int num); - void slotDataChanged(int); - -Q_SIGNALS: - void filterChanged(); - void rerunFilter(); - -protected: - bool hasData() const; - - int indexToLevel(const QModelIndex&) const; - void identify(const QModelIndex& index,int& parent,int& row) const; - - VariableModelDataHandler* data_; - static QColor varCol_; - static QColor genVarCol_; - static QColor shadowCol_; - static QColor blockBgCol_; - static QColor blockFgCol_; -}; - - -//Filter and sorts the variables - -class VariableSortModel : public QSortFilterProxyModel -{ - Q_OBJECT - -public: - enum MatchMode {FilterMode,SearchMode}; - - VariableSortModel(VariableModel*,QObject *parent=0); - ~VariableSortModel() {} - - MatchMode matchMode() const {return matchMode_;} - void setMatchMode(MatchMode mode); - void setMatchText(QString text); - - bool lessThan(const QModelIndex &left, const QModelIndex &right) const; - bool filterAcceptsRow(int,const QModelIndex &) const; - - //From QSortFilterProxyModel: - //we set the source model in the constructor. So this function should not do anything. - void setSourceModel(QAbstractItemModel*) {} - QVariant data (const QModelIndex& , int role = Qt::DisplayRole ) const; - - QModelIndexList match(const QModelIndex& start,int role,const QVariant& value,int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags( Qt::MatchStartsWith | Qt::MatchWrap )) const; - -#if 0 - void test(); - void test(const QModelIndex& p); - void testSource(const QModelIndex& p); -#endif - -public Q_SLOTS: - void slotShowShadowed(bool); - -protected Q_SLOTS: - void slotFilterChanged(); - void slotRerunFilter(); - -protected: - void match(QString text); - void print(const QModelIndex idx); - - VariableModel* varModel_; - bool showShadowed_; - - MatchMode matchMode_; - mutable QString matchText_; - mutable QModelIndexList matchLst_; - - QMap nameCnt_; - bool ignoreDuplicateNames_; //Ignore duplicate names across ancestors -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VariablePropDialog.ui ecflow-4.11.1/Viewer/src/VariablePropDialog.ui --- ecflow-4.9.0/Viewer/src/VariablePropDialog.ui 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariablePropDialog.ui 1970-01-01 00:00:00.000000000 +0000 @@ -1,138 +0,0 @@ - - - VariablePropDialog - - - - 0 - 0 - 625 - 378 - - - - Edit variable - - - true - - - - 4 - - - 6 - - - - - - - - - - - - 0 - - - 0 - - - - - &Name: - - - nameEdit_ - - - - - - - - - - - 0 - 1 - - - - - - - - &Value: - - - valueEdit_ - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Save - - - - - - - - EditorInfoLabel - QWidget -
      EditorInfoLabel.hpp
      - 1 -
      - - MessageLabel - QWidget -
      MessageLabel.hpp
      - 1 -
      -
      - - - - buttonBox_ - accepted() - VariablePropDialog - accept() - - - 257 - 218 - - - 157 - 227 - - - - - buttonBox_ - rejected() - VariablePropDialog - reject() - - - 274 - 218 - - - 283 - 227 - - - - -
      diff -Nru ecflow-4.9.0/Viewer/src/VariableSearchLine.cpp ecflow-4.11.1/Viewer/src/VariableSearchLine.cpp --- ecflow-4.9.0/Viewer/src/VariableSearchLine.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableSearchLine.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,146 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include - -#include -#include -#include - -#include "VariableSearchLine.hpp" - -VariableSearchLine::VariableSearchLine(QWidget *parent) : - AbstractSearchLine(parent), - view_(0), - currentResultItem_(-1) -{ - label_->hide(); - closeTb_->hide(); -} - -VariableSearchLine::~VariableSearchLine() -{ - -} - -//This should only be called once!! -void VariableSearchLine::setView(QTreeView* view) -{ - //We should disconnect from the previous view ... - view_=view; - - //We detect when the sorting changes in the view - connect(view_->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), - this,SLOT(slotSortHappened(int,Qt::SortOrder))); - - //At this point the model has to be set on the view!!! - assert(view_->model() != 0); - - //We detect when the model is reset - connect(view_->model(),SIGNAL(modelReset()), - this,SLOT(slotUpdate())); - - //We should detect when the model changes!!!! - connect(view_->model(),SIGNAL(dataChanged(QModelIndex,QModelIndex)), - this,SLOT(slotUpdate(QModelIndex,QModelIndex))); -} - -void VariableSearchLine::slotFind(QString txt) -{ - if(!view_) - return; - - if(txt.simplified().isEmpty() && resultItems_.isEmpty()) - { - return; - } - - QModelIndex current=view_->currentIndex(); - - //The (sort) model redoes the match. - resultItems_=view_->model()->match(current,Qt::DisplayRole,txt,-1, - Qt::MatchStartsWith | Qt::MatchRecursive | Qt::MatchWrap); - - //The view needs to be rerendered! - view_->dataChanged(QModelIndex(),QModelIndex()); - - if(resultItems_.count() > 0) - { - selectIndex(resultItems_[0]); - currentResultItem_=0; - updateButtons(true); - } - else - { - currentResultItem_=0; - updateButtons(false); - } - -} - -void VariableSearchLine::slotFindNext() -{ - if(!view_) - return; - - - if(status_==true && resultItems_.count() > 0) - { - currentResultItem_++; - if(currentResultItem_ >= resultItems_.count()) - { - currentResultItem_=0; - } - selectIndex(resultItems_[currentResultItem_]); - } -} - -void VariableSearchLine::slotFindPrev() -{ - if(!view_) - return; - - if(status_==true && resultItems_.count() > 0) - { - currentResultItem_--; - if(currentResultItem_ <0) - { - currentResultItem_=resultItems_.count()-1; - } - selectIndex(resultItems_[currentResultItem_]); - } -} - -void VariableSearchLine::selectIndex(const QModelIndex& index) -{ - if(!view_) - return; - - view_->setCurrentIndex(index); - //emit indexSelected(index); -} - -//Called when sorting changed in the view -void VariableSearchLine::slotSortHappened(int,Qt::SortOrder) -{ - slotUpdate(); -} - -void VariableSearchLine::slotUpdate() -{ - slotFind(searchLine_->text()); -} - -void VariableSearchLine::slotUpdate(const QModelIndex&,const QModelIndex&) -{ - slotUpdate(); -} - - - diff -Nru ecflow-4.9.0/Viewer/src/VariableSearchLine.hpp ecflow-4.11.1/Viewer/src/VariableSearchLine.hpp --- ecflow-4.9.0/Viewer/src/VariableSearchLine.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableSearchLine.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VARIABLESEARCHLINE_HPP_ -#define VARIABLESEARCHLINE_HPP_ - -#include "AbstractSearchLine.hpp" - -#include - -class QTreeView; - -class VariableSearchLine : public AbstractSearchLine -{ - Q_OBJECT - -public: - explicit VariableSearchLine(QWidget *parent); - ~VariableSearchLine(); - void setView(QTreeView* view); - -public Q_SLOTS: - void slotFind(QString); - void slotFindNext(); - void slotFindPrev(); - void slotFindNext(bool) { slotFindNext();} - void slotFindPrev(bool) {slotFindPrev();} - void slotSortHappened(int,Qt::SortOrder); - void slotUpdate(); - void slotUpdate(const QModelIndex&,const QModelIndex&); - -protected: - void selectIndex(const QModelIndex& index); - void clearRequested() {} - - QTreeView* view_; - QModelIndexList resultItems_; - int currentResultItem_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VariableView.cpp ecflow-4.11.1/Viewer/src/VariableView.cpp --- ecflow-4.9.0/Viewer/src/VariableView.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableView.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,323 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VariableView.hpp" - -#include -#include -#include -#include -#include -#include -#include - -#include "IconProvider.hpp" -#include "VariableModel.hpp" -#include "VParam.hpp" - -//======================================================== -// -// VariableViewDelegate -// -//======================================================== - -VariableDelegate::VariableDelegate(QTreeView *parent) : QStyledItemDelegate(parent), view_(parent) -{ - selectPen_=QPen(QColor(8,117,182)); - selectBrush_=QBrush(QColor(65,139,212)); - selectBrushBlock_=QBrush(QColor(48,102,178)); - borderPen_=QPen(QColor(230,230,230)); - genVarPixId_=IconProvider::add(":/viewer/genvar.svg","genvar"); - shadowGenVarPixId_=IconProvider::add(":/viewer/genvar_shadow.svg","genvar_shadow"); -} - -void VariableDelegate::paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const -{ -#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) - QStyleOptionViewItem vopt(option); -#else - QStyleOptionViewItemV4 vopt(option); -#endif - - initStyleOption(&vopt, index); - - const QStyle *style = vopt.widget ? vopt.widget->style() : QApplication::style(); - const QWidget* widget = vopt.widget; - - //This indicates that the item is a parent item (node or server), - // hence it has branch controls. - bool hasChild=!index.parent().isValid(); - - bool selected=option.state & QStyle::State_Selected; - - //Save painter state - painter->save(); - - //The background rect - QRect bgRect=option.rect; - - //For variables in the first column we want to extend the item - //rect to the left for the background painting. - if(index.column()==0 && !hasChild) - { - bgRect.setX(0); - } - - //Paint item highlight!! It is taken from the UserRole - QColor highCol=index.data(Qt::UserRole).value(); - if(highCol.isValid()) - { - painter->fillRect(bgRect.adjusted(1,1,-1,-1),highCol); - } - //otherwise paint item background!! - else if(!selected) - { - //Paint the item background - QColor bg=index.data(Qt::BackgroundRole).value(); - if(bg.isValid()) - { - painter->fillRect(bgRect,bg); - } - //alternating row colour? - else {} - } - - //Paint selection. This should be transparent. - if(selected) - { - //The selection rect - QRect selectRect; - if(hasChild) - { - selectRect=bgRect.adjusted(0,1,0,-1); - painter->fillRect(selectRect,selectBrushBlock_); - } - else - { - selectRect=bgRect.adjusted(0,1,0,-1); - - //For the first column we extend the selection - //rect to left edge. - if(index.column()==0) - { - selectRect.setX(0); - } - painter->fillRect(selectRect,selectBrush_); - - } - } - - //Render the horizontal border for rows. We only render the top border line. - //With this technique we miss the bottom border line of the last row!!! - painter->setPen(borderPen_); - painter->drawLine(bgRect.topLeft(),bgRect.topRight()); - - if(index.column() == 0 && !hasChild) - { - painter->drawLine(bgRect.topRight(),bgRect.bottomRight()); - } - - //Display text - QString text=index.data(Qt::DisplayRole).toString(); - QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &vopt,widget); - QFont f; - QFontMetrics fm(f); - textRect.setWidth(fm.width(text)); - - if(index.column() == 0 && !hasChild) - { - bool hasLock=false; - QPixmap lockPix; - QRect lockRect; - bool hasGen=false; - QPixmap genPix; - QRect genRect; - - textRect.setLeft(option.rect.x()-17); - bool locked=index.data(VariableModel::ReadOnlyRole).toBool(); - if(locked) - { - hasLock=true; - lockPix=IconProvider::lockPixmap(textRect.height()-6); - - lockRect=QRect(textRect.left()-4-lockPix.width(), - textRect.top()+(textRect.height()-lockPix.height())/2, - lockPix.width(),lockPix.height()); - } - - bool gen=index.data(VariableModel::GenVarRole).toBool(); - if(gen && genVarPixId_ >= 0) - { - int pixId=genVarPixId_; - if(index.data(VariableModel::ShadowRole).toBool()) - { - pixId=shadowGenVarPixId_; - } - if(pixId >=0) - { - hasGen=true; - - genPix=IconProvider::pixmap(pixId,textRect.height()-4); - genRect=QRect(textRect.left(), - textRect.top()+(textRect.height()-genPix.height())/2, - genPix.width(), genPix.height()); - - textRect.moveLeft(genRect.right()+4); - } - } - - if(textRect.right()+1 > option.rect.right()) - { - painter->setClipRect(option.rect.adjusted(-option.rect.left(),0,0,0)); - } - QColor fg; - if(option.state & QStyle::State_Selected) - { - fg=Qt::white; - } - else - { - fg=index.data(Qt::ForegroundRole).value(); - if(!fg.isValid()) - fg=Qt::black; - } - - painter->setPen(fg); - - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - - if(hasLock) - painter->drawPixmap(lockRect,lockPix); - - if(hasGen) - painter->drawPixmap(genRect,genPix); - - } - else if(index.column() == 0) - { - QColor fg=index.data(Qt::ForegroundRole).value(); - if(!fg.isValid()) - fg=Qt::black; - painter->setPen(fg); - - QRegExp rx("^(.+)\\s(\\S+)$"); - //QRegExp rx("inherited from (\\S+) (\\S+)"); - if(rx.indexIn(text) > -1 && rx.captureCount() == 2) - { - QFont f; - f.setPointSize(f.pointSize()-1); - QFontMetrics fm(f); - QString txt1=rx.cap(1); - textRect.setWidth(fm.width(txt1)); - painter->setFont(f); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,txt1); - textRect.setLeft(textRect.right()+fm.width("D")); - text=rx.cap(2); - } - - QFont fBold; - fBold.setPointSize(fBold.pointSize()-1); - fBold.setBold(true); - QFontMetrics fmBold(fBold); - textRect.setWidth(fmBold.width(text + "a")); - painter->setFont(fBold); - - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - - } - else if(index.column() == 1) - { - textRect.adjust(2,0,2,0); - - QColor fg; - if(selected) - { - fg=Qt::white; - } - else - { - fg=index.data(Qt::ForegroundRole).value(); - if(!fg.isValid()) - fg=Qt::black; - } - - painter->setPen(fg); - painter->drawText(textRect,Qt::AlignLeft | Qt::AlignVCenter,text); - } - - //Restore painter state - painter->restore(); -} - -QSize VariableDelegate::sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const -{ - QSize size=QStyledItemDelegate::sizeHint(option,index); - - size+=QSize(0,2); - - //We need to starcth the second column to the right edge of the viewport - if(index.column() == 1) - { - int w1=view_->header()->sectionSize(0); - int w=view_->viewport()->size().width(); - if(w1+size.width() < w) - size.setWidth(w-w1+1); - } - return size; -} - -//======================================================== -// -// VariableView -// -//======================================================== - -VariableView::VariableView(QWidget* parent) : TreeView(parent) -{ - setProperty("var","1"); - - delegate_=new VariableDelegate(this); - setItemDelegate(delegate_); - - setRootIsDecorated(true); - setAllColumnsShowFocus(true); - setUniformRowHeights(true); - //setAlternatingRowColors(true); - setSortingEnabled(true); - - header()->setSortIndicator(0,Qt::AscendingOrder); - - //Context menu - setContextMenuPolicy(Qt::ActionsContextMenu); -} - -//We do not want to render the branches for the variable items. We only -//render the branch for the group items (nodes or server). -void VariableView::drawBranches(QPainter* painter,const QRect& rect,const QModelIndex& index ) const -{ - if(!index.parent().isValid() && index.column()==0) - { - //We need to fill the branch area here. We cannot do it in the delegate - //because when the delegate is called the branch control is already - //rendered, so the delegate would just overpaint it!!! - - if(selectionModel()->rowIntersectsSelection(index.row(),QModelIndex())) - { - painter->fillRect(rect.adjusted(0,1,0,-1),delegate_->selectBrushBlock_); - } - else - { - painter->fillRect(rect.adjusted(0,1,0,0),index.data(Qt::BackgroundRole).value()); - } - - //Draw the branch with the default method - QTreeView::drawBranches(painter,rect,index); - } -} diff -Nru ecflow-4.9.0/Viewer/src/VariableView.hpp ecflow-4.11.1/Viewer/src/VariableView.hpp --- ecflow-4.9.0/Viewer/src/VariableView.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VariableView.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,58 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VARIABLEVIEW_HPP_ -#define VARIABLEVIEW_HPP_ - - -#include -#include -#include -#include - -#include "TreeView.hpp" - -class VariableView; - -class VariableDelegate : public QStyledItemDelegate -{ - friend class VariableView; - -public: - explicit VariableDelegate(QTreeView *parent); - void paint(QPainter *painter,const QStyleOptionViewItem &option, - const QModelIndex& index) const; - QSize sizeHint(const QStyleOptionViewItem & option, const QModelIndex & index ) const; - -protected: - QPen selectPen_; - QBrush selectBrush_; - QBrush selectBrushBlock_; - QPen borderPen_; - QPixmap lockPix_; - int genVarPixId_; - int shadowGenVarPixId_; - QTreeView* view_; - -}; - -class VariableView : public TreeView -{ -public: - explicit VariableView(QWidget *parent=0); - -protected: - void drawBranches(QPainter* painter,const QRect& rect,const QModelIndex& index ) const; - - VariableDelegate *delegate_; -}; - - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VAttribute.cpp ecflow-4.11.1/Viewer/src/VAttribute.cpp --- ecflow-4.9.0/Viewer/src/VAttribute.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VAttribute.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,158 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VAttribute.hpp" -#include "VAttributeType.hpp" - -#include "VNode.hpp" -#include "UIDebug.hpp" - -#include - -//#define _UI_VATTRIBUTE_DEBUG - - -VAttribute::VAttribute(VNode *parent,int index) : - VItem(parent), - index_(index) -{ -} - -VAttribute::~VAttribute() -{ -} - -ServerHandler* VAttribute::server() const -{ - return (parent_)?parent_->server():NULL; -} - - -VServer* VAttribute::root() const -{ - return (parent_)?parent_->root():NULL; -} - -QString VAttribute::toolTip() const -{ - VAttributeType* t=type(); - return (t)?(t->toolTip(data())):QString(); -} - -QString VAttribute::definition() const -{ - VAttributeType* t=type(); - return (t)?(t->definition(data())):QString(); -} - -const std::string& VAttribute::typeName() const -{ - VAttributeType* t=type(); - assert(t); - static std::string e; - return (t)?(t->strName()):e; -} - -const std::string& VAttribute::subType() const -{ - static std::string e; - return e; -} - -std::string VAttribute::fullPath() const -{ - return (parent_)?(parent_->fullPath() + ":" + strName()):""; -} - -bool VAttribute::sameContents(VItem* item) const -{ - if(!item) - return false; - - if(VAttribute *a=item->isAttribute()) - { - return a->parent() == parent() && - a->type() == type() && - a->name() == name(); - } - return false; -} - -QString VAttribute::name() const -{ - return QString::fromStdString(strName()); -} - -std::string VAttribute::strName() const -{ - static std::string eStr; - return eStr; -} - -bool VAttribute::value(const std::string& key,std::string& val) const -{ - int idx=type()->searchKeyToDataIndex(key); - if(idx != -1) - { - QStringList d=data(); - val=d[idx].toStdString(); - return true; - } - return false; -} - -bool VAttribute::sameAs(QStringList d) const -{ - if(d.count() >=2) - { - VAttributeType* t=type(); - - if(t->name() == d[0]) - { - int idx=t->searchKeyToDataIndex("name"); - if(idx != -1 && idx < d.count()) - { - return name() == d[idx]; - } - } - } - return false; -} - -void VAttribute::buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& name,const std::string& value) -{ - cmd.push_back("ecflow_client"); - cmd.push_back("--alter"); - cmd.push_back(action); - cmd.push_back(type); - - if(!name.empty()) - { - cmd.push_back(name); - cmd.push_back(value); - } - - cmd.push_back(""); -} - -void VAttribute::buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& value) -{ - cmd.push_back("ecflow_client"); - cmd.push_back("--alter"); - cmd.push_back(action); - cmd.push_back(type); - cmd.push_back(value); - - cmd.push_back(""); -} diff -Nru ecflow-4.9.0/Viewer/src/VAttribute.hpp ecflow-4.11.1/Viewer/src/VAttribute.hpp --- ecflow-4.9.0/Viewer/src/VAttribute.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VAttribute.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VATTRIBUTE_HPP -#define VATTRIBUTE_HPP - -#include "VItem.hpp" - -#include -#include -#include - -class AttributeFilter; -class VAttributeType; -class VNode; -class VAttribute; - -class VAttribute : public VItem -{ -public: - VAttribute(VNode *parent,int index); - ~VAttribute(); - - virtual VAttributeType* type() const=0; - virtual const std::string& subType() const; - virtual int lineNum() const {return 1;} - ServerHandler* server() const; - VServer* root() const; - VAttribute* isAttribute() const {return const_cast(this);} - QString toolTip() const; - QString name() const; - std::string strName() const; - const std::string& typeName() const; - std::string fullPath() const; - virtual QStringList data(bool firstLine=false) const=0; - QString definition() const; - bool sameAs(QStringList d) const; - bool sameContents(VItem*) const; - bool value(const std::string& key,std::string& val) const; - - static void buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& name,const std::string& value); - - static void buildAlterCommand(std::vector& cmd, - const std::string& action, const std::string& type, - const std::string& value); - - -protected: - - int index_; -}; - -#endif // VATTRIBUTE_HPP - diff -Nru ecflow-4.9.0/Viewer/src/VAttributeType.cpp ecflow-4.11.1/Viewer/src/VAttributeType.cpp --- ecflow-4.9.0/Viewer/src/VAttributeType.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VAttributeType.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,173 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VAttributeType.hpp" - -#include - -#include -#include -#include -#include - -#include -#include - -#include "VNode.hpp" -#include "VConfigLoader.hpp" -#include "VProperty.hpp" -#include "VFilter.hpp" -#include "VAttribute.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" -#include "UIDebug.hpp" - -std::map VAttributeType::typesMap_; -std::vector VAttributeType::types_; - -//#define _UI_ATTR_DEBUG - -VAttributeType::VAttributeType(const std::string& name) : - VParam(name), - dataCount_(0), - typeId_(types_.size()) -{ - typesMap_[name]=this; - types_.push_back(this); -} - -std::vector VAttributeType::filterItems() -{ - std::vector v; - for(TypeIterator it=types_.begin(); it != types_.end(); ++it) - v.push_back(*it); - - return v; -} - -VAttributeType* VAttributeType::find(const std::string& name) -{ - std::map::const_iterator it=typesMap_.find(name); - if(it != typesMap_.end()) - return it->second; - - return 0; -} - -VAttributeType* VAttributeType::find(int id) -{ - assert(id >=0 && id < static_cast(types_.size())); - return types_[id]; -} - -void VAttributeType::scan(VNode* vnode,std::vector& v) -{ - for(TypeIterator it=types_.begin(); it != types_.end(); ++it) - { - (*it)->scanProc()(vnode,v); - } -} - -int VAttributeType::keyToDataIndex(const std::string& key) const -{ - std::map::const_iterator it=keyToData_.find(key); - if(it != keyToData_.end()) - return it->second; - - return -1; -} - -int VAttributeType::searchKeyToDataIndex(const std::string& key) const -{ - std::map::const_iterator it=searchKeyToData_.find(key); - if(it != searchKeyToData_.end()) - return it->second; - - return -1; -} - -QStringList VAttributeType::searchKeys() const -{ - QStringList lst; - for(std::map::const_iterator it=searchKeyToData_.begin(); it != searchKeyToData_.end(); it++) - { - lst << QString::fromStdString(it->first); - } - return lst; -} - -//Load the attributes parameter file -void VAttributeType::load(VProperty* group) -{ - //We set some extra information on each type and also - //try to reorder the types according to the order defined in the - //parameter file. This order is very important: - // -it defines the rendering order - // -defines the order of the attribute items in the attribute filter - std::vector v; - Q_FOREACH(VProperty* p,group->children()) - { - if(VAttributeType* obj=VAttributeType::find(p->strName())) - { - obj->setProperty(p); - v.push_back(obj); - } - else - { - UserMessage::message(UserMessage::ERROR,true, - "Unknown attribute type=" + p->strName() + " is loaded from parameter file!"); - exit(1); - //UI_ASSERT(0,"Unknown attribute type is read from parameter file: " << p->strName()); - } - } - - //UI_ASSERT(v.size() == types_.size(),"types size=" << types_.size() << "loaded size=" << v.size()); - - if(v.size() == types_.size()) - types_=v; - else - { - UserMessage::message(UserMessage::ERROR,true, - "The number attributes loaded from parameter file do not match expected number! loaded=" + - UserMessage::toString(v.size()) + " expected=" + UserMessage::toString(types_.size())); - exit(1); - } -} - - -static SimpleLoader loader("attribute"); - -//Initialise attribute types - -#include "VLabelAttr.hpp" -#include "VMeterAttr.hpp" -#include "VEventAttr.hpp" -#include "VLimitAttr.hpp" -#include "VLimiterAttr.hpp" -#include "VRepeatAttr.hpp" -#include "VTriggerAttr.hpp" -#include "VDateAttr.hpp" -#include "VTimeAttr.hpp" -#include "VLateAttr.hpp" -#include "VGenVarAttr.hpp" -#include "VUserVarAttr.hpp" - -static VLabelAttrType labelAttrType; -static VMeterAttrType meterAttType; -static VEventAttrType eventAttrType; -static VLimitAttrType limitAttrType; -static VLimiterAttrType limiterAttrType; -static VRepeatAttrType repeatAttrType; -static VTriggerAttrType triggerAttrType; -static VDateAttrType dateAttrType; -static VTimeAttrType timeAttrType; -static VLateAttrType lateAttrType; -static VGenVarAttrType genvarAttrType; -static VUserVarAttrType uservarAttrType; diff -Nru ecflow-4.9.0/Viewer/src/VAttributeType.hpp ecflow-4.11.1/Viewer/src/VAttributeType.hpp --- ecflow-4.9.0/Viewer/src/VAttributeType.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VAttributeType.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VATTRIBUTETYPE_HPP_ -#define VATTRIBUTETYPE_HPP_ - -#include -#include -#include - -#include "VParam.hpp" - -class AttributeFilter; -class VNode; -class VAttribute; - -class VAttributeType : public VParam -{ -public: - virtual ~VAttributeType() {} - - static std::vector filterItems(); - static VAttributeType* find(const std::string& name); - static VAttributeType* find(int id); - static const std::vector& types() {return types_;} - int typeId() const {return typeId_;} - int keyToDataIndex(const std::string& key) const; - int searchKeyToDataIndex(const std::string& key) const; - QStringList searchKeys() const; - virtual QString toolTip(QStringList d) const {return QString();} - virtual QString definition(QStringList d) const {return QString();} - - static void scan(VNode* vnode,std::vector& v); - typedef void (*ScanProc) (VNode* vnode,std::vector& vec); - ScanProc scanProc() {return scanProc_;} - - //Called from VConfigLoader - static void load(VProperty*); - -protected: - explicit VAttributeType(const std::string& name); - - typedef std::vector::const_iterator TypeIterator; - std::map keyToData_; - std::map searchKeyToData_; - int dataCount_; - int typeId_; - ScanProc scanProc_; - -private: - static std::map typesMap_; - static std::vector types_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VConfig.cpp ecflow-4.11.1/Viewer/src/VConfig.cpp --- ecflow-4.9.0/Viewer/src/VConfig.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VConfig.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,658 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VConfig.hpp" -#include "VConfigLoader.hpp" -#include "VProperty.hpp" -#include "VSettings.hpp" - -#include "DirectoryHandler.hpp" -#include "SessionHandler.hpp" -#include "UiLog.hpp" -#include "UserMessage.hpp" - -#include "Version.hpp" - -#include - -#include -#include -#include -#include - -VConfig* VConfig::instance_=0; - -//#define _UI_CONFIG_LOAD_DEBUG - -VConfig::VConfig() -{ - appName_="ecFlowUI"; - appLongName_=appName_ + " (" + ecf::Version::raw() + ")"; -} - -VConfig::~VConfig() -{ - for(std::vector::iterator it=groups_.begin(); it != groups_.end(); ++it) - { - delete *it; - } - groups_.clear(); -} - - -VConfig* VConfig::instance() -{ - if(!instance_) - instance_=new VConfig(); - - return instance_; -} - -void VConfig::init(const std::string& parDirPath) -{ - namespace fs=boost::filesystem; - - fs::path parDir(parDirPath); - - if(fs::exists(parDir) && fs::is_directory(parDir)) - { - //fs::directory_iterator it(parDir); - - //The conf files have to be loaded in alphabetical order!! At least NotifyChange require it! - //So we read the paths into a vector and sort it. - std::vector vec; - copy(fs::directory_iterator(parDir), fs::directory_iterator(), back_inserter(vec)); - std::sort(vec.begin(), vec.end()); - - //The paths are now in alphabetical order - for(std::vector::const_iterator it=vec.begin(); it != vec.end(); ++it) - { - if(fs::is_regular_file(*it)) - { - std::string name=it->filename().string(); - if(name.find("_conf.json") != std::string::npos) - { - loadInit(it->string()); - } - } - } - } - - //Read gui definition for the editable properties - std::string guiFile=DirectoryHandler::concatenate(parDir.string(),"ecflowview_gui.json"); - loadInit(guiFile); - - //Read gui definition for the editable properties tahat can be cutomised per server - std::string guiServerFile=DirectoryHandler::concatenate(parDir.string(),"ecflowview_gui_server.json"); - loadInit(guiServerFile); - - //Load existing user settings for the editable properties - loadSettings(); - -} - -void VConfig::loadInit(const std::string& parFile) -{ - //Parse param file using the boost JSON property tree parser - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(parFile,pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, - std::string("Fatal error!\nVConfig::load() unable to parse definition file: " + parFile + "\nMessage: " +errorMessage)); - exit(1); - return; - } - - //Loop over the groups - for(ptree::const_iterator itGr = pt.begin(); itGr != pt.end(); ++itGr) - { - ptree ptGr=itGr->second; - - //Get the group name and create it - std::string groupName=itGr->first; - VProperty *grProp=new VProperty(groupName); - groups_.push_back(grProp); - - UiLog().dbg() << "VConfig::loadInit() read config group: " << groupName; - - //Load the property parameters. It will recursively add all the - //children properties. - loadProperty(ptGr,grProp); - - //Add the group we created to the registered configloader - VConfigLoader::process(groupName,grProp); - } - } - -void VConfig::loadProperty(const boost::property_tree::ptree& pt,VProperty *prop) -{ - using boost::property_tree::ptree; - - ptree::const_assoc_iterator itProp; - - //Loop over the possible properties - for(ptree::const_iterator it = pt.begin(); it != pt.end(); ++it) - { - std::string name=it->first; - ptree ptProp=it->second; - -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " VConfig::loadProperty() read item: " << name; -#endif - //Default value - if(name == "default") - { - std::string val=ptProp.get_value(); - prop->setDefaultValue(val); - } - - //If it is just a key/value pair "line" - else if(name == "line" && ptProp.empty()) - { - VProperty *chProp=new VProperty(name); - prop->addChild(chProp); - std::string val=ptProp.get_value(); - - QString prefix=prop->param("prefix"); - if(!prefix.isEmpty()) - val=prefix.toStdString() + "." + val; - -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " VConfig::loadProperty() line: " << val; -#endif - if(VProperty* lineEditProp=find(val)) - { -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " --> link found"; -#endif - chProp->setLink(lineEditProp); - } - else - { -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " --> link NOT found"; -#endif - } - } - //If the property is a "line" (i.e. a line with additional parameters) - else if(prop->name() == "line" && name == "link") - { - std::string val=ptProp.get_value(); - -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " VConfig::loadProperty() line link: " << val; -#endif - if(VProperty* lineEditProp=find(val)) - { -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " --> link found"; -#endif - prop->setLink(lineEditProp); - } - else - { -#ifdef _UI_CONFIG_LOAD_DEBUG - UiLog().dbg() << " --> link NOT found"; -#endif - } - } - - //Here we only load the properties with - //children (i.e. key/value pairs (like "line" etc above) - //are ignored. - else if(!ptProp.empty()) - { - VProperty *chProp=new VProperty(name); - prop->addChild(chProp); - loadProperty(ptProp,chProp); - chProp->adjustAfterLoad(); - } - else - { - QString val=QString::fromStdString(ptProp.get_value()); - prop->setParam(QString::fromStdString(name),val); - } - } -} - -VProperty* VConfig::find(const std::string& path) -{ - VProperty* res=0; - - for(std::vector::const_iterator it=groups_.begin();it != groups_.end(); ++it) - { - VProperty *vGroup=*it; - res=vGroup->find(path); - if(res) - { - return res; - } - } - - return res; -} - -VProperty* VConfig::group(const std::string& name) -{ - for(std::vector::const_iterator it=groups_.begin();it != groups_.end(); ++it) - { - if((*it)->strName() == name) - return *it; - } - - return 0; -} - -VProperty* VConfig::cloneServerGui(VProperty *linkTarget) -{ - VProperty* gr=find("gui_server.server"); - - assert(gr); - - VProperty* cGr=gr->clone(true,false); - - std::vector chVec; - cGr->collectChildren(chVec); - for(std::vector::iterator it=chVec.begin(); it != chVec.end(); ++it) - { - VProperty *p=*it; - if(p->link()) - { - p->setLink(linkTarget->find(p->link()->path())); - } - } - - return cGr; -} - -//Saves the global settings that can be edited through the gui -void VConfig::saveSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - std::string fName=cs->settingsFile(); - - VProperty *guiProp=group("gui"); - - saveSettings(fName,guiProp,NULL,true); -} - -//Saves the settings per server that can be edited through the servers option gui -void VConfig::saveSettings(const std::string& parFile,VProperty* guiProp,VSettings* vs,bool global) -{ - using boost::property_tree::ptree; - ptree pt; - - //Get editable properties. We will operate on the links. - std::vector linkVec; - guiProp->collectLinks(linkVec); - - for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) - { - if(global) - { - if((*it)->changed()) - { - pt.put((*it)->path(),(*it)->valueAsStdString()); - } - } - - else - { - if(!(*it)->useMaster()) - { - pt.put((*it)->path(),(*it)->valueAsStdString()); - } - } - } - - //Add settings stored in VSettings - if(vs) - { - //Loop over the possible properties - for(ptree::const_iterator it = vs->propertyTree().begin(); it != vs->propertyTree().end(); ++it) - { - pt.add_child(it->first,it->second); - } - } - - write_json(parFile,pt); -} - -//Loads the global settings that can be edited through the gui -void VConfig::loadSettings() -{ - SessionItem* cs=SessionHandler::instance()->current(); - std::string parFile=cs->settingsFile(); - - VProperty *guiProp=group("gui"); - - loadSettings(parFile,guiProp,true); -} - -//Loads the settings per server that can be edited through the servers option gui -void VConfig::loadSettings(const std::string& parFile,VProperty* guiProp,bool global) -{ - //We will operate on the links - std::vector linkVec; - guiProp->collectLinks(linkVec); - - //Parse file using the boost JSON property tree parser - using boost::property_tree::ptree; - ptree pt; - - try - { - read_json(parFile,pt); - } - catch (const boost::property_tree::json_parser::json_parser_error& e) - { - if(boost::filesystem::exists(parFile)) - { - std::string errorMessage = e.what(); - UserMessage::message(UserMessage::ERROR, true, - std::string("Error! VConfig::loadSettings() unable to parse settings file: " + parFile + " Message: " +errorMessage)); - } - return; - } - - for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) - { - if(pt.get_child_optional((*it)->path()) != boost::none) - { - std::string val=pt.get((*it)->path()); - - if(!global) - { - (*it)->setUseMaster(false); - } - - (*it)->setValue(val); - } - } - - //nodeMenuMode was introduced in 4.7.0 but was renamed in 4.8.0. We need to make sure - //it is read correctly when 4.8.0 started up for the first time. - //TODO: In versions after 4.8.0 we can remove this code!!! - if(global) - { - std::string prevPath="menu.access.nodeMenuMode"; - std::string actPath="server.menu.nodeMenuMode"; - if(pt.get_child_optional(prevPath) != boost::none) - { - for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) - { - if((*it)->path() == actPath) - { - std::string val=pt.get(prevPath); - (*it)->setValue(val); - break; - } - } - } - } - -} - -void VConfig::loadImportedSettings(const boost::property_tree::ptree& pt,VProperty* guiProp) -{ - std::vector linkVec; - guiProp->collectLinks(linkVec); - - for(std::vector::const_iterator it=linkVec.begin(); it != linkVec.end(); ++it) - { - if(pt.get_child_optional((*it)->path()) != boost::none) - { - std::string val=pt.get((*it)->path()); - (*it)->setValue(val); - } - else if((*it)->master()) - { - (*it)->setUseMaster(true); - } - } -} - -void VConfig::importSettings() -{ - boost::property_tree::ptree pt; - - std::string globalRcFile(DirectoryHandler::concatenate(DirectoryHandler::rcDir(),"user.default.options")); - if(readRcFile(globalRcFile,pt)) - { - VProperty* gr=VConfig::find("gui"); - loadImportedSettings(pt,gr); - VConfig::saveSettings(); - } -} - -bool VConfig::readRcFile(const std::string& rcFile,boost::property_tree::ptree& pt) -{ - std::ifstream in(rcFile.c_str()); - - if(!in.good()) - return false;; - - bool hasValue=false; - - std::string line; - while(getline(in,line)) - { - std::string buf; - std::stringstream ssdata(line); - std::vector vec; - - while(ssdata >> buf) - { - vec.push_back(buf); - } - - if(vec.size() >= 1) - { - std::vector par; - boost::split(par,vec[0],boost::is_any_of(":")); - - if(par.size()==2) - { - //Update - if(par[0] == "timeout") - { - pt.put("server.update.updateRateInSec",par[1]); - hasValue=true; - } - else if(par[0] == "poll") - { - pt.put("server.update.update",par[1]); - hasValue=true; - } - - else if(par[0] == "drift") - { - pt.put("server.update.adaptiveUpdate",par[1]); - hasValue=true; - } - else if(par[0] == "maximum") - { - pt.put("server.update.maxAdaptiveUpdateRateInMin",par[1]); - hasValue=true; - } - - //Files - else if(par[0] == "direct_read") - { - pt.put("server.files.readFilesFromDisk",par[1]); - hasValue=true; - } - else if(par[0] == "jobfile_length") - { - pt.put("server.files.maxOutputFileLines",par[1]); - hasValue=true; - } - - //Popup - else if(par[0] == "aborted") - { - pt.put("server.notification.aborted.enabled",par[1]); - pt.put("server.notification.aborted.popup",par[1]); - hasValue=true; - } - else if(par[0] == "restarted") - { - pt.put("server.notification.restarted.enabled",par[1]); - pt.put("server.notification.restarted.popup",par[1]); - hasValue=true; - } - else if(par[0] == "late") - { - pt.put("server.notification.late.enabled",par[1]); - pt.put("server.notification.late.popup",par[1]); - hasValue=true; - } - else if(par[0] == "zombies") - { - pt.put("server.notification.zombie.enabled",par[1]); - pt.put("server.notification.zombie.popup",par[1]); - hasValue=true; - } - else if(par[0] == "aliases") - { - pt.put("server.notification.alias.enabled",par[1]); - pt.put("server.notification.alias.popup",par[1]); - hasValue=true; - } - //Suites - else if(par[0] == "new_suites") - { - pt.put("suite_filter.autoAddNew",par[1]); - hasValue=true; - - } - else if(par[0] == "suites") - { - boost::property_tree::ptree suites; - suites.push_back(std::make_pair(std::string(""),boost::property_tree::ptree(par[1]))); - - for(unsigned int j=1; j < vec.size(); j++) - { - suites.push_back(std::make_pair(std::string(""),boost::property_tree::ptree(vec.at(j)))); - } - - pt.put_child("suite_filter.suites",suites); - - pt.put("suite_filter.enabled","true"); - - hasValue=true; - } - } - } - - } //while(getline) - - in.close(); - - return hasValue; - -} -/* -void VConfig::decodeShowMask() -{ - -option show::status32_ (globals::instance(), "show_mask32", 0); - -option show::status_ (globals::instance(), "show_mask", - (1< 31) { - status32_ = int(status32_) | (1<<(flag_-32)); - } else { - status_ = int(status_ ) | (1<<(flag_)); - } -} - -void show::off() -{ - if (flag_ == show::all) { - status_ = 0xFFFF ; status32_ = 0xFFFF; - status32_ = (int) (status32_) & (~(1<<(show::none-32))); - status32_ = (int) (status32_) & (~(1<<(show::all -32))); - } else if (flag_ == show::none) { - status_ = 0; status32_ = 0; - } else if (flag_ > 31) { - status32_ = int(status32_) & (~(1<<(flag_-32))); - } else { - status_ = int(status_) & (~(1< 31) { - return (int(status32_) & (1<<(flag_-32))) != 0; - } else { - return (int(status_ ) & (1< - -#include - -class VProperty; -class VServerSettings; -class VSettings; - -//This singleton class stores the configuration of the viewer. - -class VConfig -{ - friend class VServerSettings; - -public: - ~VConfig(); - - static VConfig* instance(); - - const std::string& appName() const {return appName_;} - const std::string& appLongName() const {return appLongName_;} - void init(const std::string& parDir); - const std::vector& groups() const {return groups_;} - VProperty* find(const std::string& path); - - VProperty* cloneServerGui(VProperty *linkTarget); - - void saveSettings(); - void importSettings(); - -protected: - VConfig(); - - void loadInit(const std::string& parFile); - void loadProperty(const boost::property_tree::ptree& pt,VProperty *prop); - void loadSettings(); - void saveSettings(const std::string& parFile,VProperty* guiProp,VSettings* vs,bool); - void loadSettings(const std::string& parFile,VProperty* guiProp,bool); - void loadImportedSettings(const boost::property_tree::ptree& pt,VProperty* guiProp); - bool readRcFile(const std::string& rcFile,boost::property_tree::ptree& pt); - - VProperty* group(const std::string& name); - - static VConfig* instance_; - - std::string appName_; - std::string appLongName_; - std::vector groups_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VConfigLoader.cpp ecflow-4.11.1/Viewer/src/VConfigLoader.cpp --- ecflow-4.9.0/Viewer/src/VConfigLoader.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VConfigLoader.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VConfigLoader.hpp" - -#include - - -typedef std::multimap Map; - -static Map* makers = 0; - -VConfigLoader::VConfigLoader(const std::string& name) -{ - if(makers == 0) - makers = new Map(); - - makers->insert(Map::value_type(name,this)); -} - -VConfigLoader::~VConfigLoader() -{ - // Not called -} - -bool VConfigLoader::process(const std::string& name,VProperty *prop) -{ - Map::size_type entries=makers->count(name); - Map::iterator it=makers->find(name); - - bool retVal=false; - for(Map::size_type cnt=0; cnt != entries; ++cnt, ++it) - { - (*it).second->load(prop); - retVal=true; - } - - /* if(it != makers->end()) - { - (*it).second->load(prop); - return true; - }*/ - return retVal; -} diff -Nru ecflow-4.9.0/Viewer/src/VConfigLoader.hpp ecflow-4.11.1/Viewer/src/VConfigLoader.hpp --- ecflow-4.9.0/Viewer/src/VConfigLoader.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VConfigLoader.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VCONFIGLOADER_HPP_ -#define VCONFIGLOADER_HPP_ - -#include - -class VProperty; - -class VConfigLoader -{ -public: - explicit VConfigLoader(const std::string& name); - virtual ~VConfigLoader(); - - virtual void load(VProperty* group) = 0; - static bool process(const std::string& name,VProperty*); - -private: - // No copy allowed - explicit VConfigLoader(const VConfigLoader&); - VConfigLoader& operator=(const VConfigLoader&); -}; - -template -class SimpleLoader : public VConfigLoader { - void load(VProperty* prop) { T::load(prop); } -public: - explicit SimpleLoader(const std::string& name) : VConfigLoader(name) {} -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VDateAttr.cpp ecflow-4.11.1/Viewer/src/VDateAttr.cpp --- ecflow-4.9.0/Viewer/src/VDateAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VDateAttr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,148 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VDateAttr.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -#include "DateAttr.hpp" -#include "DayAttr.hpp" - -//================================ -// VDateAttrType -//================================ - -VDateAttrType::VDateAttrType() : VAttributeType("date") -{ - dataCount_=2; - searchKeyToData_["date_name"]=NameIndex; - searchKeyToData_["name"]=NameIndex; - scanProc_=VDateAttr::scan; -} - -QString VDateAttrType::toolTip(QStringList d) const -{ - QString t="Type: Date
      "; - if(d.count() == dataCount_) - { - t+="Name: " + d[NameIndex]; - } - return t; -} - -QString VDateAttrType::definition(QStringList d) const -{ - QString t; - if(d.count() == dataCount_) - { - t+=" " + d[NameIndex]; - } - return t; -} - -void VDateAttrType::encode(const DateAttr& d,QStringList& data) -{ - data << qName_ << QString::fromStdString(d.name()); -} - -void VDateAttrType::encode(const DayAttr& d,QStringList& data) -{ - data << qName_ << QString::fromStdString(d.name()); -} - -//===================================================== -// -// VDateAttr -// -//===================================================== - -VDateAttr::VDateAttr(VNode *parent,const DateAttr& t, int index) : - VAttribute(parent,index), - dataType_(DateData) -{ - //name_=t.name(); -} - -VDateAttr::VDateAttr(VNode *parent,const DayAttr& t, int index) : - VAttribute(parent,index), - dataType_(DayData) -{ - //name_=t.name(); -} - -VAttributeType* VDateAttr::type() const -{ - static VAttributeType* atype=VAttributeType::find("date"); - return atype; -} - -QStringList VDateAttr::data(bool /*firstLine*/) const -{ - static VDateAttrType* atype=static_cast(type()); - QStringList s; - if(parent_->node_) - { - if(dataType_ == DateData) - { - const std::vector& v=parent_->node_->dates(); - if(index_ < static_cast(v.size())) - atype->encode(v[index_],s); - } - else if(dataType_ == DayData) - { - const std::vector& v=parent_->node_->days(); - if(index_ < static_cast(v.size())) - atype->encode(v[index_],s); - } - } - return s; -} - -std::string VDateAttr::strName() const -{ - if(parent_->node_) - { - if(dataType_ == DateData) - { - const std::vector& v=parent_->node_->dates(); - if(index_ < static_cast(v.size())) - return v[index_].name(); - } - else if(dataType_ == DayData) - { - const std::vector& v=parent_->node_->days(); - if(index_ < static_cast(v.size())) - return v[index_].name(); - } - } - return std::string(); -} - -void VDateAttr::scan(VNode* vnode,std::vector& vec) -{ - if(vnode->node_) - { - const std::vector& dateV=vnode->node_->dates(); - const std::vector& dayV=vnode->node_->days(); - - int n=static_cast(dateV.size()); - for(int i=0; i < n; i++) - { - vec.push_back(new VDateAttr(vnode,dateV[i],i)); - } - - n=static_cast(dayV.size()); - for(int i=0; i < n; i++) - { - vec.push_back(new VDateAttr(vnode,dayV[i],i)); - } - } -} - diff -Nru ecflow-4.9.0/Viewer/src/VDateAttr.hpp ecflow-4.11.1/Viewer/src/VDateAttr.hpp --- ecflow-4.9.0/Viewer/src/VDateAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VDateAttr.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VDATE_HPP -#define VDATE_HPP - -#include "VAttribute.hpp" -#include "VAttributeType.hpp" - -#include -#include - -class AttributeFilter; -class VAttributeType; -class VNode; - -class DateAttr; -class DayAttr; - -class VDateAttrType : public VAttributeType -{ -public: - explicit VDateAttrType(); - QString toolTip(QStringList d) const; - QString definition(QStringList d) const; - void encode(const DateAttr& d,QStringList& data); - void encode(const DayAttr& d,QStringList& data); - -private: - enum DataIndex {TypeIndex=0,NameIndex=1}; -}; - -class VDateAttr : public VAttribute -{ - -public: - enum DataType {DateData,DayData}; - - VDateAttr(VNode *parent,const DateAttr&,int index); - VDateAttr(VNode *parent,const DayAttr&,int index); - - VAttributeType* type() const; - QStringList data(bool firstLine) const; - std::string strName() const; - - static void scan(VNode* vnode,std::vector& vec); - -protected: - DataType dataType_; -}; - -#endif // VDATE_HPP diff -Nru ecflow-4.9.0/Viewer/src/VDir.cpp ecflow-4.11.1/Viewer/src/VDir.cpp --- ecflow-4.9.0/Viewer/src/VDir.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VDir.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VDir.hpp" -#include "DirectoryHandler.hpp" - -#include -#include -#include - -#include - - -VDir::VDir(const std::string& path) : path_(path), fetchMode_(NoFetchMode) -{ -} - -VDir::VDir(const std::string& path,const std::string& pattern) : - path_(path), - pattern_(pattern), - fetchMode_(NoFetchMode) -{ - reload(); -} - -VDir::~VDir() -{ - clear(); -} - -void VDir::path(const std::string& path,bool doReload) -{ - path_=path; - if(doReload) - reload(); -} - -void VDir::clear() -{ - for(std::vector::iterator it=items_.begin(); it != items_.end(); ++it) - delete (*it); -} - -void VDir::addItem(const std::string& name, unsigned int size,unsigned int mtime) -{ - VDirItem* item=new VDirItem; - - boost::filesystem::path p(name); - //std::string dirName=p.parent_path().string(); - std::string fileName=p.leaf().string(); - - item->name_=fileName; - item->size_=size; - item->mtime_ = QDateTime::fromTime_t(mtime); - - items_.push_back(item); - -} - -void VDir::reload() -{ - clear(); - - where_="localhost"; - - boost::filesystem::path path(path_); - - boost::filesystem::directory_iterator it(path), eod; - - BOOST_FOREACH(boost::filesystem::path const &p, std::make_pair(it, eod )) - { - if(is_regular_file(p) && boost::algorithm::starts_with(p.filename().string(),pattern_)) - { - VDirItem* item=new VDirItem; - - item->name_ = p.filename().string(); - item->size_ = boost::filesystem::file_size(p); - item->size_ = boost::filesystem::file_size(p); - item->mtime_ = QDateTime::fromTime_t(boost::filesystem::last_write_time(p)); - items_.push_back(item); - - } - } -} - -std::string VDir::fullName(int row) -{ - std::string res; - if(row >=0 && row < static_cast(items_.size())) - { - res=DirectoryHandler::concatenate(path_,items_.at(row)->name_); - } - return res; -} - -int VDir::findByFullName(const std::string& fName) -{ - for(std::size_t i=0; i < items_.size(); i++) - { - if(fullName(i) == fName) - return i; - } - return -1; -} diff -Nru ecflow-4.9.0/Viewer/src/VDir.hpp ecflow-4.11.1/Viewer/src/VDir.hpp --- ecflow-4.9.0/Viewer/src/VDir.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VDir.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,78 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VDIR_H -#define VDIR_H - -#include -#include - -#include - -#include - -class VDirItem -{ -public: - std::string name_; - //int mode_; - //int uid_; - //int gid_; - unsigned int size_; - //int atime_; - QDateTime mtime_; - //int ctime_; - std::string method_; -}; - -class VDir -{ -public: - explicit VDir(const std::string& path); - VDir(const std::string& path, const std::string& pattern); - ~VDir(); - - enum FetchMode {NoFetchMode,LocalFetchMode,ServerFetchMode,LogServerFetchMode}; - - const std::string& path() const {return path_;} - void path(const std::string& path,bool reload=true); - - const std::string& where() const {return where_;} - void where(const std::string& w) {where_=w;} - - std::string fullName(int row); - int findByFullName(const std::string& fName); - int count() const {return static_cast(items_.size());} - void clear(); - void reload(); - void addItem(const std::string&, unsigned int, unsigned int); - const std::vector& items() const {return items_;} - - void setFetchDate(QDateTime d) {fetchDate_=d;} - QDateTime fetchDate() const {return fetchDate_;} - void setFetchMode(FetchMode m) {fetchMode_=m;} - FetchMode fetchMode() const {return fetchMode_;} - void setFetchModeStr(const std::string& fetchMethod) {fetchModeStr_=fetchMethod;} - const std::string& fetchModeStr() const {return fetchModeStr_;} - -protected: - std::string path_; - std::string pattern_; - std::string where_; - std::vector items_; - - FetchMode fetchMode_; - std::string fetchModeStr_; - QDateTime fetchDate_; -}; - -class VDir; -typedef boost::shared_ptr VDir_ptr; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VEventAttr.cpp ecflow-4.11.1/Viewer/src/VEventAttr.cpp --- ecflow-4.9.0/Viewer/src/VEventAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VEventAttr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,110 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VEventAttr.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -#include "NodeAttr.hpp" - -//================================ -// VEventAttrType -//================================ - -VEventAttrType::VEventAttrType() : VAttributeType("event") -{ - dataCount_=3; - searchKeyToData_["event_name"]=NameIndex; - searchKeyToData_["event_value"]=ValueIndex; - searchKeyToData_["name"]=NameIndex; - scanProc_=VEventAttr::scan; -} - -QString VEventAttrType::toolTip(QStringList d) const -{ - QString t="Type: Event
      "; - if(d.count() == dataCount_) - { - t+="Name: " + d[NameIndex] + "
      "; - t+="Status: "; - t+=(d[ValueIndex] == "1")?"set (true)":"clear (false)"; - - } - return t; -} - -QString VEventAttrType::definition(QStringList d) const -{ - QString t="event"; - if(d.count() == dataCount_) - { - t+=" " + d[NameIndex]; - } - return t; -} - -void VEventAttrType::encode(const Event& e,QStringList& data) const -{ - data << qName_ << - QString::fromStdString(e.name_or_number()) << - QString::number((e.value()==true)?1:0); -} - -//===================================================== -// -// VEventAttr -// -//===================================================== - -VEventAttr::VEventAttr(VNode *parent,const Event& e, int index) : VAttribute(parent,index) -{ - //name_=e.name_or_number(); -} - -VAttributeType* VEventAttr::type() const -{ - static VAttributeType* atype=VAttributeType::find("event"); - return atype; -} - -QStringList VEventAttr::data(bool /*firstLine*/) const -{ - static VEventAttrType* atype=static_cast(type()); - QStringList s; - if(node_ptr node=parent_->node_) - { - const std::vector& v=parent_->node_->events(); - atype->encode(v[index_],s); - } - return s; -} - -std::string VEventAttr::strName() const -{ - if(parent_->node_) - { - const std::vector& v=parent_->node_->events(); - return v[index_].name_or_number(); - } - return std::string(); -} - -void VEventAttr::scan(VNode* vnode,std::vector& vec) -{ - if(vnode->node_) - { - const std::vector& v=vnode->node_->events(); - int n=static_cast(v.size()); - for(int i=0; i < n; i++) - { - vec.push_back(new VEventAttr(vnode,v[i],i)); - } - } -} diff -Nru ecflow-4.9.0/Viewer/src/VEventAttr.hpp ecflow-4.11.1/Viewer/src/VEventAttr.hpp --- ecflow-4.9.0/Viewer/src/VEventAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VEventAttr.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,51 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VEVENT_HPP -#define VEVENT_HPP - -#include "VAttribute.hpp" -#include "VAttributeType.hpp" - -#include -#include -#include - -class AttributeFilter; -class VAttributeType; -class VNode; - -class Event; - -class VEventAttrType : public VAttributeType -{ -public: - explicit VEventAttrType(); - QString toolTip(QStringList d) const; - QString definition(QStringList d) const; - void encode(const Event&,QStringList&) const; - -private: - enum DataIndex {TypeIndex=0,NameIndex=1,ValueIndex=2}; -}; - -class VEventAttr : public VAttribute -{ -public: - VEventAttr(VNode *parent,const Event&,int index); - - VAttributeType* type() const; - QStringList data(bool firstLine) const; - std::string strName() const; - - static void scan(VNode* vnode,std::vector& vec); -}; - -#endif // VEVENT_HPP diff -Nru ecflow-4.9.0/Viewer/src/VFile.cpp ecflow-4.11.1/Viewer/src/VFile.cpp --- ecflow-4.9.0/Viewer/src/VFile.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFile.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,299 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "DirectoryHandler.hpp" -#include "VFile.hpp" -#include "UiLog.hpp" - -#include - -const size_t VFile::maxDataSize_=1024*1024*10; - -VFile::VFile(const std::string& name,const std::string& str,bool deleteFile) : - path_(name), - deleteFile_(deleteFile), - storageMode_(DiskStorage), - data_(0), - dataSize_(0), - fp_(0), - fetchMode_(NoFetchMode), - transferDuration_(0), - truncatedTo_(0), - cached_(false) -{ - std::ofstream f(path_.c_str()); - if(f.is_open()) - { - f << str; - f.close(); - } -} - -VFile::VFile(const std::string& name,bool deleteFile) : - path_(name), - deleteFile_(deleteFile), - storageMode_(DiskStorage), - data_(0), - dataSize_(0), - fp_(0), - fetchMode_(NoFetchMode), - transferDuration_(0), - truncatedTo_(0), - cached_(false) -{ -} - -VFile::VFile(bool deleteFile) : - path_(""), - deleteFile_(deleteFile), - storageMode_(MemoryStorage), - data_(0), - dataSize_(0), - fp_(0), - fetchMode_(NoFetchMode), - transferDuration_(0), - truncatedTo_(0), - cached_(false) -{ -} - -VFile::~VFile() -{ - close(); - - UiLog().dbg() << "VFile::~VFile -->"; - print(); - - if(data_) - { - delete [] data_; - UiLog().dbg() << " memory released"; - } - - //TODO: add further/better checks - if(deleteFile_ && - exists() && !path_.empty() && path_ != "/" && path_.size() > 4) - { - unlink(path_.c_str()); - UiLog().dbg() << " file deleted from disk"; - } - else if(!path_.empty() && exists()) - { - UiLog().dbg() << " file was kept on disk"; - } - - UiLog().dbg() << "<-- ~VFile"; -} - -bool VFile::exists() const -{ - if(path_.empty()) - return false; - return (access(path_.c_str(), R_OK) ==0); -} - -VFile_ptr VFile::create(const std::string& path,const std::string& str,bool deleteFile) -{ - return VFile_ptr(new VFile(path,str,deleteFile)); -} - -VFile_ptr VFile::create(const std::string& path,bool deleteFile) -{ - return VFile_ptr(new VFile(path,deleteFile)); -} - -VFile_ptr VFile::create(bool deleteFile) -{ - return VFile_ptr(new VFile(deleteFile)); -} - -VFile_ptr VFile::createTmpFile(bool deleteFile) -{ - std::string tmpFile=DirectoryHandler::tmpFileName(); - std::ofstream f(tmpFile.c_str()); - if(f.is_open()) - { - f.close(); - } - - return VFile_ptr(new VFile(tmpFile,deleteFile)); -} - -void VFile::setStorageMode(StorageMode mode) -{ - if(storageMode_ == mode) - return; - - storageMode_=mode; - - if(storageMode_== DiskStorage) - { - if(dataSize_ > 0) - { - if(path_.empty()) - path_=DirectoryHandler::tmpFileName(); - - fp_ = fopen(path_.c_str(),"w"); - if(fwrite(data_,1,dataSize_,fp_) != dataSize_) - { - - } - fclose(fp_); - fp_=NULL; - delete [] data_; - data_=0; - dataSize_=0; - } - } -} - -bool VFile::write(const std::string& buf,std::string& err) -{ - return write(buf.c_str(),buf.size(),err); -} - -bool VFile::write(const char *buf,size_t len,std::string& err) -{ - //printf("total:%d \n len: %d \n",dataSize_,len); - - //Keep data in memory - if(storageMode_ == MemoryStorage) - { - if(!data_) - { - data_ = new char[maxDataSize_+1]; - } - - if(dataSize_ + len < maxDataSize_) - { - memcpy(data_+dataSize_,buf,len); - dataSize_+=len; - data_[dataSize_] = '\0'; //terminate the string - return true; - } - else - { - setStorageMode(DiskStorage); - } - } - - //Write data to disk - if(storageMode_ == DiskStorage) - { - if(!fp_) - { - if(path_.empty()) - path_=DirectoryHandler::tmpFileName(); - - fp_ = fopen(path_.c_str(),"a"); - } - - if(fwrite(buf,1,len,fp_) != len) - { - //char buf_loc[2048]; - //sprintf(buf_loc,"Write error on %s",out->path().c_str()); - //gui::syserr(buf); - fclose(fp_); - return false; - } - fflush(fp_); - } - - return true; -} - -void VFile::close() -{ - if(fp_) - { - fclose(fp_); - fp_=NULL; - } - if(data_) - { - data_[dataSize_]='\0'; - dataSize_++; - } -} - -/* -std::string VFile::tmpName() -{ - std::string res; - -#if defined(linux) || defined(_AIX) - - char *path = getenv("SCRATCH"); - char *s = (char*) malloc(128); - - if (!path || !access(path, R_OK)) - path=getenv("TMPDIR"); - - if (!path || !access(path, R_OK)) - path=(char*)"/tmp"; - - snprintf(s, 128, "%s/%sXXXXXX", path, "ecflow_ui"); - if(mkstemp(s) != -1) - { - res=std::string(s); - } - - free(s); - -#else - -// char* s=std::string(tmpnam(NULL)); - res=std::string(tmpnam(NULL)); - -#endif - - return res; - -} -*/ - -bool VFile::isEmpty() const -{ - if(storageMode_ == VFile::MemoryStorage) - return dataSize_ == 0; - - if(exists()) - { - struct stat info; - return (::stat( path_.c_str(), &info ) != 0 || info.st_size == 0); - } - return false; -} - -void VFile::print() -{ - std::string str=" VFile contents --> storage:"; - if(storageMode_ == MemoryStorage) - { - str+="memory size:" + boost::lexical_cast(dataSize_); - } - else - { - str+="disk path: " + path_; - } - - UiLog().dbg() << str; -} - diff -Nru ecflow-4.9.0/Viewer/src/VFile.hpp ecflow-4.11.1/Viewer/src/VFile.hpp --- ecflow-4.9.0/Viewer/src/VFile.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFile.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,100 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VFILE_INC__ -#define VFILE_INC__ - -#include -#include - -#include -#include - -#include - -class VFile; -typedef boost::shared_ptr VFile_ptr; - -class VFile : public boost::enable_shared_from_this -{ -public: - virtual ~VFile(); - - enum StorageMode {MemoryStorage,DiskStorage}; - enum FetchMode {NoFetchMode,LocalFetchMode,ServerFetchMode,LogServerFetchMode}; - - const std::string& path() const {return path_;} - const std::string& sourcePath() const {return sourcePath_;} - void setSourcePath(const std::string& p) {sourcePath_=p;} - void setContents(const std::string); - bool exists() const; - bool isEmpty() const; - - StorageMode storageMode() const {return storageMode_;} - static const size_t maxDataSize() {return maxDataSize_;} - size_t dataSize() const {return dataSize_;} - const char* data() const {return data_;} - - void setTransferDuration(unsigned int d) {transferDuration_=d;} - unsigned int transferDuration() const {return transferDuration_;} - void setFetchDate(QDateTime d) {fetchDate_=d;} - QDateTime fetchDate() const {return fetchDate_;} - void setFetchMode(FetchMode m) {fetchMode_=m;} - FetchMode fetchMode() const {return fetchMode_;} - void setFetchModeStr(const std::string& fetchMethod) {fetchModeStr_=fetchMethod;} - const std::string& fetchModeStr() const {return fetchModeStr_;} - int truncatedTo() const {return truncatedTo_;} - void setTruncatedTo(int t) {truncatedTo_=t;} - void setCached(bool b) {cached_=b;} - bool cached() const {return cached_;} - void setLog(const std::vector& log) {log_=log;} - void addToLog(const std::string& s) {log_.push_back(s);} - const std::vector& log() const {return log_;} - - bool write(const char *buf,size_t len,std::string& err); - bool write(const std::string& buf,std::string& err); - - void close(); - void print(); - - static VFile_ptr create(const std::string& path,const std::string& contents,bool deleteFile=true); - static VFile_ptr create(const std::string& path,bool deleteFile= true); - static VFile_ptr create(bool deleteFile= true); - static VFile_ptr createTmpFile(bool deleteFile= true); - - //static std::string tmpName(); - -protected: - VFile(const std::string& name,const std::string& str,bool deleteFile=true); - VFile(const std::string& str,bool deleteFile= true); - explicit VFile(bool deleteFile= true); - void setStorageMode(StorageMode); - - std::string path_; - std::string sourcePath_; - bool deleteFile_; - - StorageMode storageMode_; - static const size_t maxDataSize_; - char* data_; - size_t dataSize_; - FILE* fp_; - - FetchMode fetchMode_; - std::string fetchModeStr_; - QDateTime fetchDate_; - unsigned int transferDuration_; - int truncatedTo_; - - bool cached_; - std::vector log_; - -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VFileInfo.cpp ecflow-4.11.1/Viewer/src/VFileInfo.cpp --- ecflow-4.9.0/Viewer/src/VFileInfo.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFileInfo.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,107 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VFileInfo.hpp" - -#include -#include - -QString VFileInfo::formatSize() const -{ - return formatSize(size()); -} - -QString VFileInfo::formatModDate() const -{ - QDateTime dt=lastModified(); - return dt.toString("yyyy-MM-dd hh:mm:ss"); -} - -QString VFileInfo::formatPermissions() const -{ - QString str(permission(QFile::ReadOwner)?"r":"-"); - str+=(permission(QFile::WriteOwner)?"w":"-"); - str+=(permission(QFile::ExeOwner)?"x":"-"); - str+=(permission(QFile::ReadGroup)?"r":"-"); - str+=(permission(QFile::WriteGroup)?"w":"-"); - str+=(permission(QFile::ExeGroup)?"x":"-"); - str+=(permission(QFile::ReadOther)?"r":"-"); - str+=(permission(QFile::WriteOther)?"w":"-"); - str+=(permission(QFile::ExeOther)?"x":"-"); - - return str; -} - -QString VFileInfo::formatSize(unsigned int size) -{ - if(size < 1024) - return QString::number(size) + " B"; - else if(size < 1024*1024) - return QString::number(size/1024) + " KB"; - else if(size < 1024*1024*1024) - return QString::number(size/(1024*1024)) + " MB"; - else - return QString::number(size/(1024*1024*1024)) + " GB"; - - return QString(); -} - -QString VFileInfo::formatDate(const std::time_t& t) -{ - QDateTime dt=QDateTime::fromTime_t(t); - return dt.toString("yyyy-MM-dd hh:mm:ss"); -} - -QString VFileInfo::formatDateAgo(const std::time_t& t) -{ - QString str=QObject::tr("Right now"); - - time_t now = time(0); - - int delta = now - t; - if(delta<0) delta = 0; - - if( t== 0) - { - return QObject::tr("never"); - } - - if(delta ==1) - str=QObject::tr("1 second ago"); - - else if(delta >=1 && delta < 60) - { - str=QString::number(delta) + QObject::tr(" second") + ((delta==1)?"":"s") + QObject::tr(" ago"); - } - - else if(delta >= 60 && delta < 60*60) - { - int val=delta/60; - str=QString::number(val) + QObject::tr(" minute") + ((val==1)?"":"s") + QObject::tr(" ago"); - } - - else if(delta >= 60*60 && delta < 60*60*24) - { - int val=delta/(60*60); - str=QString::number(val) + QObject::tr(" hour") + ((val==1)?"":"s") + QObject::tr(" ago"); - } - - else if(delta >= 60*60*24) - { - int val=delta/(60*60*24); - str=QString::number(val) + QObject::tr(" day") + ((val==1)?"":"s") + QObject::tr(" ago"); - } - - return str; -} - - - - - diff -Nru ecflow-4.9.0/Viewer/src/VFileInfo.hpp ecflow-4.11.1/Viewer/src/VFileInfo.hpp --- ecflow-4.9.0/Viewer/src/VFileInfo.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFileInfo.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VFILEINFO_H_ -#define VFILEINFO_H_ - -#include - -#include - -class VFileInfo : public QFileInfo -{ -public: - explicit VFileInfo(const QString& file) : QFileInfo(file) {} - - QString formatSize() const; - QString formatModDate() const; - QString formatPermissions() const; - - static QString formatSize(unsigned int size); - static QString formatDate(const std::time_t& t); - static QString formatDateAgo(const std::time_t& t); -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VFilter.cpp ecflow-4.11.1/Viewer/src/VFilter.cpp --- ecflow-4.9.0/Viewer/src/VFilter.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFilter.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,803 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#include "VFilter.hpp" - -#include "NodeQuery.hpp" -#include "NodeQueryEngine.hpp" -#include "UIDebug.hpp" -#include "UiLog.hpp" -#include "VNState.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VIcon.hpp" -#include "VNode.hpp" -#include "VParam.hpp" -#include "VSettings.hpp" -#include "VTree.hpp" - -#include "ServerFilter.hpp" -#include "ServerHandler.hpp" - -#include -#include - -#include - -#define _UI_VFILTER_DEBUG - -//============================================== -// -// VFilter -// -//============================================== - -VParamSet::VParamSet() : empty_(true), complete_(false) -{ -} - -void VParamSet::init(const std::vector& items) -{ - all_=items; - - int maxId=0; - for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - if(static_cast((*it)->id()) > maxId) - maxId=(*it)->id(); - } - - if(maxId > 0) - { - currentCache_.resize(maxId+1,0); - for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - currentCache_[(*it)->id()]=1; - } - } - - setCurrent(all_,false); -} - -//This has to be very fast. Called a millions of times! -bool VParamSet::isSet(VParam* p) const -{ - //assert(p); - //return (current_.find(p) != current_.end()); - //return std::find(current_.begin(),current_.end(),p) != current_.end(); - - //for(size_t i=0; i < current_.size(); i++) - // if(current_[i] == p) - // return true; - - //return true; - return (currentCache_[p->id()]==0)?false:true; - - //return false; -} - -bool VParamSet::isSet(const std::string &name) const -{ - for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) - { - if((*it)->strName() == name) - return true; - } - return false; -} - -QStringList VParamSet::currentAsList() const -{ - QStringList lst; - for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) - { - lst << QString::fromStdString((*it)->strName()); - } - return lst; -} - -void VParamSet::clearCurrent() -{ - current_.clear(); - std::fill(currentCache_.begin(), currentCache_.end(), 0); - empty_=true; - complete_=false; -} - -void VParamSet::addToCurrent(VParam* p) -{ - current_.push_back(p); - uint id=p->id(); - UI_ASSERT(id >=0 && id < currentCache_.size(),"id=" << id - << " currentCache_.size()=" << currentCache_.size()); - currentCache_[id]=1; - empty_=false; - complete_=(current_.size() == all_.size()); -} - -void VParamSet::setCurrent(const std::vector& items,bool broadcast) -{ - clearCurrent(); - - for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - if(std::find(all_.begin(),all_.end(),*it) != all_.end()) - { - addToCurrent(*it); - } - } - - if(broadcast) - Q_EMIT changed(); -} - -void VParamSet::setCurrent(const std::vector& names,bool broadcast) -{ - clearCurrent(); - - for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - if(std::find(names.begin(),names.end(),(*it)->strName()) != names.end()) - { - addToCurrent(*it); - } - } - - if(broadcast) - Q_EMIT changed(); -} - -void VParamSet::setCurrent(QStringList names,bool broadcast) -{ - clearCurrent(); - - for(std::vector::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - if(names.contains(QString::fromStdString((*it)->strName()))) - { - addToCurrent(*it); - } - } - - if(broadcast) - Q_EMIT changed(); -} - - -void VParamSet::writeSettings(VSettings *vs) -{ - std::vector array; - - if(isComplete()) - { - array.push_back("_ALL_"); - } - else - { - for(std::vector::const_iterator it=current_.begin(); it != current_.end(); ++it) - { - array.push_back((*it)->strName()); - } - } - - vs->put(settingsId_,array); -} - -void VParamSet::readSettings(VSettings* vs) -{ - clearCurrent(); - - std::vector array; - - //Try to read the old version (aka V0) of the settings - //In this case an empty list means all is selected! - if(vs->contains(settingsIdV0_)) - { - vs->get(settingsIdV0_,array); - if(array.empty()) - { - setCurrent(all_,false); - return; - } - } - //otherwise read the standard version - else - { - vs->get(settingsId_,array); - } - - for(std::vector::const_iterator it = array.begin(); it != array.end(); ++it) - { - std::string name=*it; - if(name == "_ALL_") - { - setCurrent(all_,false); - return; - } - - for(std::vector::const_iterator itA=all_.begin(); itA != all_.end(); ++itA) - { - if((*itA)->strName() == name) - addToCurrent(*itA); - } - } -} - -//============================================== -// -// StateFilter -// -//============================================== - -NodeStateFilter::NodeStateFilter() : VParamSet() -{ - settingsId_="states"; - settingsIdV0_="state"; - std::vector v=VNState::filterItems(); - init(v); -} - - -//============================================== -// -// AttributeFilter -// -//============================================== - -AttributeFilter::AttributeFilter() : VParamSet() -{ - settingsId_="attributes"; - settingsIdV0_="attribute"; - std::vector v=VAttributeType::filterItems(); - init(v); - - /*for(std::set::const_iterator it=all_.begin(); it != all_.end(); ++it) - { - if((*it)->strName() != "var" && (*it)->strName() != "genvar") - current_.insert(*it); - }*/ -} - -bool AttributeFilter::matchForceShowAttr(const VNode *n,VAttributeType* t) const -{ - if(forceShowAttr_) - { - if(VAttribute *a=forceShowAttr_->attribute()) - return (a->parent() == n && a->type() == t); - } - return false; -} - -void AttributeFilter::setForceShowAttr(VAttribute* a) -{ - forceShowAttr_=VInfoAttribute::create(a); -} - -VAttribute* AttributeFilter::forceShowAttr() const -{ - return (forceShowAttr_)?(forceShowAttr_->attribute()):0; -} - -void AttributeFilter::clearForceShowAttr() -{ - forceShowAttr_.reset(); -} - -void AttributeFilter::updateForceShowAttr() -{ - if(forceShowAttr_) - { - forceShowAttr_->regainData(); - if(forceShowAttr_->hasData()) - { - forceShowAttr_.reset(); - } - } -} - -//============================================== -// -// IconFilter -// -//============================================== - -IconFilter::IconFilter() : VParamSet() -{ - settingsId_="icons"; - settingsIdV0_="icon"; - std::vector v=VIcon::filterItems(); - init(v); -} - -void IconFilter::readSettings(VSettings* vs) -{ - VParamSet::readSettings(vs); - - //If the filter list is not complete we need to be sure that a newly added icon type - //is automatically enabled in the filter. This is based on the contents of the lastNames icon - //file. This file is updated on exit and stores the full list of icon names (existing at exit). - //So we can figure out if a new icon type were introduced since the last startup and we can - //guarantee that is is always enabled for the first time. - if(!isComplete()) - { - const std::vector& lastNames=VIcon::lastNames(); - - //The lastNames are not found. This must be the first startup after lastNames concept were introduced - //or it is a fresh startup after cleaning the config (or the very first one). We enable all the icons. - //It could be only a one-time problem for users who already set theit icon filter. - if(lastNames.empty()) - { - setCurrent(all_,false); - } - else - { - //Check which icons are not in lastNames - for(std::vector::const_iterator itA=all_.begin(); itA != all_.end(); ++itA) - { - //The item is not in lastNames so it must be a newly added icon type. We add it to the filter list - if(std::find(lastNames.begin(),lastNames.end(),(*itA)->strName()) == lastNames.end()) - { - addToCurrent(*itA); - } - } - } - } -} - -//============================================== -// -// NodeFilter -// -//============================================== - -NodeFilterDef::NodeFilterDef(ServerFilter* serverFilter,Scope scope) : - serverFilter_(serverFilter), - nodeState_(0) -{ - nodeState_=new NodeStateFilter; - - //if(scope == NodeStateScope) - // nodeState_=new NodeStateFilter; - - //else if(scope == GeneralScope) - // nodeState_=new NodeStateFilter; - - if(nodeState_) - { - exprStr_="state = all"; - - connect(nodeState_,SIGNAL(changed()), - this,SIGNAL(changed())); - } - - query_=new NodeQuery("tmp",true); - //QStringList sel("aborted"); - //query_->setStateSelection(sel); -} - -NodeFilterDef::~NodeFilterDef() -{ - delete query_; -} - -NodeQuery* NodeFilterDef::query() const -{ - return query_; -} - -void NodeFilterDef::setQuery(NodeQuery* q) -{ - query_->swap(q); - Q_EMIT changed(); -} - -void NodeFilterDef::writeSettings(VSettings *vs) -{ - vs->beginGroup("query"); - query_->save(vs); - vs->endGroup(); -} - -void NodeFilterDef::readSettings(VSettings *vs) -{ - vs->beginGroup("query"); - query_->load(vs); - vs->endGroup(); - - Q_EMIT changed(); -} - -NodeFilter::NodeFilter(NodeFilterDef* def,ServerHandler* server) : - def_(def), - matchMode_(VectorMatch), - server_(server), - forceShowNode_(0) -{ - assert(server_); - - queryEngine_=new NodeFilterEngine(this); -} - -NodeFilter::~NodeFilter() -{ - delete queryEngine_; -} - -void NodeFilter::clear() -{ - clearForceShowNode(); -} - -void NodeFilter::setForceShowNode(VNode* n) -{ - forceShowNode_=n; -#ifdef _UI_VFILTER_DEBUG - if(forceShowNode_) - UiLog(server_).dbg() << "NodeFilter::setForceShowNode --> " << forceShowNode_->absNodePath(); -#endif -} - -void NodeFilter::clearForceShowNode() -{ - forceShowNode_=0; -} - -//============================================ -// -// TreeNodeFilter -// -//============================================ - -TreeNodeFilter::TreeNodeFilter(NodeFilterDef* def,ServerHandler* server,VTree* tree) : - NodeFilter(def,server), - tree_(tree) -{ -} - -void TreeNodeFilter::clear() -{ - NodeFilter::clear(); - match_=std::vector(); -} - -bool TreeNodeFilter::isNull() -{ - //return def_->nodeState_->isComplete() || def_->nodeState_->isEmpty(); - return def_->nodeState_->isEmpty(); -} - -bool TreeNodeFilter::isComplete() -{ - return def_->nodeState_->isComplete(); -} - -// -bool TreeNodeFilter::update(const std::vector& topChange,std::vector& topFilterChange) -{ -#ifdef _UI_VFILTER_DEBUG - UI_FUNCTION_LOG_S(server_); -#endif - - //nodes_.clear(); - - //If all states are hidden or visible - if(def_->nodeState_->isComplete() || def_->nodeState_->isEmpty()) - { - //deallocate the match vector - match_=std::vector(); - //assert(match_.capacity() == 0); -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " no filter is defined!"; -#endif - return false; - } - -#ifdef _UI_VFILTER_DEBUG - QTime timer; - timer.start(); -#endif - - VServer* root=server_->vRoot(); - if(root->totalNum() > 0) - { - bool fullRun=false; - - //The number of nodes changed: we need to rerun everything - if(static_cast(match_.size()) != root->totalNum() || match_.size() != tree_->nodeVec().size()) - { - //Deallocates the match vector - match_=std::vector(); - //match_.reserve(root->totalNum()); - VNode *n=0; - match_.resize(root->totalNum(),n); - //td::fill(match_.begin(), match_.end(), n); - fullRun=true; - } - - //The topchange vector is empty: it can only happen when we need to rerun everything - else if(topChange.empty()) - { - VNode *n=0; - //match_.clear(); - std::fill(match_.begin(), match_.end(), n); - fullRun=true; - } - - //We rerun everything - if(fullRun) - { - for(int i=0; i < root->numOfChildren(); i++) - { - filterState(root->childAt(i),def_->nodeState_); - } - } - - //We only check the branches defined by the nodes in topChange - else - { - //At this point the tree_->nodeVec() and match must have the same content - assert(tree_->nodeVec().size() == match_.size()); - - //Update the filter results - for(size_t i=0; i < topChange.size(); i++) - { - filterState(topChange[i],def_->nodeState_); - } - -#ifdef _UI_VFILTER_DEBUG - int diffCnt=0; - for(size_t i=0; i < match_.size(); i++) - { - if(tree_->vnodeAt(i) != match_[i]) - diffCnt++; - } - UiLog(server_).dbg() << " number of differences in filter: " << diffCnt; -#endif - - //We collect the topmost nodes with changes. It could be different to - //topChange so we need this step! - for(size_t i=0; i < topChange.size(); i++) - { - assert(topChange[i]->isSuite()); - collectTopFilterChange(topChange[i],topFilterChange); - } - -#ifdef _UI_VFILTER_DEBUG - assert(static_cast(topFilterChange.size()) <= diffCnt); - if(diffCnt > 0) - assert(topFilterChange.size() >0); -#endif - } - -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " top level nodes that changed in filter:"; - for(size_t i= 0; i < topFilterChange.size(); i++) - UiLog(server_).dbg() << " " << topFilterChange.at(i)->strName(); -#endif - - } - else - { - match_.clear(); - } - -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " elapsed time: " << timer.elapsed() << " ms"; - UiLog(server_).dbg() << " filter size: " << match_.size(); - UiLog(server_).dbg() << " capacity:" << match_.capacity(); -#endif - - return true; -} - -bool TreeNodeFilter::update() -{ - std::vector topChange; - std::vector topFilterChange; - return update(topChange,topFilterChange); -} - -//Finds the top level nodes whose filter status changed -bool TreeNodeFilter::collectTopFilterChange(VNode* node,std::vector& topFilterChange) -{ - int idx=node->index(); - if(tree_->vnodeAt(idx) != match_[idx]) - { - topFilterChange.push_back(node); - return true; - } - - for(int i=0; i < node->numOfChildren(); i++) - { - if(collectTopFilterChange(node->childAt(i),topFilterChange)) - { - break; - } - } - - return false; -} - -bool TreeNodeFilter::filterState(VNode* node,VParamSet* stateFilter) -{ - bool ok=false; - - if(stateFilter->isSet(VNState::toState(node)) || forceShowNode_ == node) - { - ok=true; - } - - for(int i=0; i < node->numOfChildren(); i++) - { - if(filterState(node->childAt(i),stateFilter) == true && ok == false) - { - ok=true; - } - } - - if(ok) - { - match_[node->index()]=node; - } - else - { - match_[node->index()]=NULL; - } - - return ok; -} - -//=========================================================== -// -// TableNodeFilter -// -//=========================================================== - -TableNodeFilter::TableNodeFilter(NodeFilterDef* def,ServerHandler* server) : - NodeFilter(def,server), - matchCount_(0) -{ -} - -//When nothing should be shown -bool TableNodeFilter::isNull() -{ - return false; - //return def_->nodeState_->isComplete(); - //return def_->nodeState_->isNull(); - -} - -//When everything should be shown -bool TableNodeFilter::isComplete() -{ - return false; - //return def_->nodeState_->isComplete(); -} - -void TableNodeFilter::clear() -{ - NodeFilter::clear(); - match_.clear(); - index_.clear(); - matchCount_=0; -} - -int TableNodeFilter::indexOf(const VNode* node) const -{ - switch(matchMode_) - { - case VectorMatch: - return index_[node->index()]; - case AllMatch: - return node->index(); - case NoneMatch: - return -1; - default: - assert(0); - return -1; - } - - return -1; -} - -VNode* TableNodeFilter::nodeAt(int index) const -{ - switch(matchMode_) - { - case VectorMatch: - return match_[index]; - case AllMatch: - return server_->vRoot()->nodeAt(index); - case NoneMatch: - return NULL; - default: - assert(0); - return NULL; - } - - return NULL; -} - -bool TableNodeFilter::update() -{ -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << "TableNodeFilter::update -->"; -#endif - - NodeQuery* q=def_->query_; - Q_ASSERT(q); - if(!q->hasServer(server_->name()) || server_->vRoot()->totalNum() ==0) - { - matchMode_=NoneMatch; - //Deallocates - match_=std::vector(); - index_=std::vector(); - matchCount_=0; -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " no nodes are filtered!"; -#endif - return true; - } - - if(q->query().isEmpty() && q->rootNode().empty()) - { - matchMode_=AllMatch; - //Deallocates - match_=std::vector(); - index_=std::vector(); - matchCount_=server_->vRoot()->totalNum(); -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " all the nodes are filtered!"; -#endif - return true; - } - -#ifdef _UI_VFILTER_DEBUG - QTime timer; - timer.start(); -#endif - - matchMode_=VectorMatch; - match_.clear(); - int num=server_->vRoot()->totalNum(); - if(num != static_cast(index_.size())) - { - //Reallocates - index_=std::vector(); - index_.resize(num,-1); - } - else - { - std::fill(index_.begin(), index_.end(), -1); - } - - queryEngine_->setQuery(def_->query_); - if(queryEngine_->runQuery(server_)) - { - matchCount_=match_.size(); - for(size_t i=0; i < match_.size(); i++) - { - index_[match_[i]->index()]=i; - } - } - else - { - std::fill(index_.begin(),index_.end(),-1); - } - -#ifdef _UI_VFILTER_DEBUG - UiLog(server_).dbg() << " elapsed time: " << timer.elapsed() << " ms"; - UiLog(server_).dbg() << " filter size: " << match_.size(); - UiLog(server_).dbg() << " capacity: " << match_.capacity(); -#endif - - return true; -} - diff -Nru ecflow-4.9.0/Viewer/src/VFilter.hpp ecflow-4.11.1/Viewer/src/VFilter.hpp --- ecflow-4.9.0/Viewer/src/VFilter.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VFilter.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,218 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -//============================================================================ - -#ifndef VIEWFILTER_HPP_ -#define VIEWFILTER_HPP_ - -#include -#include - -#include -#include - -#include "VInfo.hpp" -#include "VParam.hpp" - -#include "Node.hpp" - -class NodeQuery; -class NodeFilterEngine; -class ServerFilter; -class ServerHandler; -class VAttribute; -class VAttributeType; -class VInfo; -class VNode; -class VSettings; -class VTree; - -#include - -class VParamSet : public QObject -{ -Q_OBJECT - -public: - VParamSet(); - virtual ~VParamSet() {} - - const std::vector& all() const {return all_;} - const std::vector& current() const {return current_;} - QStringList currentAsList() const; - void setCurrent(const std::vector&,bool broadcast=true); - void setCurrent(const std::vector&,bool broadcast=true); - void setCurrent(QStringList,bool broadcast=true); - - bool isEmpty() const {return empty_;} - bool isComplete() const { return complete_;} - bool isSet(const std::string&) const; - bool isSet(VParam*) const; - - void writeSettings(VSettings* vs); - void virtual readSettings(VSettings* vs); - -Q_SIGNALS: - void changed(); - -protected: - void init(const std::vector& items); - void addToCurrent(VParam*); - - std::vector all_; - std::vector current_; - std::string settingsId_; - std::string settingsIdV0_; - -private: - void clearCurrent(); - - std::vector currentCache_; //we use to speed up the check in isSet() - bool empty_; - bool complete_; -}; - -class NodeStateFilter : public VParamSet -{ -public: - NodeStateFilter(); -}; - -class AttributeFilter : public VParamSet -{ -public: - AttributeFilter(); - bool matchForceShowAttr(const VNode*,VAttributeType*) const; - void setForceShowAttr(VAttribute* a); - void clearForceShowAttr(); - void updateForceShowAttr(); - VAttribute* forceShowAttr() const; - -private: - VInfo_ptr forceShowAttr_; -}; - -class IconFilter : public VParamSet -{ -public: - IconFilter(); - void readSettings(VSettings* vs); -}; - - -class TreeNodeFilter; -class TableNodeFilter; - -class NodeFilterDef : public QObject -{ -Q_OBJECT - -friend class TreeNodeFilter; -friend class TableNodeFilter; - -public: - enum Scope {NodeStateScope,GeneralScope}; - NodeFilterDef(ServerFilter*,Scope); - ~NodeFilterDef(); - - NodeStateFilter* nodeState() const {return nodeState_;} - - const std::string& exprStr() const {return exprStr_;} - NodeQuery* query() const; - void setQuery(NodeQuery*); - - void writeSettings(VSettings *vs); - void readSettings(VSettings *vs); - -Q_SIGNALS: - void changed(); - -protected: - ServerFilter *serverFilter_; - std::string exprStr_; - NodeStateFilter *nodeState_; - std::string nodePath_; - std::string nodeType_; - NodeQuery* query_; - - //AttributeFilter *attribute_; - //std::string nodeType_; - //std::string nodeName_; -}; - - -class NodeFilter -{ - friend class NodeFilterEngine; - friend class VTreeServer; - -public: - enum MatchMode {NoneMatch,AllMatch,VectorMatch}; - - NodeFilter(NodeFilterDef* def,ServerHandler*); - virtual ~NodeFilter(); - - virtual void clear(); - virtual bool isNull()=0; - virtual bool isComplete()=0; - virtual int matchCount() const = 0; - virtual bool update()=0; - - VNode* forceShowNode() const {return forceShowNode_;} - void setForceShowNode(VNode*); - void clearForceShowNode(); - -protected: - NodeFilterDef* def_; - NodeFilterEngine* queryEngine_; - std::set type_; - MatchMode matchMode_; - std::vector match_; - ServerHandler * server_; - VNode* forceShowNode_; -}; - -class TreeNodeFilter : public NodeFilter -{ -public: - explicit TreeNodeFilter(NodeFilterDef* def,ServerHandler*,VTree*); - - void clear(); - bool isNull(); - bool isComplete(); - int matchCount() const {return 0;} - bool update(); - bool update(const std::vector& topChange, - std::vector& topFilterChange); - -private: - bool filterState(VNode* node,VParamSet* stateFilter); - bool collectTopFilterChange(VNode* n,std::vector& topFilterChange); - - VTree* tree_; -}; - -class TableNodeFilter : public NodeFilter -{ -public: - explicit TableNodeFilter(NodeFilterDef* def,ServerHandler*); - - void clear(); - bool isNull(); - bool isComplete(); - int matchCount() const {return matchCount_;} - bool update(); - int indexOf(const VNode*) const; - VNode* nodeAt(int index) const; - -private: - std::vector index_; - int matchCount_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/VGenVarAttr.cpp ecflow-4.11.1/Viewer/src/VGenVarAttr.cpp --- ecflow-4.9.0/Viewer/src/VGenVarAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VGenVarAttr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VGenVarAttr.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -#include "NodeAttr.hpp" - -//================================ -// VGenVarAttrType -//================================ - -VGenVarAttrType::VGenVarAttrType() : VAttributeType("genvar") -{ - dataCount_=3; - searchKeyToData_["var_name"]=NameIndex; - searchKeyToData_["var_value"]=ValueIndex; - searchKeyToData_["var_type"]=TypeIndex; - searchKeyToData_["name"]=NameIndex; - scanProc_=VGenVarAttr::scan; -} - -QString VGenVarAttrType::toolTip(QStringList d) const -{ - QString t="Type: User variable
      "; - if(d.count() == dataCount_) - { - t+="Name: " + d[NameIndex] + "
      "; - QString s=d[ValueIndex]; - if(s.size() > 150) s=s.left(150) + "..."; - t+="Value: " + s; - } - return t; -} - -void VGenVarAttrType::encode(const Variable& v,QStringList& data) const -{ - data << qName_ << - QString::fromStdString(v.name()) << - QString::fromStdString(v.theValue()); -} - -//===================================================== -// -// VGenVarAttr -// -//===================================================== - -VGenVarAttr::VGenVarAttr(VNode *parent,const Variable& v, int index) : VAttribute(parent,index) -{ - //name_=v.name(); -} - -VAttributeType* VGenVarAttr::type() const -{ - static VAttributeType* atype=VAttributeType::find("genvar"); - return atype; -} - -QStringList VGenVarAttr::data(bool /*firstLine*/) const -{ - static VGenVarAttrType* atype=static_cast(type()); - QStringList s; - if(parent_->isServer() == 0) - { - std::vector v; - parent_->genVariables(v); - atype->encode(v[index_],s); - } - return s; -} - -std::string VGenVarAttr::strName() const -{ - if(parent_->isServer() == 0) - { - std::vector v; - parent_->genVariables(v); - return v[index_].name(); - } - return std::string(); -} - -void VGenVarAttr::scan(VNode* vnode,std::vector& vec) -{ - if(vnode->isServer() == 0) - { - std::vector v; - vnode->genVariables(v); - int n=static_cast(v.size()); - for(int i=0; i < n; i++) - { - vec.push_back(new VGenVarAttr(vnode,v[i],i)); - } - } -} - -bool VGenVarAttr::isReadOnly(const std::string& varName) -{ - static QStringList readOnlyVars=QStringList() << - "ECF_NODE" << "ECF_HOST" << "ECF_PORT" << "ECF_PID" << - "ECF_VERSION" << "ECF_LISTS"; - return readOnlyVars.contains(QString::fromStdString(varName)); -} diff -Nru ecflow-4.9.0/Viewer/src/VGenVarAttr.hpp ecflow-4.11.1/Viewer/src/VGenVarAttr.hpp --- ecflow-4.9.0/Viewer/src/VGenVarAttr.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VGenVarAttr.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VGENVARATTR_HPP -#define VGENVARATTR_HPP - -#include "VAttribute.hpp" -#include "VAttributeType.hpp" - -#include -#include -#include - -class AttributeFilter; -class VAttributeType; -class VNode; -class Variable; - -class VGenVarAttrType : public VAttributeType -{ -public: - explicit VGenVarAttrType(); - QString toolTip(QStringList d) const; - void encode(const Variable&,QStringList&) const; - -private: - enum DataIndex {TypeIndex=0,NameIndex=1,ValueIndex=2}; -}; - -class VGenVarAttr : public VAttribute -{ -public: - VGenVarAttr(VNode *parent,const Variable&,int index); - - VAttributeType* type() const; - QStringList data(bool firstLine) const; - std::string strName() const; - static void scan(VNode* vnode,std::vector& vec); - static bool isReadOnly(const std::string&); -}; - -#endif // VGENVARATTR_HPP - diff -Nru ecflow-4.9.0/Viewer/src/VIcon.cpp ecflow-4.11.1/Viewer/src/VIcon.cpp --- ecflow-4.9.0/Viewer/src/VIcon.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VIcon.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,520 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VIcon.hpp" - -#include -#include -#include - -#include -#include -#include -#include - -#include "ExprAst.hpp" -#include "Submittable.hpp" - -#include "DirectoryHandler.hpp" -#include "IconProvider.hpp" -#include "UserMessage.hpp" -#include "VConfigLoader.hpp" -#include "VFilter.hpp" -#include "VNode.hpp" -#include "VProperty.hpp" -#include "VConfig.hpp" -#include "VSettings.hpp" - -std::map VIcon::items_; -std::vector VIcon::itemsVec_; -std::vector VIcon::lastNames_; - -//========================================================== -// -// Define VIcon subclasses implementing the various icons. -// These do not need to be visible outside the class so we -// define them here in the cpp file. -// -//========================================================== - -class VWaitIcon : public VIcon -{ -public: - explicit VWaitIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VRerunIcon : public VIcon -{ -public: - explicit VRerunIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VNodeLogIcon : public VIcon -{ -public: - explicit VNodeLogIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VCompleteIcon : public VIcon -{ -public: - explicit VCompleteIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VTimeIcon : public VIcon -{ -public: - explicit VTimeIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VDateIcon : public VIcon -{ -public: - explicit VDateIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VZombieIcon : public VIcon -{ -public: - explicit VZombieIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VLateIcon : public VIcon -{ -public: - explicit VLateIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VSlowIcon : public VIcon -{ -public: - explicit VSlowIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VKilledIcon : public VIcon -{ -public: - explicit VKilledIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -class VMigratedIcon : public VIcon -{ -public: - explicit VMigratedIcon(const std::string& name) : VIcon(name) {} - bool show(VNode*); -}; - -//========================================================== -// -// Create VIcon instances -// -//========================================================== - -//This also defines the order the icons will appear in the views -static VNodeLogIcon nodeLogIcon("message"); -static VRerunIcon rerunIcon("rerun"); -static VCompleteIcon completeIcon("complete"); -static VLateIcon lateIcon("late"); -static VTimeIcon timeIcon("time"); -static VDateIcon dateIcon("date"); -static VWaitIcon waitIcon("wait"); -static VZombieIcon zombieIcon("zombie"); -static VKilledIcon killedIcon("killed"); -static VSlowIcon slowIcon("slow"); -static VMigratedIcon migratedIcon("migrated"); - -//========================================================== -// -// The VIcon baseclass -// -//========================================================== - -VIcon::VIcon(const std::string& name) : - VParam(name), - pixId_(-1) -{ - items_[name]=this; - itemsVec_.push_back(this); -} - -VIcon::~VIcon() -{ -} - -void VIcon::initPixmap() -{ - if(!prop_) - { - UserMessage::message(UserMessage::WARN, true, - std::string("Warning! VIcon::initPixmap() unable to create icon image for: " + strName())); - return; - } - - //Add icon to iconprovider - if(VProperty* ip=prop_->findChild("icon")) - { - pixId_=IconProvider::add(":/viewer/" + ip->value().toString(),name()); - } -} - -QPixmap VIcon::pixmap(int size) -{ - return IconProvider::pixmap(name(),size); -} - -//=============================================================== -// -// Static methods -// -//=============================================================== - -std::vector VIcon::filterItems() -{ - std::vector v; - for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) - { - v.push_back(it->second); - } - - return v; -} - -VIcon* VIcon::find(const std::string& name) -{ - std::map::const_iterator it=items_.find(name); - if(it != items_.end()) - return it->second; - - return NULL; -} - -//Create the pixmap containing all the relevant icons for the given node according to the filter. -QVariantList VIcon::pixmapList(VNode *vnode,VParamSet *filter) -{ - QVariantList lst; - if(!vnode) - return lst; - - for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) - { - VIcon *v=*it; - - if(!filter || filter->isSet(v)) - { - if(v->show(vnode)) - { - lst << v->pixId_; - } - } - } - - return lst; -} - -//Create the pixmap containing all the relevant icons for the given node according to the filter. -int VIcon::pixmapNum(VNode *vnode,VParamSet *filter) -{ - if(!vnode) - return 0; - - int ret=0; - - for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) - { - VIcon *v=*it; - if(!filter || filter->isSet(v)) - { - if(v->show(vnode)) - { - ret++; - } - } - } - return ret; -} - - -QString VIcon::toolTip(VNode *vnode,VParamSet *filter) -{ - if(!filter || filter->isEmpty()) - return QString(); - - int iconSize=16; - QString txt; - - for(std::vector::const_iterator it=itemsVec_.begin(); it != itemsVec_.end(); ++it) - { - VIcon *v=*it; - - if(!filter || filter->isSet(v)) - { - if(v->show(vnode)) - { - if(txt.isEmpty()) - { - txt+="
      Icons:"; - } - - txt+=""; - } - } - } - - if(!txt.isEmpty()) - txt+="
      pixId_) + "\' width=\'" + - QString::number(iconSize) + "\' height=\'" + QString::number(iconSize) + "\'>" + v->shortDescription() + "
      "; - - return txt; -} - -QString VIcon::shortDescription() const -{ - QString v; - if(prop_) - v=prop_->param("shortDesc"); - - if(v.isEmpty()) - v=name(); - - return v; -} -void VIcon::names(std::vector& v) -{ - for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) - v.push_back(it->first); -} - -void VIcon::saveLastNames() -{ - lastNames_.clear(); - for(std::map::const_iterator it=items_.begin(); it != items_.end(); ++it) - lastNames_.push_back(it->first); - - std::string iconFile = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "last_icons.txt"); - VSettings vs(iconFile); - vs.clear(); - vs.put("icons",lastNames_); - vs.write(); -} - -void VIcon::initLastNames() -{ - //It has to be called only once - assert(lastNames_.empty()); - std::string iconFile = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "last_icons.txt"); - VSettings vs(iconFile); - if(vs.read(false)) - vs.get("icons",lastNames_); -} - -void VIcon::load(VProperty* group) -{ - Q_FOREACH(VProperty* p,group->children()) - { - if(VIcon* obj=VIcon::find(p->strName())) - { - obj->setProperty(p); - obj->initPixmap(); - } - } -} - -static SimpleLoader loader("icon"); - - -//========================================================== -// Wait -//========================================================== - -//Task only -bool VWaitIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - return n->isFlagSet(ecf::Flag::WAIT); -} - -//========================================================== -// Rerun -//========================================================== - -//Task only -bool VRerunIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - node_ptr node=n->node(); - if(!node.get()) return false; - - if(Submittable* s = node->isSubmittable()) - { - return (s->try_no() > 1); - } - - return false; -} - -//========================================================== -// Message -//========================================================== - -//Node and server -bool VNodeLogIcon::show(VNode *n) -{ - if(!n) - return false; - - return n->isFlagSet(ecf::Flag::MESSAGE); -} - -//========================================================== -// Complete -//========================================================== - -//Task only -bool VCompleteIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - if(!n->node()) - return false; - - node_ptr node=n->node(); - if(!node.get()) return false; - - if(n->isDefaultStateComplete()) - return true; - - if(AstTop* t = node->completeAst()) - { - if(t->evaluate()) - return true; - } - return false; -} - -//========================================================== -// Date -//========================================================== - -//Node only? -bool VDateIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - node_ptr node=n->node(); - - if(!node.get()) return false; - - return (node->days().size() > 0 || node->dates().size() > 0); -} - -//========================================================== -// Time -//========================================================== - -//Node only? -bool VTimeIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - node_ptr node=n->node(); - - if(node->timeVec().size() > 0 || - node->todayVec().size() > 0 || - node->crons().size() > 0) - { - //if(TimeDepAttrs *attr = node->get_time_dep_attrs()) - // return !attr->time_today_cron_is_free(); - return true; - - } - return false; -} - -//========================================================== -// Zombie -//========================================================== - -//Node only? -bool VZombieIcon::show(VNode *n) -{ - if(!n) - return false; - - return n->isFlagSet(ecf::Flag::ZOMBIE); -} - -//========================================================== -// Late -//========================================================== - -//Node and server -bool VLateIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - return n->isFlagSet(ecf::Flag::LATE); -} - -//========================================================== -// Slow -//========================================================== - -//Server only -bool VSlowIcon::show(VNode *n) -{ - if(!n || !n->isServer()) - return false; - - return n->isFlagSet(ecf::Flag::LATE); -} - -//========================================================== -// Killed -//========================================================== - -//Node only? -bool VKilledIcon::show(VNode *n) -{ - if(!n || n->isServer()) - return false; - - return n->isFlagSet(ecf::Flag::KILLED); -} - -//========================================================== -// Migrated -//========================================================== - -bool VMigratedIcon::show(VNode *n) -{ - if(n && (n->isSuite() || n->isFamily())) - { - return n->isFlagSet(ecf::Flag::MIGRATED); - } - return false; -} diff -Nru ecflow-4.9.0/Viewer/src/VIcon.hpp ecflow-4.11.1/Viewer/src/VIcon.hpp --- ecflow-4.9.0/Viewer/src/VIcon.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VIcon.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,61 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VICON_HPP_ -#define VICON_HPP_ - -#include -#include -#include -#include - -#include "VParam.hpp" - -#include -#include -#include - -class VNode; -class VParamSet; - -class VIcon : public VParam -{ -public: - explicit VIcon(const std::string& name); - virtual ~VIcon(); - - static std::vector filterItems(); - static QVariantList pixmapList(VNode *vnode,VParamSet *filter); - static int pixmapNum(VNode *vnode,VParamSet *filter); - static QString toolTip(VNode *vnode,VParamSet *filter); - static VIcon* find(const std::string& name); - static void names(std::vector&); - static const std::vector& lastNames() {return lastNames_;} - static void saveLastNames(); - static void initLastNames(); - - QPixmap pixmap(int size); - - //Called from VConfigLoader - static void load(VProperty* group); - -protected: - void initPixmap(); - virtual bool show(VNode*)=0; - QString shortDescription() const; - - int pixId_; - - static std::map items_; - static std::vector itemsVec_; - static std::vector lastNames_; -}; - -#endif diff -Nru ecflow-4.9.0/Viewer/src/Viewer.hpp ecflow-4.11.1/Viewer/src/Viewer.hpp --- ecflow-4.9.0/Viewer/src/Viewer.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/Viewer.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWER_HPP_ -#define VIEWER_HPP_ - -namespace Viewer -{ - enum ViewMode {TreeViewMode,TableViewMode,NoViewMode}; - enum ItemRole {InfoRole,ManualRole,ScriptRole,JobRole,OutputRole,WhyRole,TriggersRole,TimelineRole,VariableRole,EditRole,MessageRole}; - enum AttributeType {NoAttribute,LabelAttribute,MeterAttribute,EventAttribute,RepeatAttribute,TimeAttribute,DateAttribute, - TriggerAttribute,VarAttribute,GenVarAttribute,LateAttribute,LimitAttribute,LimiterAttribute}; - - - enum Param {UnknownParam,UnknownState,ActiveState,AbortedState,NoIcon,WaitIcon,RerunIcon,MessageIcon,CompleteIcon,TimeIcon,DateIcon,ZombieIcon,LateIcon}; -} - -#endif diff -Nru ecflow-4.9.0/Viewer/src/ViewerMain.cpp ecflow-4.11.1/Viewer/src/ViewerMain.cpp --- ecflow-4.9.0/Viewer/src/ViewerMain.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ViewerMain.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,170 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include -#include - -#include -#include -#include -#include -#include - -#include "File.hpp" -#include "MainWindow.hpp" -#include "ServerHandler.hpp" -#include "MenuHandler.hpp" -#include "InfoPanelHandler.hpp" -#include "InputEventLog.hpp" -#include "DirectoryHandler.hpp" -#include "Highlighter.hpp" -#include "NodeQueryHandler.hpp" -#include "CustomCommandHandler.hpp" -#include "Palette.hpp" -#include "ServerList.hpp" -#include "VConfig.hpp" -#include "VIcon.hpp" -#include "VServerSettings.hpp" -#include "SessionHandler.hpp" -#include "SessionDialog.hpp" -#include "UiLog.hpp" - -int main(int argc, char **argv) -{ - //if (argc != 3) - //{ - // std::cout << "Usage:" << std::endl; - // std::cout << argv[0] << " " << std::endl; - // return 1; - // - - //Init qt - QApplication app(argc, argv); - - app.setWindowIcon(QPixmap(":/viewer/logo_small.png")); - - QStringList styleLst=QStyleFactory::keys(); - - //Set the style - QString style="Plastique"; - if(styleLst.contains(style)) - { - app.setStyle(style); - } - else - { - style="Fusion"; - if(styleLst.contains(style)) - { - app.setStyle(style); - } - } - - //Set font size for application - //QFont font=app.font(); - //font.setPointSize(9); - //app.setFont(font); - - //Initialise the config and other paths - std::string exe(argv[0]); - DirectoryHandler::init(exe); // we need to tell the Directory class where we started from - - //Set the stylesheet - std::string styleSheetFileName="viewer.qss"; - std::string styleSheetPath=DirectoryHandler::concatenate(DirectoryHandler::etcDir(),styleSheetFileName); - - QFile shFile(QString::fromStdString(styleSheetPath)); - if(shFile.open(QIODevice::ReadOnly | QIODevice::Text)) - { - app.setStyleSheet(shFile.readAll()); - } - shFile.close(); - - //Load the configurable menu items - std::string menuFilename("ecflowview_menus.json"); - std::string menuPath = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), menuFilename); - MenuHandler::readMenuConfigFile(menuPath); - - //Load the custom context menu commands - CustomCommandHistoryHandler::instance()->init(); - CustomSavedCommandHandler::instance()->init(); - MenuHandler::refreshCustomMenuCommands(); - - //Load the info panel definition - std::string panelFile = DirectoryHandler::concatenate(DirectoryHandler::etcDir(), "ecflowview_panels.json"); - InfoPanelHandler::instance()->init(panelFile); - - //Load the queries - std::string queryDir = DirectoryHandler::concatenate(DirectoryHandler::configDir(), "query"); - NodeQueryHandler::instance()->init(queryDir); - - //Initialise the server list. This will update the server list - //from the central the system server list - ServerList::instance()->init(); - - // startup - via the session manager, or straight to the main window? - bool startMainWindow = true; - - //Initialise the session. We have to call this before VConfig::init() because - //some settings VConfig loads are session-dependent. - if (SessionHandler::requestStartupViaSessionManager()) - { - SessionDialog sessionDialog; - if (sessionDialog.exec() != QDialog::Accepted) - startMainWindow = false; - } - else - { - SessionHandler::setTemporarySessionIfReqested(); // user starts with -ts command-line switch? - } - - //Load the global configurations - VConfig::instance()->init(DirectoryHandler::etcDir()); - - //Import server settings from the previous viewer - if(DirectoryHandler::isFirstStartUp()) - { - VConfig::instance()->importSettings(); - VServerSettings::importRcFiles(); - } - - //Initialise highlighter - Highlighter::init(DirectoryHandler::concatenate(DirectoryHandler::etcDir(), - "ecflowview_highlighter.json")); - - //Initialise the system palette - Palette::load(DirectoryHandler::concatenate(DirectoryHandler::etcDir(), - "ecflowview_palette.json")); - - //Initialise the list containing all the icon names existed on last exit - VIcon::initLastNames(); - - //Start the GUI - if (startMainWindow) - { - //Build the GUI - MainWindow::init(); - - //Show all the windows - MainWindow::showWindows(); - - //Start input event logging - InputEventLog::instance()->start(); - - //Enable (daily) truncation for ui log - UiLog::enableTruncation(); - - return app.exec(); - } - else - { - return 0; // user quit from within the session manager - } -} diff -Nru ecflow-4.9.0/Viewer/src/viewer.qrc ecflow-4.11.1/Viewer/src/viewer.qrc --- ecflow-4.9.0/Viewer/src/viewer.qrc 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/viewer.qrc 1970-01-01 00:00:00.000000000 +0000 @@ -1,128 +0,0 @@ - - - ../images/goto_line.svg - ../images/dock_float.svg - ../images/dock_close.svg - ../images/configure.svg - ../images/reset.svg - ../images/search.svg - ../images/info.svg - ../images/add_info.svg - ../images/add_table.svg - ../images/add_tree.svg - ../images/icon_calendar.svg - ../images/icon_clock.svg - ../images/icon_complete.svg - ../images/icon_killed.svg - ../images/icon_late.svg - ../images/icon_children_hidden.svg - ../images/icon_node_log.svg - ../images/icon_rerun.svg - ../images/icon_slow.svg - ../images/icon_waiting.svg - ../images/icon_zombie.svg - ../images/arrow_up.svg - ../images/arrow_down.svg - ../images/add.svg - ../images/configure.svg - ../images/exit.svg - ../images/add_tab.svg - ../images/attribute.svg - ../images/chain.svg - ../images/chain_open.svg - ../images/chain_closed.svg - ../images/clear_left.svg - ../images/close.svg - ../images/close_grey.svg - ../images/close_red.svg - ../images/case_sensitive.svg - ../images/cogwheel.svg - ../images/cogwheel_blue.svg - ../images/colour.svg - ../images/copy_path.svg - ../images/dependency.svg - ../images/dependency_detail.svg - ../images/directory_arrow.svg - ../images/dock_chain_closed.svg - ../images/dock_chain_open.svg - ../images/dock_config.svg - ../images/dock_dependency.svg - ../images/dock_max.svg - ../images/dock_max_disabled.svg - ../images/dock_menu_indicator.png - ../images/dock_restore.svg - ../images/down_arrow.svg - ../images/drawer_open.svg - ../images/drawer_close.svg - ../images/edit.svg - ../images/editcopy.svg - ../images/editpaste.svg - ../images/error.svg - ../images/expression.svg - ../images/favourite.svg - ../images/favourite_empty.svg - ../images/filesave.svg - ../images/filesaveas.svg - ../images/filter_decor.svg - ../images/filter_decor_green.svg - ../images/filter_decor_red.svg - ../images/filter_edit.svg - ../images/filter.svg - ../images/filter_match.svg - ../images/filter_no_match.svg - ../images/font.svg - ../images/fontsize_down.svg - ../images/fontsize_up.svg - ../images/genvar.svg - ../images/genvar_shadow.svg - ../images/grey_info.svg - ../images/overview.svg - ../images/job.svg - ../images/large_file_search.svg - ../images/log_info.svg - ../images/log_error.svg - ../images/logo.png - ../images/logo_small.png - ../images/manage_server.svg - ../images/manual.svg - ../images/menu.svg - ../images/menu_arrow_down.svg - ../images/node_log.svg - ../images/notification.svg - ../images/output.svg - ../images/padlock.svg - ../images/path_arrow.svg - ../images/reload.svg - ../images/reload_green.svg - ../images/reload_one.svg - ../images/remove.svg - ../images/rescue.svg - ../images/reset_to_default.svg - ../images/script.svg - ../images/search_decor.svg - ../images/select_all.svg - ../images/server.svg - ../images/server_log.svg - ../images/show_shadowed.svg - ../images/spinning_wheel.gif - ../images/splash_screen.png - ../images/status.svg - ../images/system.svg - ../images/submit.svg - ../images/submit_round.svg - ../images/sync.svg - ../images/sync_black.svg - ../images/terminate.svg - ../images/tip.svg - ../images/trigger.svg - ../images/trigger_left_arrow.svg - ../images/trigger_right_arrow.svg - ../images/unknown.svg - ../images/unselect_all.svg - ../images/variable.svg - ../images/warning.svg - ../images/why.svg - ../images/zombie.svg - trigger.css - - diff -Nru ecflow-4.9.0/Viewer/src/ViewerUtil.cpp ecflow-4.11.1/Viewer/src/ViewerUtil.cpp --- ecflow-4.9.0/Viewer/src/ViewerUtil.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ViewerUtil.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,137 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "ViewerUtil.hpp" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -void ViewerUtil::initComboBox(QSettings& settings,QString key,QComboBox* cb) -{ - Q_ASSERT(cb); - QString txt=settings.value(key).toString(); - for(int i=0; i < cb->count(); i++) - { - if(cb->itemText(i) == txt) - { - cb->setCurrentIndex(i); - return; - } - } - - if(cb->currentIndex() == -1) - cb->setCurrentIndex(0); -} - -void ViewerUtil::initComboBoxByData(QString dataValue,QComboBox* cb) -{ - Q_ASSERT(cb); - for(int i=0; i < cb->count(); i++) - { - if(cb->itemData(i).toString() == dataValue) - { - cb->setCurrentIndex(i); - return; - } - } - - if(cb->currentIndex() == -1) - cb->setCurrentIndex(0); -} - -void ViewerUtil::initTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree) -{ - Q_ASSERT(tree); - - QStringList dataColumns=settings.value(key).toStringList(); - for(int i=0; i < tree->model()->columnCount()-1 && i < dataColumns.size(); i++) - { - tree->setColumnWidth(i,dataColumns[i].toInt()); - } -} - -void ViewerUtil::saveTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree) -{ - QStringList dataColumns; - for(int i=0; i < tree->model()->columnCount()-1; i++) - { - dataColumns << QString::number(tree->columnWidth(i)); - } - settings.setValue(key,dataColumns); -} - - -void ViewerUtil::initStacked(QSettings& settings,QString key,QStackedWidget *stacked) -{ - Q_ASSERT(stacked); - - int v=settings.value(key).toInt(); - if(v >= 0 && v < stacked->count()) - stacked->setCurrentIndex(v); -} - -void ViewerUtil::initButtonGroup(QSettings& settings,QString key,QButtonGroup *bg) -{ - Q_ASSERT(bg); - - int v=settings.value(key).toInt(); - if(v >= 0 && v < bg->buttons().count()) - { - bg->buttons().at(v)->setChecked(true); - bg->buttons().at(v)->click(); - } -} - -void ViewerUtil::initCheckableAction(QSettings& settings,QString key,QAction *ac) -{ - Q_ASSERT(ac); - - if(settings.contains(key)) - { - ac->setChecked(settings.value(key).toBool()); - } -} - -QBrush ViewerUtil::lineEditGreenBg() -{ - //return lineEditBg(QColor(189,239,205)); - return lineEditBg(QColor(210,255,224)); -} - -QBrush ViewerUtil::lineEditRedBg() -{ - return lineEditBg(QColor(234,215,214)); -} - -QBrush ViewerUtil::lineEditBg(QColor col) -{ - QLinearGradient grad; - grad.setCoordinateMode(QGradient::ObjectBoundingMode); - grad.setStart(0,0); - grad.setFinalStop(0,1); - - grad.setColorAt(0,col); - grad.setColorAt(0.1,col.lighter(105)); - grad.setColorAt(0.1,col.lighter(108)); - grad.setColorAt(0.9,col.lighter(108)); - grad.setColorAt(0.9,col.lighter(105)); - grad.setColorAt(1,col); - return QBrush(grad); -} diff -Nru ecflow-4.9.0/Viewer/src/ViewerUtil.hpp ecflow-4.11.1/Viewer/src/ViewerUtil.hpp --- ecflow-4.9.0/Viewer/src/ViewerUtil.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/ViewerUtil.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VIEWERUTIL_HPP -#define VIEWERUTIL_HPP - -#include -#include -#include -#include - -class QAction; -class QButtonGroup; -class QComboBox; -class QStackedWidget; -class QTabWidget; -class QTreeView; - -class ViewerUtil -{ -public: - static void initComboBox(QSettings&,QString key,QComboBox* cb); - static void initComboBoxByData(QString dataValue,QComboBox* cb); - static void initTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree); - static void saveTreeColumnWidth(QSettings& settings,QString key,QTreeView *tree); - static void initStacked(QSettings& settings,QString key,QStackedWidget *stacked); - static void initButtonGroup(QSettings& settings,QString key,QButtonGroup *bg); - static void initCheckableAction(QSettings& settings,QString key,QAction *ac); - static QBrush lineEditGreenBg(); - static QBrush lineEditRedBg(); - static QBrush lineEditBg(QColor col); -}; - -#endif // VIEWERUTIL_HPP - diff -Nru ecflow-4.9.0/Viewer/src/VInfo.cpp ecflow-4.11.1/Viewer/src/VInfo.cpp --- ecflow-4.9.0/Viewer/src/VInfo.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VInfo.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,492 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VInfo.hpp" - -#include "ServerHandler.hpp" -#include "UiLog.hpp" -#include "VAttribute.hpp" -#include "VAttributeType.hpp" -#include "VItemPathParser.hpp" -#include "VNode.hpp" - -#include - -//#define _UI_VINFO_DEBUG - -//======================================== -// -// VInfo -// -//======================================== - -VInfo::VInfo(ServerHandler* server,VNode* node,VAttribute* attr) : - server_(server), - node_(node), - attr_(attr) -{ - if(server_) - server_->addServerObserver(this); -} - -VInfo::~VInfo() -{ -#ifdef _UI_VINFO_DEBUG - UiLog().dbg() << "VInfo::~VInfo() --> " << this; -#endif - if(server_) - server_->removeServerObserver(this); - - for(std::vector::const_iterator it=observers_.begin(); it != observers_.end(); ++it) - (*it)->notifyDelete(this); - -#ifdef _UI_VINFO_DEBUG - UiLog().dbg() << "<-- ~VInfo()"; -#endif -} - -void VInfo::notifyServerDelete(ServerHandler* /*server*/) -{ - server_=0; - node_=0; - attr_=0; - - //This function is called from the server destructor. We do not remove this object from the ServerObservers - dataLost(); -} - -void VInfo::dataLost() -{ - std::vector obsTmp=observers_; - observers_.clear(); - - for(std::vector::iterator it=obsTmp.begin(); it != obsTmp.end(); ++it) - { - VInfoObserver* o=*it; - o->notifyDataLost(this); - } - - attr_=0; -} - -void VInfo::notifyBeginServerClear(ServerHandler* server) -{ - assert(server_==server); - node_=0; - attr_=0; -} - -void VInfo::notifyEndServerClear(ServerHandler* server) -{ - assert(server_==server); - node_=0; - attr_=0; -} - -void VInfo::notifyEndServerScan(ServerHandler* server) -{ - assert(server_==server); - regainData(); -} - -void VInfo::regainData() -{ - if(!server_) - { - dataLost(); - return; - } - - if(!node_) - { - if(isServer()) - { - node_=server_->vRoot(); - return; - } - else if(isNode()) - { - VItemPathParser p(storedPath_); - if(p.itemType() == VItemPathParser::NodeType) - { - node_=server_->vRoot()->find(p.node()); - if(node_) - return; - } - if(!node_) - { - dataLost(); - return; - } - } - } - - if(isAttribute()) - { - VItemPathParser p(storedPath_); - if(p.itemType() == VItemPathParser::AttributeType) - { - if(!node_) - { - node_=server_->vRoot()->find(p.node()); - } - if(node_) - { - attr_=node_->findAttribute(p.type(),p.attribute()); - } - if(!node_ || !attr_) - { - dataLost(); - } - } - } -} - -std::string VInfo::storedNodePath() const -{ - VItemPathParser p(storedPath_); - if(p.itemType() == VItemPathParser::ServerType) - return "/"; - else - return p.node(); -} - -void VInfo::addObserver(VInfoObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it == observers_.end()) - observers_.push_back(o); -} - -void VInfo::removeObserver(VInfoObserver* o) -{ - std::vector::iterator it=std::find(observers_.begin(),observers_.end(),o); - if(it != observers_.end()) - observers_.erase(it); -} - -bool VInfo::operator ==(const VInfo& other) -{ - if(server_ == other.server_ && node_ == other.node_ && - storedPath_ == other.storedPath_) - { - if((!attr_ && other.attr_) || - (attr_ && !other.attr_)) - return false; - - else if(attr_ && other.attr_) - { - return (attr_->type() == other.attr_->type() && - attr_->data() == other.attr_->data()); - } - else - return true; - } - return false; -} - -VInfo_ptr VInfo::createParent(VInfo_ptr info) -{ - if(!info) - return VInfo_ptr(); - - if(info->isServer()) - return info; - else if(info->isNode()) - { - return VInfoServer::create(info->server()); - } - else if(info->isAttribute()) - { - return VInfoNode::create(info->node()); - } - - return VInfo_ptr(); -} - -VInfo_ptr VInfo::createFromPath(ServerHandler* s,const std::string& path) -{ - if(!s || path.empty()) - return VInfo_ptr(); - - VItemPathParser p(path); - - if(p.itemType() == VItemPathParser::ServerType) - { - return VInfoServer::create(s); - } - else if(p.itemType() == VItemPathParser::NodeType) - { - if(VNode* n=s->vRoot()->find(p.node())) - return VInfoNode::create(n); - - } - else if(p.itemType() == VItemPathParser::AttributeType) - { - if(VNode* n=s->vRoot()->find(p.node())) - { - if(VAttribute* a=n->findAttribute(p.type(),p.attribute())) - { - return VInfoAttribute::create(a); - } - } - } - - return VInfo_ptr(); -} - -VInfo_ptr VInfo::createFromPath(const std::string& path) -{ - if(path.empty()) - return VInfo_ptr(); - - VItemPathParser p(path); - if(!p.server().empty()) - { - if(ServerHandler* s=ServerHandler::find(p.server())) - { - return createFromPath(s,path); - } - } - return VInfo_ptr(); -} - -VInfo_ptr VInfo::createFromItem(VItem* item) -{ - if(!item) - return VInfo_ptr(); - - if(VServer* s=item->isServer()) - { - return VInfoServer::create(s->server()); - } - else if(VNode* n=item->isNode()) - { - return VInfoNode::create(n); - } - else if(VAttribute* a=item->isAttribute()) - { - return VInfoAttribute::create(a); - } - - return VInfo_ptr(); -} - -//========================================= -// -// VInfoServer -// -//========================================= - -VInfoServer::VInfoServer(ServerHandler *server) : VInfo(server,NULL) -{ - if(server_) - { - node_=server_->vRoot(); - storedPath_=VItemPathParser::encodeWithServer(server_->name(),"/","server"); - } -} - -VInfo_ptr VInfoServer::create(ServerHandler *server) -{ - return VInfo_ptr(new VInfoServer(server)); -} - -bool VInfoServer::hasData() const -{ - return server_ != 0; -} - -void VInfoServer::accept(VInfoVisitor* v) -{ - v->visit(this); -} - -std::string VInfoServer::name() -{ - return (server_)?(server_->name()):(std::string()); -} - -std::string VInfoServer::path() -{ - return name() + "://"; -} - -VItem* VInfoServer::item() const -{ - return node_; -} - -//========================================= -// -// VInfoNode -// -//========================================= - - -VInfoNode::VInfoNode(ServerHandler* server,VNode* node) : VInfo(server,node) -{ - if(node_) - { - assert(server_); - storedPath_=VItemPathParser::encodeWithServer(server_->name(),node_->absNodePath(),"node"); - } -} - -VInfo_ptr VInfoNode::create(VNode *node) -{ - ServerHandler* server=NULL; - if(node) - { - server=node->server(); - } - return VInfo_ptr(new VInfoNode(server,node)); -} - -bool VInfoNode::hasData() const -{ - return server_ != 0 && node_ != 0; -} - -void VInfoNode::accept(VInfoVisitor* v) -{ - v->visit(this); -} - -std::string VInfoNode::name() -{ - if(node_ && node_->node()) - return node_->strName(); - - return std::string(); -} - -std::string VInfoNode::path() -{ - std::string p; - if(server_) - p=server_->name(); - - if(node_ && node_->node()) - p+=":/" + node_->absNodePath(); - - return p; -} - -std::string VInfoNode::serverAlias() -{ - std::string p; - if(server_) - p = server_->name(); - return p; -} - -std::string VInfoNode::nodePath() -{ - std::string p; - if(node_ && node_->node()) - p = node_->absNodePath(); - return p; -} - -std::string VInfoNode::relativePath() -{ - std::string p; - if(node_ && node_->node()) - p = node_->absNodePath(); - return p; -} - -VItem* VInfoNode::item() const -{ - return node_; -} - -//========================================= -// -// VInfoAttribute -// -//========================================= - - -VInfoAttribute::VInfoAttribute(ServerHandler* server,VNode* node,VAttribute* attr) : - VInfo(server,node,attr) -{ - if(attr_) - { - assert(server_); - storedPath_=VItemPathParser::encodeWithServer(server_->name(), - attr_->fullPath(),attr_->typeName()); - } -} - -VInfoAttribute::~VInfoAttribute() -{ -} - -bool VInfoAttribute::hasData() const -{ - return server_ != 0 && node_ != 0 && attr_ != 0; -} - -void VInfoAttribute::accept(VInfoVisitor* v) -{ - v->visit(this); -} - -VInfo_ptr VInfoAttribute::create(VAttribute* att) -{ - ServerHandler* server=NULL; - VNode* node=att->parent(); - if(node) - { - server=node->server(); - } - - return VInfo_ptr(new VInfoAttribute(server,node,att)); -} - -std::string VInfoAttribute::path() -{ - std::string p; - if(server_) - p=server_->name(); - if(attr_) - p+=":/" + attr_->fullPath(); - - return p; -} - -std::string VInfoAttribute::nodePath() -{ - std::string p; - if(node_ && node_->node()) - p = node_->absNodePath(); - return p; -} - -std::string VInfoAttribute::name() -{ - return (attr_)?attr_->strName():std::string(); -} - -VItem* VInfoAttribute::item() const -{ - return attr_; -} - - - - - - - - - diff -Nru ecflow-4.9.0/Viewer/src/VInfo.hpp ecflow-4.11.1/Viewer/src/VInfo.hpp --- ecflow-4.9.0/Viewer/src/VInfo.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VInfo.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,195 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VINFO_HPP_ -#define VINFO_HPP_ - -#include -#include -#include - -#include -#include - -#include "ServerObserver.hpp" -#include "Variable.hpp" - -class ServerHandler; -class VAttribute; -class VNode; -class VItem; - -class VInfoObserver; -class VInfoVisitor; - -class VInfo; -typedef boost::shared_ptr VInfo_ptr; - -//============================================================================== -// For each selected item in any of the views a new VInfo object is created. -// This class offers the same interface to access information about any selected -// items: servers, nodes, attributes. The concrete implementation of -// these access methods are done in subclasses derived from VInfo. -// -// VInfo is regarded as a temporary object. We only need it while the selection -// is used in breadcrumbs, info panels or other widgets outside the main views. -//============================================================================== - -class VInfo : public ServerObserver -{ -public: - virtual ~VInfo(); - - virtual bool isServer() {return false;} - virtual bool isNode() {return false;} - virtual bool isAttribute() {return false;} - virtual bool isEmpty() {return true;} - virtual bool hasData() const=0; - - ServerHandler* server() const {return server_;} - VNode* node() const {return node_;} - VAttribute* attribute() const {return attr_;} - virtual VItem* item() const=0; - - virtual std::string name()=0; - virtual std::string path()=0; - virtual std::string serverAlias() {return "";} - virtual std::string relativePath() {return "";} - virtual std::string nodePath()=0; - std::string storedNodePath() const; - const std::string& storedPath() const {return storedPath_;} - - virtual void accept(VInfoVisitor*)=0; - - void regainData(); - void addObserver(VInfoObserver*); - void removeObserver(VInfoObserver*); - - //Form ServerObserver - void notifyDefsChanged(ServerHandler* server, const std::vector& a) {} - void notifyServerDelete(ServerHandler* server); - void notifyBeginServerClear(ServerHandler* server); - void notifyEndServerClear(ServerHandler* server); - void notifyBeginServerScan(ServerHandler* server,const VServerChange&) {} - void notifyEndServerScan(ServerHandler* server); - void notifyServerConnectState(ServerHandler* server) {} - void notifyServerActivityChanged(ServerHandler* server) {} - void notifyServerSuiteFilterChanged(ServerHandler* server) {} - - bool operator ==(const VInfo&); - - static VInfo_ptr createParent(VInfo_ptr); - static VInfo_ptr createFromPath(ServerHandler*,const std::string&); - static VInfo_ptr createFromPath(const std::string& path); - static VInfo_ptr createFromItem(VItem*); - -protected: - VInfo(ServerHandler* server,VNode* node,VAttribute* attr=0); - void dataLost(); - - mutable ServerHandler* server_; - mutable VNode* node_; - mutable VAttribute* attr_; - mutable std::string storedPath_; - - std::vector observers_; -}; - -// Implements the info object for server selections -class VInfoServer : public VInfo, public boost::enable_shared_from_this -{ -public: - bool isServer() {return true;} - bool isEmpty() {return false;} - bool hasData() const; - VItem* item() const; - - void accept(VInfoVisitor*); - std::string name(); - std::string path(); - std::string nodePath() {return "/";} - static VInfo_ptr create(ServerHandler*); - -protected: - explicit VInfoServer(ServerHandler*); -}; - - -// Implements the info object for node selections -class VInfoNode: public VInfo, public boost::enable_shared_from_this -{ -public: - bool isNode() {return true;} - bool isEmpty() {return false;} - bool hasData() const; - void accept(VInfoVisitor*); - std::string path(); - std::string name(); - std::string serverAlias(); - std::string nodePath(); - std::string relativePath(); - VItem* item() const; - - static VInfo_ptr create(VNode*); - -protected: - VInfoNode(ServerHandler*,VNode*); -}; - - -// Implements the info base class for attribute selections -class VInfoAttribute: public VInfo, public boost::enable_shared_from_this -{ -public: - ~VInfoAttribute(); - VAttribute* attribute() const {return attr_;} - bool isAttribute() {return true;} - bool isEmpty() {return false;} - bool hasData() const; - void accept(VInfoVisitor*); - std::string name(); - std::string path(); - std::string nodePath(); - VItem* item() const; - - static VInfo_ptr create(VAttribute*); - -protected: - VInfoAttribute(ServerHandler*,VNode*,VAttribute*); -}; - -typedef boost::shared_ptr VInfoServer_ptr; -typedef boost::shared_ptr VInfoNode_ptr; -typedef boost::shared_ptr VInfoAttribute_ptr; - -class VInfoVisitor -{ -public: - VInfoVisitor() {} - virtual ~VInfoVisitor() {} - - virtual void visit(VInfoServer*)=0; - virtual void visit(VInfoNode*)=0; - virtual void visit(VInfoAttribute*)=0; - -}; - -class VInfoObserver -{ -public: - VInfoObserver() {} - virtual ~VInfoObserver() {} - - virtual void notifyDataLost(VInfo*)=0; - virtual void notifyDelete(VInfo*)=0; -}; - -#endif - diff -Nru ecflow-4.9.0/Viewer/src/VItem.cpp ecflow-4.11.1/Viewer/src/VItem.cpp --- ecflow-4.9.0/Viewer/src/VItem.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VItem.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VItem.hpp" -#include "VNode.hpp" - -bool VItem::isAncestor(const VItem* n) const -{ - if(n == this) - return true; - - VNode* nd=parent(); - while(nd) - { - if(const_cast(n) == nd) - return true; - - nd=nd->parent(); - } - return false; -} diff -Nru ecflow-4.9.0/Viewer/src/VItem.hpp ecflow-4.11.1/Viewer/src/VItem.hpp --- ecflow-4.9.0/Viewer/src/VItem.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VItem.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VITEM_HPP_ -#define VITEM_HPP_ - -#include -#include - -class ServerHandler; -class VNode; -class VServer; -class VSuiteNode; -class VFamilyNode; -class VAliasNode; -class VTaskNode; -class VAttribute; -class VItemVisitor; - -class VItem -{ -public: - VItem(VNode* parent) : parent_(parent) {} - virtual ~VItem() {} - - VNode* parent() const {return parent_;} - virtual VServer* isServer() const {return NULL;} - virtual VNode* isNode() const {return NULL;} - virtual VSuiteNode* isSuite() const {return NULL;} - virtual VFamilyNode* isFamily() const {return NULL;} - virtual VTaskNode* isTask() const {return NULL;} - virtual VAliasNode* isAlias() const {return NULL;} - virtual VAttribute* isAttribute() const {return NULL;} - - virtual ServerHandler* server() const=0; - virtual VServer* root() const=0; - virtual bool isTopLevel() const {return false;} - virtual std::string strName() const=0; - virtual QString name() const=0; - virtual const std::string& typeName() const=0; - virtual std::string fullPath() const=0; - virtual bool sameContents(VItem*) const=0; - virtual bool isAncestor(const VItem*) const; - virtual QString nodeMenuMode() const {return QString();} - -protected: - VNode* parent_; -}; - - -#if 0 -class VItemVisitor -{ -public: - VItemVisitor() {} - virtual ~VItemVisitor() {} - - virtual void visit(VServer*) {} - virtual void visit(VNode*) {} - virtual void visit(VAttribute*) {} -}; -#endif - - -#endif // VITEM_HPP_ - diff -Nru ecflow-4.9.0/Viewer/src/VItemPathParser.cpp ecflow-4.11.1/Viewer/src/VItemPathParser.cpp --- ecflow-4.9.0/Viewer/src/VItemPathParser.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VItemPathParser.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,127 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VItemPathParser.hpp" -#include "VAttributeType.hpp" - -VItemPathParser::VItemPathParser(const std::string& path) : itemType_(NoType) -{ - if(path.empty()) - return; - - size_t pos; - std::string p=path; - if(p.find("[") == 0) - { - pos=p.find("]"); - if(pos != std::string::npos) - { - type_=path.substr(1,pos-1); - p=path.substr(pos+1); - } - else - return; - } - - pos=p.find("://"); - - if(pos != std::string::npos) - { - server_=p.substr(0,pos); - p=p.substr(pos+2); - } - - if(p.size() == 1 && p.find("/") == 0) - { - itemType_=ServerType; - return; - } - - //Here we suppose that the node name cannot contain ":" - pos=p.find_first_of(":"); - if(pos != std::string::npos) - { - node_=p.substr(0,pos); - attribute_=p.substr(pos+1); - itemType_=AttributeType; - - if(type_ == "gen-variable") - type_="genvar"; - else if(type_ == "user-variable") - type_="var"; - - if(!VAttributeType::find(type_)) - { - itemType_=NoType; - type_.clear(); - } - } - else - { - node_=p; - itemType_=NodeType; - } -} - -std::string VItemPathParser::encode(const std::string& path,const std::string& type) -{ - if(type.empty()) - return path; - - return "[" + type + "]" + path; -} - -std::string VItemPathParser::encodeWithServer(const std::string& server,const std::string& path,const std::string& type) -{ - if(type.empty()) - return path; - - return "[" + type + "]" + server + ":/" + path; -} - -std::string VItemPathParser::encodeAttribute(const std::string& parentPath,const std::string& attrName,const std::string& attrType) -{ - if(attrType.empty()) - return std::string(); - - VItemPathParser parent(parentPath); - - return "[" + attrType + "]" + parent.server() + ":/" + parent.node() + ":" + attrName; -} - -std::string VItemPathParser::parent() const -{ - switch(itemType_) - { - case ServerType: - return std::string(); - break; - case NodeType: - { - std::size_t pos=node_.find_last_of("/"); - if(pos != std::string::npos) - { - std::string n=node_.substr(0,pos); - if(!n.empty()) - return encodeWithServer(server_,n,type_); - else - return encodeWithServer(server_,"/","server"); - } - } - break; - case AttributeType: - return encodeWithServer(server_,node_,"node"); - break; - default: - break; - } - - return std::string(); -} diff -Nru ecflow-4.9.0/Viewer/src/VItemPathParser.hpp ecflow-4.11.1/Viewer/src/VItemPathParser.hpp --- ecflow-4.9.0/Viewer/src/VItemPathParser.hpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VItemPathParser.hpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -//============================================================================ -// Copyright 2009-2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#ifndef VITEMPATHPARSER_HPP -#define VITEMPATHPARSER_HPP - -#include - -class VItemPathParser -{ -public: - VItemPathParser(const std::string& path); - - enum ItemType {ServerType,NodeType,AttributeType,NoType}; - - ItemType itemType() const {return itemType_;} - const std::string type() const {return type_;} - const std::string server() const {return server_;} - const std::string node() const {return node_;} - const std::string attribute() const {return attribute_;} - std::string parent() const; - - static std::string encode(const std::string& path,const std::string& type); - static std::string encodeWithServer(const std::string& server,const std::string& path, - const std::string& type); - static std::string encodeAttribute(const std::string& parentPath,const std::string& attrName,const std::string& attrType); - -protected: - ItemType itemType_; - std::string type_; - std::string server_; - std::string node_; - std::string attribute_; -}; - - -#endif // VITEMPATHPARSER_HPP - diff -Nru ecflow-4.9.0/Viewer/src/VLabelAttr.cpp ecflow-4.11.1/Viewer/src/VLabelAttr.cpp --- ecflow-4.9.0/Viewer/src/VLabelAttr.cpp 2018-04-09 09:05:21.000000000 +0000 +++ ecflow-4.11.1/Viewer/src/VLabelAttr.cpp 1970-01-01 00:00:00.000000000 +0000 @@ -1,139 +0,0 @@ -//============================================================================ -// Copyright 2017 ECMWF. -// This software is licensed under the terms of the Apache Licence version 2.0 -// which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. -// In applying this licence, ECMWF does not waive the privileges and immunities -// granted to it by virtue of its status as an intergovernmental organisation -// nor does it submit to any jurisdiction. -// -//============================================================================ - -#include "VLabelAttr.hpp" -#include "VAttributeType.hpp" -#include "VNode.hpp" - -#include "NodeAttr.hpp" - -//================================ -// VLabelAttrType -//================================ - -VLabelAttrType::VLabelAttrType() : VAttributeType("label") -{ - dataCount_=3; - searchKeyToData_["label_name"]=NameIndex; - searchKeyToData_["label_value"]=ValueIndex; - searchKeyToData_["name"]=NameIndex; - scanProc_=VLabelAttr::scan; -} - -QString VLabelAttrType::toolTip(QStringList d) const -{ - QString t="Type: Label
      "; - if(d.count() == dataCount_) - { - t+="Name: " + d[NameIndex] + "
      "; - t+="Value: " + d[ValueIndex]; - } - return t; -} - -QString VLabelAttrType::definition(QStringList d) const -{ - QString t="label"; - if(d.count() == dataCount_) - { - t+=" " + d[NameIndex] + " '" + d[ValueIndex] + "'"; - } - return t; -} - -void VLabelAttrType::encode(const Label& label,QStringList& data,bool firstLine) const -{ - std::string val=label.new_value(); - if(val.empty() || val == " ") - { - val=label.value(); - } - - if(firstLine) - { - std::size_t pos=val.find("\n"); - if(pos != std::string::npos) - { - val=val.substr(0,pos); - } - } - - data << qName_ << - QString::fromStdString(label.name()) << - QString::fromStdString(val); -} - -//===================================================== -// -// VLabelAttr -// -//===================================================== - -VLabelAttr::VLabelAttr(VNode *parent,const Label& label, int index) : VAttribute(parent,index) -{ - //name_=label.name(); -} - -int VLabelAttr::lineNum() const -{ - if(parent_->node_) - { - const std::vector