Fawkes API Fawkes Development Version
|
00001 00002 /*************************************************************************** 00003 * logview.cpp - Fawkes log view widget 00004 * 00005 * Created: Mon Nov 02 13:19:03 2008 00006 * Copyright 2008 Tim Niemueller [www.niemueller.de] 00007 * 00008 ****************************************************************************/ 00009 00010 /* This program is free software; you can redistribute it and/or modify 00011 * it under the terms of the GNU General Public License as published by 00012 * the Free Software Foundation; either version 2 of the License, or 00013 * (at your option) any later version. A runtime exception applies to 00014 * this software (see LICENSE.GPL_WRE file mentioned below for details). 00015 * 00016 * This program is distributed in the hope that it will be useful, 00017 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00018 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00019 * GNU Library General Public License for more details. 00020 * 00021 * Read the full text in the LICENSE.GPL_WRE file in the doc directory. 00022 */ 00023 00024 #include <gui_utils/logview.h> 00025 #include <gui_utils/connection_dispatcher.h> 00026 #include <netcomm/fawkes/client.h> 00027 #include <netcomm/utils/network_logger.h> 00028 00029 #include <gtkmm.h> 00030 00031 namespace fawkes { 00032 #if 0 /* just to make Emacs auto-indent happy */ 00033 } 00034 #endif 00035 00036 00037 /** @class LogView <gui_utils/logview.h> 00038 * Log View widget. 00039 * This widget derives a Gtk::TreeView and provides an easy way to show 00040 * log messages in a GUI application. 00041 * @author Tim Niemueller 00042 */ 00043 00044 00045 /** Constructor. */ 00046 LogView::LogView() 00047 { 00048 ctor(); 00049 } 00050 00051 00052 /** Constructor. 00053 * @param hostname hostname to set for the FawkesNetworkClient. 00054 * @param port port to set for the FawkesNetworkClient. 00055 */ 00056 LogView::LogView(const char *hostname, unsigned short int port) 00057 { 00058 ctor(hostname, port); 00059 } 00060 00061 00062 #ifdef HAVE_GLADEMM 00063 /** Constructor. 00064 * Special ctor to be used with Glade's get_widget_derived(). 00065 * @param cobject Gtk C object 00066 * @param refxml Glade's XML reference 00067 */ 00068 LogView::LogView(BaseObjectType* cobject, 00069 const Glib::RefPtr<Gnome::Glade::Xml>& refxml) 00070 : Gtk::TreeView(cobject) 00071 { 00072 ctor(); 00073 } 00074 #endif 00075 00076 00077 /** Destructor. */ 00078 LogView::~LogView() 00079 { 00080 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00081 if (c && c->connected()) { 00082 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00083 NetworkLogger::MSGTYPE_UNSUBSCRIBE); 00084 c->enqueue(msg); 00085 } 00086 delete __connection_dispatcher; 00087 } 00088 00089 00090 /** Internal constructor method. */ 00091 void 00092 LogView::ctor(const char *hostname, unsigned short int port) 00093 { 00094 __list = Gtk::ListStore::create(__record); 00095 __have_recently_added_path = false; 00096 00097 __list->signal_row_inserted().connect(sigc::mem_fun(*this, &LogView::on_row_inserted)); 00098 set_model(__list); 00099 get_selection()->set_mode(Gtk::SELECTION_NONE); 00100 00101 if ( (hostname != NULL) && (port != 0) ) { 00102 __connection_dispatcher = new ConnectionDispatcher(hostname, port, FAWKES_CID_NETWORKLOGGER); 00103 } else { 00104 __connection_dispatcher = new ConnectionDispatcher(FAWKES_CID_NETWORKLOGGER); 00105 } 00106 00107 append_column("Level", __record.loglevel); 00108 append_column("Time", __record.time); 00109 int compcol = append_column("Component", __record.component); 00110 int msgcol = append_column("Message", __record.message); 00111 00112 // We stored the number of columns, for an index (which starts at 0) we need 00113 // to subtract 1 00114 compcol -= 1; 00115 msgcol -= 1; 00116 00117 Glib::ListHandle<Gtk::TreeViewColumn *> columns = get_columns(); 00118 int colnum = -1; 00119 for (Glib::ListHandle<Gtk::TreeViewColumn *>::iterator c = columns.begin(); c != columns.end(); ++c) { 00120 ++colnum; 00121 Gtk::CellRenderer *cell_renderer = (*c)->get_first_cell_renderer(); 00122 Gtk::CellRendererText *text_renderer = dynamic_cast<Gtk::CellRendererText *>(cell_renderer); 00123 if ( text_renderer ) { 00124 #ifdef GLIBMM_PROPERTIES_ENABLED 00125 if ( (colnum == compcol) || (colnum == msgcol) ) { 00126 (*c)->set_resizable(); 00127 } 00128 if ( colnum == compcol ) { 00129 text_renderer->property_ellipsize().set_value(Pango::ELLIPSIZE_END); 00130 } 00131 00132 (*c)->add_attribute(text_renderer->property_background_gdk(), __record.background); 00133 (*c)->add_attribute(text_renderer->property_foreground_gdk(), __record.foreground); 00134 #else 00135 (*c)->add_attribute(*text_renderer, "background-gdk", __record.background); 00136 (*c)->add_attribute(*text_renderer, "foreground-gdk", __record.background); 00137 #endif 00138 } 00139 } 00140 00141 __connection_dispatcher->signal_message_received().connect(sigc::mem_fun(*this, &LogView::on_message_received)); 00142 __connection_dispatcher->signal_connected().connect(sigc::mem_fun(*this, &LogView::on_client_connected)); 00143 __connection_dispatcher->signal_disconnected().connect(sigc::mem_fun(*this, &LogView::on_client_disconnected)); 00144 signal_expose_event().connect_notify(sigc::mem_fun(*this, &LogView::on_expose_notify)); 00145 } 00146 00147 00148 /** Set FawkesNetworkClient instance. 00149 * @param client Fawkes network client to use 00150 */ 00151 void 00152 LogView::set_client(FawkesNetworkClient *client) 00153 { 00154 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00155 if (c && c->connected()) { 00156 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00157 NetworkLogger::MSGTYPE_UNSUBSCRIBE); 00158 c->enqueue(msg); 00159 } 00160 __connection_dispatcher->set_client(client); 00161 if (client && client->connected()) { 00162 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00163 NetworkLogger::MSGTYPE_SUBSCRIBE); 00164 client->enqueue(msg); 00165 } 00166 } 00167 00168 00169 /** Get the used FawkesNetworkClient. 00170 * @return Fawkes network client instance 00171 */ 00172 FawkesNetworkClient * 00173 LogView::get_client() 00174 { 00175 return __connection_dispatcher->get_client(); 00176 } 00177 00178 00179 /** Get ConnectionDispatcher instance that is used internally. 00180 * @return connection dispatcher 00181 */ 00182 ConnectionDispatcher * 00183 LogView::get_connection_dispatcher() const 00184 { 00185 return __connection_dispatcher; 00186 } 00187 00188 00189 /** Clear all records. */ 00190 void 00191 LogView::clear() 00192 { 00193 __list->clear(); 00194 } 00195 00196 00197 /** Event handler when row inserted. 00198 * @param path path to element 00199 * @param iter iterator to inserted element 00200 */ 00201 void 00202 LogView::on_row_inserted(const Gtk::TreeModel::Path& path, 00203 const Gtk::TreeModel::iterator& iter) 00204 { 00205 Gtk::TreeModel::Path vstart, vend; 00206 Gtk::TreeModel::Path prev = path; 00207 get_visible_range(vstart, vend); 00208 prev = path; 00209 if (! get_visible_range(vstart, vend) || 00210 ( prev.prev() && 00211 ((vend == prev) || 00212 (__have_recently_added_path && (__recently_added_path == prev)))) ) { 00213 scroll_to_row(path); 00214 00215 // the recently added stuff is required if multiple rows are inserted at 00216 // a time. In this case the widget wasn't redrawn and get_visible_range() does 00217 // not give the desired result and we have to "advance" it manually 00218 __have_recently_added_path = true; 00219 __recently_added_path = path; 00220 } 00221 } 00222 00223 00224 void 00225 LogView::on_expose_notify(GdkEventExpose *event) 00226 { 00227 __have_recently_added_path = false; 00228 } 00229 00230 void 00231 LogView::on_client_connected() 00232 { 00233 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00234 if (c && c->connected()) { 00235 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00236 NetworkLogger::MSGTYPE_SUBSCRIBE); 00237 c->enqueue(msg); 00238 struct timeval t; 00239 gettimeofday(&t, NULL); 00240 append_message(Logger::LL_DEBUG, t, "LogView", false, "Connected"); 00241 } 00242 } 00243 00244 void 00245 LogView::on_client_disconnected() 00246 { 00247 struct timeval t; 00248 gettimeofday(&t, NULL); 00249 append_message(Logger::LL_ERROR, t, "LogView", false, "*** Connection died. ***"); 00250 } 00251 00252 00253 /** Append a single message. 00254 * @param log_level log level 00255 * @param t time of the message 00256 * @param component component string for the message 00257 * @param is_exception true if essage was produced via an exception 00258 * @param message log message 00259 */ 00260 void 00261 LogView::append_message(Logger::LogLevel log_level, struct timeval t, 00262 const char *component, bool is_exception, 00263 const char *message) 00264 { 00265 const char *loglevel; 00266 const char *timestr; 00267 char* time = NULL; 00268 Gdk::Color color; 00269 bool set_foreground = false; 00270 bool set_background = false; 00271 00272 switch ( log_level ) { 00273 case Logger::LL_DEBUG: 00274 loglevel = "DEBUG"; 00275 color.set_rgb_p(0.4, 0.4, 0.4); 00276 set_foreground = true; 00277 break; 00278 case Logger::LL_INFO: 00279 loglevel = "INFO"; 00280 break; 00281 case Logger::LL_WARN: 00282 loglevel = "WARN"; 00283 color.set_rgb_p(1.0, 1.0, 0.7); 00284 set_background = true; 00285 break; 00286 case Logger::LL_ERROR: 00287 loglevel = "ERROR"; 00288 color.set_rgb_p(1.0, 0.8, 0.8); 00289 set_background = true; 00290 break; 00291 default: 00292 loglevel = "NONE?"; 00293 color.set_rgb_p(1.0, 0.0, 0.0); 00294 set_background = true; 00295 break; 00296 } 00297 00298 struct tm time_tm; 00299 localtime_r(&(t.tv_sec), &time_tm); 00300 if (asprintf(&time, "%02d:%02d:%02d.%06ld", time_tm.tm_hour, 00301 time_tm.tm_min, time_tm.tm_sec, t.tv_usec) == -1) { 00302 timestr = "OutOfMemory"; 00303 } else { 00304 timestr = time; 00305 } 00306 00307 Gtk::TreeModel::Row row = *__list->append(); 00308 row[__record.loglevel] = loglevel; 00309 row[__record.time] = timestr; 00310 row[__record.component] = component; 00311 if ( is_exception ) { 00312 row[__record.message] = std::string("[EXCEPTION] ") + message; 00313 } else { 00314 row[__record.message] = message; 00315 } 00316 if ( set_foreground ) row[__record.foreground] = color; 00317 if ( set_background ) row[__record.background] = color; 00318 00319 if (time) free(time); 00320 } 00321 00322 /** Message received event handler. 00323 * @param msg Fawkes network message just recveived. 00324 */ 00325 void 00326 LogView::on_message_received(FawkesNetworkMessage *msg) 00327 { 00328 if ( (msg->cid() == FAWKES_CID_NETWORKLOGGER) && 00329 (msg->msgid() == NetworkLogger::MSGTYPE_LOGMESSAGE) ) { 00330 00331 NetworkLoggerMessageContent* content = msg->msgc<NetworkLoggerMessageContent>(); 00332 00333 append_message(content->get_loglevel(), content->get_time(), 00334 content->get_component(), 00335 content->is_exception(), content->get_message()); 00336 00337 delete content; 00338 } 00339 } 00340 00341 /** @class LogView::LogRecord <gui_utils/logview.h> 00342 * TreeView record for LogView. 00343 */ 00344 00345 /** Constructor. */ 00346 LogView::LogRecord::LogRecord() 00347 { 00348 add(loglevel); 00349 add(time); 00350 add(component); 00351 add(message); 00352 add(foreground); 00353 add(background); 00354 } 00355 00356 00357 00358 } // end namespace fawkes