log_database_proxy_model.cpp
Go to the documentation of this file.
00001 // *****************************************************************************
00002 //
00003 // Copyright (c) 2015, Southwest Research Institute® (SwRI®)
00004 // All rights reserved.
00005 //
00006 // Redistribution and use in source and binary forms, with or without
00007 // modification, are permitted provided that the following conditions are met:
00008 //     * Redistributions of source code must retain the above copyright
00009 //       notice, this list of conditions and the following disclaimer.
00010 //     * Redistributions in binary form must reproduce the above copyright
00011 //       notice, this list of conditions and the following disclaimer in the
00012 //       documentation and/or other materials provided with the distribution.
00013 //     * Neither the name of Southwest Research Institute® (SwRI®) nor the
00014 //       names of its contributors may be used to endorse or promote products
00015 //       derived from this software without specific prior written permission.
00016 //
00017 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
00018 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00019 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00020 // ARE DISCLAIMED. IN NO EVENT SHALL Southwest Research Institute® BE LIABLE 
00021 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
00022 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
00023 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 
00024 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
00025 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
00026 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
00027 // DAMAGE.
00028 //
00029 // *****************************************************************************
00030 
00031 #include <stdio.h>
00032 #include <algorithm>
00033 #include <iterator>
00034 
00035 #include <ros/time.h>
00036 #include <rosbag/bag.h>
00037 
00038 #include <swri_console/log_database_proxy_model.h>
00039 #include <swri_console/log_database.h>
00040 #include <swri_console/settings_keys.h>
00041 
00042 #include <QColor>
00043 #include <QFile>
00044 #include <QTextStream>
00045 #include <QTimer>
00046 #include <QSettings>
00047 #include <QtGlobal>
00048 
00049 namespace swri_console
00050 {
00051 LogDatabaseProxyModel::LogDatabaseProxyModel(LogDatabase *db)
00052   :
00053   colorize_logs_(true),
00054   display_time_(true),
00055   display_absolute_time_(false),
00056   use_regular_expressions_(false),
00057   debug_color_(Qt::gray),
00058   info_color_(Qt::black),
00059   warn_color_(QColor(255,127,0)),
00060   error_color_(Qt::red),
00061   fatal_color_(Qt::magenta),
00062   db_(db),
00063   failedSearchText_(""),
00064   failedSearchIndex_(0)
00065 {
00066   QObject::connect(db_, SIGNAL(databaseCleared()),
00067                    this, SLOT(handleDatabaseCleared()));
00068   QObject::connect(db_, SIGNAL(messagesAdded()),
00069                    this, SLOT(processNewMessages()));
00070 
00071   QObject::connect(db_, SIGNAL(minTimeUpdated()),
00072                    this, SLOT(minTimeUpdated()));
00073 }
00074 
00075 LogDatabaseProxyModel::~LogDatabaseProxyModel()
00076 {
00077 }
00078 
00079 void LogDatabaseProxyModel::setNodeFilter(const std::set<std::string> &names)
00080 {
00081   names_ = names;
00082   reset();
00083 }
00084 
00085 void LogDatabaseProxyModel::setSeverityFilter(uint8_t severity_mask)
00086 {
00087   severity_mask_ = severity_mask;
00088   reset();
00089 }
00090 
00091 void LogDatabaseProxyModel::setAbsoluteTime(bool absolute)
00092 {
00093   if (absolute == display_absolute_time_) {
00094     return;
00095   }
00096 
00097   display_absolute_time_ = absolute;
00098 
00099   QSettings settings;
00100   settings.setValue(SettingsKeys::ABSOLUTE_TIMESTAMPS, display_absolute_time_);
00101 
00102   if (display_time_ && msg_mapping_.size()) {
00103     Q_EMIT dataChanged(index(0), index(msg_mapping_.size()));
00104   }
00105 }
00106 
00107 
00108 void LogDatabaseProxyModel::setColorizeLogs(bool colorize_logs)
00109 {
00110   if (colorize_logs == colorize_logs_) {
00111     return;
00112   }
00113 
00114   colorize_logs_ = colorize_logs;
00115   QSettings settings;
00116   settings.setValue(SettingsKeys::COLORIZE_LOGS, colorize_logs_);
00117 
00118   if (msg_mapping_.size()) {
00119     Q_EMIT dataChanged(index(0), index(msg_mapping_.size()));
00120   }
00121 }
00122 
00123 void LogDatabaseProxyModel::setDisplayTime(bool display)
00124 {
00125   if (display == display_time_) {
00126     return;
00127   }
00128 
00129   display_time_ = display;
00130 
00131   QSettings settings;
00132   settings.setValue(SettingsKeys::DISPLAY_TIMESTAMPS, display_time_);
00133 
00134   if (msg_mapping_.size()) {
00135     Q_EMIT dataChanged(index(0), index(msg_mapping_.size()));
00136   }
00137 }
00138 
00139 void LogDatabaseProxyModel::setUseRegularExpressions(bool useRegexps)
00140 {
00141   if (useRegexps == use_regular_expressions_) {
00142     return;
00143   }
00144 
00145   use_regular_expressions_ = useRegexps;
00146   QSettings settings;
00147   settings.setValue(SettingsKeys::USE_REGEXPS, useRegexps);
00148   reset();
00149 }
00150 
00151 void LogDatabaseProxyModel::setIncludeFilters(
00152   const QStringList &list)
00153 {
00154   include_strings_ = list;
00155   // The text and regexp filters are always updated at the same time, so this
00156   // value will be saved by setIncludeRegexpPattern.
00157   reset();
00158 }
00159 
00160 void LogDatabaseProxyModel::setExcludeFilters(
00161   const QStringList &list)
00162 {
00163   exclude_strings_ = list;
00164   // The text and regexp filters are always updated at the same time, so this
00165   // value will be saved by setExcludeRegexpPattern.
00166   reset();
00167 }
00168 
00169 
00170 void LogDatabaseProxyModel::setIncludeRegexpPattern(const QString& pattern)
00171 {
00172   include_regexp_.setPattern(pattern);
00173   QSettings settings;
00174   settings.setValue(SettingsKeys::INCLUDE_FILTER, pattern);
00175   reset();
00176 }
00177 
00178 void LogDatabaseProxyModel::setExcludeRegexpPattern(const QString& pattern)
00179 {
00180   exclude_regexp_.setPattern(pattern);
00181   QSettings settings;
00182   settings.setValue(SettingsKeys::EXCLUDE_FILTER, pattern);
00183   reset();
00184 }
00185 
00186 void LogDatabaseProxyModel::setDebugColor(const QColor& debug_color)
00187 {
00188   debug_color_ = debug_color;
00189   QSettings settings;
00190   settings.setValue(SettingsKeys::DEBUG_COLOR, debug_color);
00191   reset();
00192 }
00193 
00194 void LogDatabaseProxyModel::setInfoColor(const QColor& info_color)
00195 {
00196   info_color_ = info_color;
00197   QSettings settings;
00198   settings.setValue(SettingsKeys::INFO_COLOR, info_color);
00199   reset();
00200 }
00201 
00202 void LogDatabaseProxyModel::setWarnColor(const QColor& warn_color)
00203 {
00204   warn_color_ = warn_color;
00205   QSettings settings;
00206   settings.setValue(SettingsKeys::WARN_COLOR, warn_color);
00207   reset();
00208 }
00209 
00210 void LogDatabaseProxyModel::setErrorColor(const QColor& error_color)
00211 {
00212   error_color_ = error_color;
00213   QSettings settings;
00214   settings.setValue(SettingsKeys::ERROR_COLOR, error_color);
00215   reset();
00216 }
00217 
00218 void LogDatabaseProxyModel::setFatalColor(const QColor& fatal_color)
00219 {
00220   fatal_color_ = fatal_color;
00221   QSettings settings;
00222   settings.setValue(SettingsKeys::FATAL_COLOR, fatal_color);
00223   reset();
00224 }
00225 
00226 int LogDatabaseProxyModel::rowCount(const QModelIndex &parent) const
00227 {
00228   if (parent.isValid()) {
00229     return 0;
00230   }
00231 
00232   return msg_mapping_.size();
00233 }
00234 
00235 
00236 bool LogDatabaseProxyModel::isIncludeValid() const
00237 {
00238   if (use_regular_expressions_ && !include_regexp_.isValid()) {
00239     return false;
00240   }
00241   return true;
00242 }
00243 
00244 bool LogDatabaseProxyModel::isExcludeValid() const
00245 {
00246   if (use_regular_expressions_ && !exclude_regexp_.isValid()) {
00247     return false;
00248   }
00249   return true;
00250 }
00251 
00252 // Locates the next index based on search criteria, VCM 25 April 2017
00253 // searchText_ - string from searchText, all upper case and trimmed spaces
00254 // index - currently selected item in messageList
00255 // increment - +1 = next||search(i.e. down), -1 = prev (i.e. up)
00256 int LogDatabaseProxyModel::getItemIndex(const QString searchText, int index, int increment)
00257 {
00258   int searchNotFound = -1;  // indicates search not found
00259   int counter=0;  // used to stop loop once full list has been searched
00260   bool partialSearch = false;  // tells main loop to run a partial search, triggered by prior failed search
00261   if(searchText==""||msg_mapping_.size()==0)  // skip search for 1)empty string 2)empty set
00262   {
00263     clearSearchFailure();  // reset failed search variables
00264     return searchNotFound;
00265   }
00266 
00267   // round corners for searches
00268   if(index<0)  // if index < 0, set to size()-1;
00269   {
00270     index = msg_mapping_.size()-1;
00271   }
00272   else if(index>=msg_mapping_.size())  // if index >size(), set to 0;
00273   {
00274     index = 0;
00275   }
00276 
00277   // trigger partial search if:
00278   //   searchText_ conaints prior failed text
00279   //   prior failed text is not empty
00280   //   failed index is not 0
00281   //   failed search index isn't greater than current index, this could happen through user
00282   //     interface message selection. Software should clear the variables when UI is adjusted.
00283   if(searchText.contains(failedSearchText_) && failedSearchText_ != "" && failedSearchIndex_ !=0 && failedSearchIndex_ <= msg_mapping_.size() )
00284   {
00285     partialSearch = true;
00286     index = failedSearchIndex_-1;
00287     counter = failedSearchIndex_;
00288   }
00289   int i;
00290   for(i=0; i<msg_mapping_.size();i++)  // loop through all messages until end or match is found
00291   {
00292     const LineMap line_idx = msg_mapping_[index];
00293     const LogEntry &item = db_->log()[line_idx.log_index];
00294     QString tempString = item.text.join("|");  // concatenate strings
00295     if(tempString.toUpper().contains(searchText))  // search match found
00296     {
00297       clearSearchFailure();  // reset failed search variables
00298       return index;  // match found, return location and exit loop
00299     }
00300     counter++;  // used to track total search length
00301     if(counter>=msg_mapping_.size())  // exit if all messages have been scanned
00302     {
00303       if((!partialSearch)||(failedSearchText_ == ""))  // store failed text if one isn't already stored
00304       {
00305         failedSearchText_ = searchText;
00306       }
00307       failedSearchIndex_ = msg_mapping_.size();
00308       return searchNotFound;  // match not found, return -1 and exit loop
00309     }
00310     // increment (next/search) or decrement (prev) index then address corner rounding
00311     index = index + increment;
00312     if(index<0)  // less than 0 set to max
00313     {
00314       index = msg_mapping_.size()-1;
00315     }
00316     else if(index>=msg_mapping_.size())  // greater than max, set to 0
00317     {
00318       index = 0;
00319     }
00320   }
00321 
00322   return -1;
00323 }
00324 
00325 void LogDatabaseProxyModel::clearSearchFailure()
00326 {
00327   // reset failed search variables, VCM 27 April 2017
00328   failedSearchIndex_ = 0;
00329   failedSearchText_ = "";
00330 }
00331 
00332 QVariant LogDatabaseProxyModel::data(
00333   const QModelIndex &index, int role) const
00334 {
00335   switch (role)
00336   {
00337     // Currently we're only returning data for these roles, so return immediately
00338     // if we're being queried for anything else.
00339     case Qt::DisplayRole:
00340     case Qt::ToolTipRole:
00341     case ExtendedLogRole:
00342       break;
00343     case Qt::ForegroundRole:
00344       if (colorize_logs_) {
00345         break;
00346       }
00347     default:
00348       return QVariant();
00349   }
00350 
00351   if (index.parent().isValid() &&
00352       static_cast<size_t>(index.row()) >= msg_mapping_.size()) {
00353     return QVariant();
00354   }
00355 
00356   const LineMap line_idx = msg_mapping_[index.row()];
00357   const LogEntry &item = db_->log()[line_idx.log_index];
00358 
00359   if (role == Qt::DisplayRole) {
00360     char level = '?';
00361     if (item.level == rosgraph_msgs::Log::DEBUG) {
00362       level = 'D';
00363     } else if (item.level == rosgraph_msgs::Log::INFO) {
00364       level = 'I';
00365     } else if (item.level == rosgraph_msgs::Log::WARN) {
00366       level = 'W';
00367     } else if (item.level == rosgraph_msgs::Log::ERROR) {
00368       level = 'E';
00369     } else if (item.level == rosgraph_msgs::Log::FATAL) {
00370       level = 'F';
00371     }
00372 
00373     char stamp[128];
00374     if (display_absolute_time_) {
00375       snprintf(stamp, sizeof(stamp),
00376                "%u.%09u",
00377                item.stamp.sec,
00378                item.stamp.nsec);
00379     } else {
00380       ros::Duration t = item.stamp - db_->minTime();
00381 
00382       int32_t secs = t.sec;
00383       int hours = secs / 60 / 60;
00384       int minutes = (secs / 60) % 60;
00385       int seconds = (secs % 60);
00386       int milliseconds = t.nsec / 1000000;
00387       
00388       snprintf(stamp, sizeof(stamp),
00389                "%d:%02d:%02d:%03d",
00390                hours, minutes, seconds, milliseconds);
00391     }
00392 
00393     char header[1024];
00394     if (display_time_) {
00395       snprintf(header, sizeof(header),
00396                "[%c %s] ", level, stamp);
00397     } else {
00398       snprintf(header, sizeof(header),
00399                "[%c] ", level);
00400     }
00401 
00402     // For multiline messages, we only want to display the header for
00403     // the first line.  For the subsequent lines, we generate a header
00404     // and then fill it with blank lines so that the messages are
00405     // aligned properly (assuming monospaced font).  
00406     if (line_idx.line_index != 0) {
00407       size_t len = strnlen(header, sizeof(header));
00408       for (size_t i = 0; i < len; i++) {
00409         header[i] = ' ';
00410       }
00411     }
00412     
00413     return QVariant(QString(header) + item.text[line_idx.line_index]);
00414   }
00415   else if (role == Qt::ForegroundRole && colorize_logs_) {
00416     switch (item.level) {
00417       case rosgraph_msgs::Log::DEBUG:
00418         return QVariant(debug_color_);
00419       case rosgraph_msgs::Log::INFO:
00420         return QVariant(info_color_);
00421       case rosgraph_msgs::Log::WARN:
00422         return QVariant(warn_color_);
00423       case rosgraph_msgs::Log::ERROR:
00424         return QVariant(error_color_);
00425       case rosgraph_msgs::Log::FATAL:
00426         return QVariant(fatal_color_);
00427       default:
00428         return QVariant(info_color_);
00429     }
00430   }
00431   else if (role == Qt::ToolTipRole) {
00432     char buffer[4096];
00433     snprintf(buffer, sizeof(buffer),
00434              "<p style='white-space:pre'>"
00435              "Timestamp: %d.%09d\n"
00436              "Seq: %d\n"
00437              "Node: %s\n"
00438              "Function: %s\n"
00439              "File: %s\n"
00440              "Line: %d\n"
00441              "\n",
00442              item.stamp.sec,
00443              item.stamp.nsec,
00444              item.seq,
00445              item.node.c_str(),
00446              item.function.c_str(),
00447              item.file.c_str(),
00448              item.line);
00449     
00450     QString text = (QString(buffer) +
00451                     item.text.join("\n") + 
00452                     QString("</p>"));
00453                             
00454     return QVariant(text);
00455   } else if (role == LogDatabaseProxyModel::ExtendedLogRole) {
00456     char buffer[4096];
00457     snprintf(buffer, sizeof(buffer),
00458              "Timestamp: %d.%09d\n"
00459              "Node: %s\n"
00460              "Function: %s\n"
00461              "File: %s\n"
00462              "Line: %d\n"
00463              "Message: ",
00464              item.stamp.sec,
00465              item.stamp.nsec,
00466              item.node.c_str(),
00467              item.function.c_str(),
00468              item.file.c_str(),
00469              item.line);
00470     
00471     QString text = (QString(buffer) +
00472                     item.text.join("\n")); 
00473                             
00474     return QVariant(text);
00475   }
00476       
00477   return QVariant();
00478 }
00479 
00480 void LogDatabaseProxyModel::reset()
00481 {
00482   beginResetModel();
00483   msg_mapping_.clear();
00484   early_mapping_.clear();
00485   earliest_log_index_ = db_->log().size();
00486   latest_log_index_ = earliest_log_index_;
00487   endResetModel();
00488   scheduleIdleProcessing();
00489 }
00490 
00491 
00492 void LogDatabaseProxyModel::saveToFile(const QString& filename) const
00493 {
00494   if (filename.endsWith(".bag", Qt::CaseInsensitive)) {
00495     saveBagFile(filename);
00496   }
00497   else {
00498     saveTextFile(filename);
00499   }
00500 }
00501 
00502 void LogDatabaseProxyModel::saveBagFile(const QString& filename) const
00503 {
00504   rosbag::Bag bag(filename.toStdString().c_str(), rosbag::bagmode::Write);
00505 
00506   size_t idx = 0;
00507   while (idx < msg_mapping_.size()) {
00508     const LineMap line_map = msg_mapping_[idx];    
00509     const LogEntry &item = db_->log()[line_map.log_index];
00510     
00511     rosgraph_msgs::Log log;
00512     log.file = item.file;
00513     log.function = item.function;
00514     log.header.seq = item.seq;
00515     if (item.stamp < ros::TIME_MIN) {
00516       // Note: I think TIME_MIN is the minimum representation of
00517       // ros::Time, so this branch should be impossible.  Nonetheless,
00518       // it doesn't hurt.
00519       log.header.stamp = ros::Time::now();
00520       qWarning("Msg with seq %d had time (%d); it's less than ros::TIME_MIN, which is invalid. "
00521                "Writing 'now' instead.",
00522                log.header.seq, item.stamp.sec);
00523     } else {
00524       log.header.stamp = item.stamp;
00525     }
00526     log.level = item.level;
00527     log.line = item.line;
00528     log.msg = item.text.join("\n").toStdString();
00529     log.name = item.node;
00530     bag.write("/rosout", log.header.stamp, log);
00531 
00532     // Advance to the next line with a different log index.
00533     idx++;
00534     while (idx < msg_mapping_.size() && msg_mapping_[idx].log_index == line_map.log_index) {
00535       idx++;
00536     }
00537   }
00538   bag.close();
00539 }
00540 
00541 void LogDatabaseProxyModel::saveTextFile(const QString& filename) const
00542 {
00543   QFile outFile(filename);
00544   outFile.open(QFile::WriteOnly);
00545   QTextStream outstream(&outFile);
00546   for(size_t i = 0; i < msg_mapping_.size(); i++)
00547   {
00548     QString line = data(index(i), Qt::DisplayRole).toString();
00549     outstream << line << '\n';
00550   }
00551   outstream.flush();
00552   outFile.close();
00553 }
00554 
00555 void LogDatabaseProxyModel::handleDatabaseCleared()
00556 {
00557   reset();
00558   clearSearchFailure();  // reset failed search variables, VCM 26 April 2017
00559 }
00560 
00561 void LogDatabaseProxyModel::processNewMessages()
00562 {
00563   std::deque<LineMap> new_items;
00564  
00565   // Process all messages from latest_log_index_ to the end of the
00566   // log.
00567   for (;
00568        latest_log_index_ < db_->log().size();
00569        latest_log_index_++)
00570   {
00571     const LogEntry &item = db_->log()[latest_log_index_];    
00572     if (!acceptLogEntry(item)) {
00573       continue;
00574     }    
00575 
00576     for (int i = 0; i < item.text.size(); i++) {
00577       new_items.push_back(LineMap(latest_log_index_, i));
00578     }
00579   }
00580   
00581   if (!new_items.empty()) {
00582     beginInsertRows(QModelIndex(),
00583                     msg_mapping_.size(),
00584                     msg_mapping_.size() + new_items.size() - 1);
00585     msg_mapping_.insert(msg_mapping_.end(),
00586                         new_items.begin(),
00587                         new_items.end());
00588     endInsertRows();
00589 
00590     Q_EMIT messagesAdded();
00591   }  
00592 }
00593 
00594 void LogDatabaseProxyModel::processOldMessages()
00595 {
00596   // We process old messages in two steps.  First, we process the
00597   // remaining messages in chunks and store them in the early_mapping_
00598   // buffer if they pass all the filters.  When the early mapping
00599   // buffer is large enough (or we have processed everything), then we
00600   // merge the early_mapping buffer in the main buffer.  This approach
00601   // allows us to process very large logs without causing major lag
00602   // for the user.
00603   
00604   for (size_t i = 0;
00605        earliest_log_index_ != 0 && i < 100;
00606        earliest_log_index_--, i++)
00607   {
00608     const LogEntry &item = db_->log()[earliest_log_index_-1];
00609     if (!acceptLogEntry(item)) {
00610       continue;
00611     }
00612 
00613     for (int i = 0; i < item.text.size(); i++) {
00614       // Note that we have to add the lines backwards to maintain the proper order.
00615       early_mapping_.push_front(
00616         LineMap(earliest_log_index_-1, item.text.size()-1-i));
00617     }
00618   }
00619  
00620   if ((earliest_log_index_ == 0 && early_mapping_.size()) ||
00621       (early_mapping_.size() > 200)) {
00622     beginInsertRows(QModelIndex(),
00623                     0,
00624                     early_mapping_.size() - 1);
00625     msg_mapping_.insert(msg_mapping_.begin(),
00626                         early_mapping_.begin(),
00627                         early_mapping_.end());
00628     early_mapping_.clear();
00629     endInsertRows();
00630 
00631     Q_EMIT messagesAdded();
00632   }
00633 
00634   scheduleIdleProcessing();
00635 }
00636 
00637 void LogDatabaseProxyModel::scheduleIdleProcessing()
00638 {
00639   // If we have older logs that still need to be processed, schedule a
00640   // callback at the next idle time.
00641   if (earliest_log_index_ > 0) {
00642     QTimer::singleShot(0, this, SLOT(processOldMessages()));
00643   }
00644 }
00645 
00646 bool LogDatabaseProxyModel::acceptLogEntry(const LogEntry &item)
00647 {
00648   if (!(item.level & severity_mask_)) {
00649     return false;
00650   }
00651   
00652   if (names_.count(item.node) == 0) {
00653     return false;
00654   }
00655 
00656   if (!testIncludeFilter(item)) {
00657     return false;
00658   }
00659 
00660   if (use_regular_expressions_) {
00661     // For multi-line messages, we join the lines together with a
00662     // space to make it easy for users to use filters that spread
00663     // across the new lines.
00664     
00665     // Don't let an empty regexp filter out everything
00666     return exclude_regexp_.isEmpty() || exclude_regexp_.indexIn(item.text.join(" ")) < 0;
00667   } else {
00668     for (int i = 0; i < exclude_strings_.size(); i++) {
00669       if (item.text.join(" ").contains(exclude_strings_[i], Qt::CaseInsensitive)) {
00670         return false;
00671       }
00672     }
00673   }
00674   
00675   return true;
00676 }
00677 
00678 // Return true if the item message contains at least one of the
00679 // strings in include_filter_.  Always returns true if there are no
00680 // include strings.
00681 bool LogDatabaseProxyModel::testIncludeFilter(const LogEntry &item)
00682 {
00683   if (use_regular_expressions_) {
00684     return include_regexp_.indexIn(item.text.join(" ")) >= 0;
00685   } else {
00686     if (include_strings_.empty()) {
00687       return true;
00688     }
00689 
00690     for (int i = 0; i < include_strings_.size(); i++) {
00691       if (item.text.join(" ").contains(include_strings_[i], Qt::CaseInsensitive)) {
00692         return true;
00693       }
00694     }
00695   }
00696 
00697   return false;
00698 }
00699 
00700 void LogDatabaseProxyModel::minTimeUpdated()
00701 {
00702   if (display_time_ &&
00703       !display_absolute_time_
00704       && msg_mapping_.size()) {
00705     Q_EMIT dataChanged(index(0), index(msg_mapping_.size()));
00706   }  
00707 }
00708 }  // namespace swri_console


swri_console
Author(s): Elliot Johnson
autogenerated on Tue Sep 12 2017 03:09:35