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 <logging/network.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 /** Constructor. 00063 * Special ctor to be used with Gtk::Builder's get_widget_derived(). 00064 * @param cobject Gtk C object 00065 * @param builder Gtk builder 00066 */ 00067 LogView::LogView(BaseObjectType* cobject, 00068 const Glib::RefPtr<Gtk::Builder> &builder) 00069 : Gtk::TreeView(cobject) 00070 { 00071 ctor(); 00072 } 00073 00074 00075 /** Destructor. */ 00076 LogView::~LogView() 00077 { 00078 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00079 if (c && c->connected()) { 00080 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00081 NetworkLogger::MSGTYPE_UNSUBSCRIBE); 00082 c->enqueue(msg); 00083 } 00084 delete __connection_dispatcher; 00085 } 00086 00087 00088 /** Internal constructor method. */ 00089 void 00090 LogView::ctor(const char *hostname, unsigned short int port) 00091 { 00092 __list = Gtk::ListStore::create(__record); 00093 set_model(__list); 00094 __have_recently_added_path = false; 00095 00096 __list->signal_row_inserted().connect(sigc::mem_fun(*this, &LogView::on_row_inserted)); 00097 get_selection()->set_mode(Gtk::SELECTION_NONE); 00098 00099 if ( (hostname != NULL) && (port != 0) ) { 00100 __connection_dispatcher = 00101 new ConnectionDispatcher(hostname, port, FAWKES_CID_NETWORKLOGGER); 00102 } else { 00103 __connection_dispatcher = new ConnectionDispatcher(FAWKES_CID_NETWORKLOGGER); 00104 } 00105 00106 append_column("Level", __record.loglevel); 00107 append_column("Time", __record.time); 00108 int compcol = append_column("Component", __record.component); 00109 int msgcol = append_column("Message", __record.message); 00110 00111 // We stored the number of columns, for an index (which starts at 0) we need 00112 // to subtract 1 00113 compcol -= 1; 00114 msgcol -= 1; 00115 00116 Glib::ListHandle<Gtk::TreeViewColumn *> columns = get_columns(); 00117 int colnum = -1; 00118 for (Glib::ListHandle<Gtk::TreeViewColumn *>::iterator c = columns.begin(); 00119 c != columns.end(); 00120 ++c) 00121 { 00122 ++colnum; 00123 #if GTK_VERSION_GE(3,0) 00124 Gtk::CellRenderer *cell_renderer = (*c)->get_first_cell(); 00125 #else 00126 Gtk::CellRenderer *cell_renderer = (*c)->get_first_cell_renderer(); 00127 #endif 00128 Gtk::CellRendererText *text_renderer = 00129 dynamic_cast<Gtk::CellRendererText *>(cell_renderer); 00130 if ( text_renderer ) { 00131 #ifdef GLIBMM_PROPERTIES_ENABLED 00132 if ( (colnum == compcol) || (colnum == msgcol) ) { 00133 (*c)->set_resizable(); 00134 } 00135 if ( colnum == compcol ) { 00136 text_renderer->property_ellipsize().set_value(Pango::ELLIPSIZE_END); 00137 } 00138 00139 (*c)->add_attribute(text_renderer->property_background_gdk(), __record.background); 00140 (*c)->add_attribute(text_renderer->property_foreground_gdk(), __record.foreground); 00141 #else 00142 (*c)->add_attribute(*text_renderer, "background-gdk", __record.background); 00143 (*c)->add_attribute(*text_renderer, "foreground-gdk", __record.background); 00144 #endif 00145 } 00146 } 00147 00148 __connection_dispatcher->signal_message_received().connect(sigc::mem_fun(*this, &LogView::on_message_received)); 00149 __connection_dispatcher->signal_connected().connect(sigc::mem_fun(*this, &LogView::on_client_connected)); 00150 __connection_dispatcher->signal_disconnected().connect(sigc::mem_fun(*this, &LogView::on_client_disconnected)); 00151 #if GTK_VERSION_LT(3,0) 00152 signal_expose_event().connect_notify(sigc::mem_fun(*this, &LogView::on_expose_notify)); 00153 #endif 00154 } 00155 00156 00157 /** Set FawkesNetworkClient instance. 00158 * @param client Fawkes network client to use 00159 */ 00160 void 00161 LogView::set_client(FawkesNetworkClient *client) 00162 { 00163 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00164 if (c && c->connected()) { 00165 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00166 NetworkLogger::MSGTYPE_UNSUBSCRIBE); 00167 c->enqueue(msg); 00168 } 00169 __connection_dispatcher->set_client(client); 00170 if (client && client->connected()) { 00171 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00172 NetworkLogger::MSGTYPE_SUBSCRIBE); 00173 client->enqueue(msg); 00174 } 00175 } 00176 00177 00178 /** Get the used FawkesNetworkClient. 00179 * @return Fawkes network client instance 00180 */ 00181 FawkesNetworkClient * 00182 LogView::get_client() 00183 { 00184 return __connection_dispatcher->get_client(); 00185 } 00186 00187 00188 /** Get ConnectionDispatcher instance that is used internally. 00189 * @return connection dispatcher 00190 */ 00191 ConnectionDispatcher * 00192 LogView::get_connection_dispatcher() const 00193 { 00194 return __connection_dispatcher; 00195 } 00196 00197 00198 /** Clear all records. */ 00199 void 00200 LogView::clear() 00201 { 00202 __list->clear(); 00203 } 00204 00205 00206 /** Event handler when row inserted. 00207 * @param path path to element 00208 * @param iter iterator to inserted element 00209 */ 00210 void 00211 LogView::on_row_inserted(const Gtk::TreeModel::Path& path, 00212 const Gtk::TreeModel::iterator& iter) 00213 { 00214 Gtk::TreeModel::Path vstart, vend; 00215 Gtk::TreeModel::Path prev = path; 00216 get_visible_range(vstart, vend); 00217 prev = path; 00218 if (! get_visible_range(vstart, vend) || 00219 ( prev.prev() && 00220 ((vend == prev) || 00221 (__have_recently_added_path && (__recently_added_path == prev)))) ) { 00222 scroll_to_row(path); 00223 00224 // the recently added stuff is required if multiple rows are inserted at 00225 // a time. In this case the widget wasn't redrawn and get_visible_range() does 00226 // not give the desired result and we have to "advance" it manually 00227 __have_recently_added_path = true; 00228 __recently_added_path = path; 00229 } 00230 } 00231 00232 #if GTK_VERSION_GE(3,0) 00233 bool 00234 LogView::on_draw(const Cairo::RefPtr<Cairo::Context> &cr) 00235 { 00236 __have_recently_added_path = false; 00237 return Gtk::TreeView::on_draw(cr); 00238 } 00239 #else 00240 void 00241 LogView::on_expose_notify(GdkEventExpose *event) 00242 { 00243 __have_recently_added_path = false; 00244 } 00245 #endif 00246 00247 00248 void 00249 LogView::on_client_connected() 00250 { 00251 FawkesNetworkClient *c = __connection_dispatcher->get_client(); 00252 if (c && c->connected()) { 00253 FawkesNetworkMessage *msg = new FawkesNetworkMessage(FAWKES_CID_NETWORKLOGGER, 00254 NetworkLogger::MSGTYPE_SUBSCRIBE); 00255 c->enqueue(msg); 00256 struct timeval t; 00257 gettimeofday(&t, NULL); 00258 append_message(Logger::LL_DEBUG, t, "LogView", false, "Connected"); 00259 } 00260 } 00261 00262 void 00263 LogView::on_client_disconnected() 00264 { 00265 struct timeval t; 00266 gettimeofday(&t, NULL); 00267 append_message(Logger::LL_ERROR, t, "LogView", false, "*** Connection died. ***"); 00268 } 00269 00270 00271 /** Append a single message. 00272 * @param log_level log level 00273 * @param t time of the message 00274 * @param component component string for the message 00275 * @param is_exception true if essage was produced via an exception 00276 * @param message log message 00277 */ 00278 void 00279 LogView::append_message(Logger::LogLevel log_level, struct timeval t, 00280 const char *component, bool is_exception, 00281 const char *message) 00282 { 00283 const char *loglevel; 00284 const char *timestr; 00285 char* time = NULL; 00286 Gdk::Color color; 00287 bool set_foreground = false; 00288 bool set_background = false; 00289 00290 switch ( log_level ) { 00291 case Logger::LL_DEBUG: 00292 loglevel = "DEBUG"; 00293 color.set_rgb_p(0.4, 0.4, 0.4); 00294 set_foreground = true; 00295 break; 00296 case Logger::LL_INFO: 00297 loglevel = "INFO"; 00298 break; 00299 case Logger::LL_WARN: 00300 loglevel = "WARN"; 00301 color.set_rgb_p(1.0, 1.0, 0.7); 00302 set_background = true; 00303 break; 00304 case Logger::LL_ERROR: 00305 loglevel = "ERROR"; 00306 color.set_rgb_p(1.0, 0.8, 0.8); 00307 set_background = true; 00308 break; 00309 default: 00310 loglevel = "NONE?"; 00311 color.set_rgb_p(1.0, 0.0, 0.0); 00312 set_background = true; 00313 break; 00314 } 00315 00316 struct tm time_tm; 00317 localtime_r(&(t.tv_sec), &time_tm); 00318 if (asprintf(&time, "%02d:%02d:%02d.%06ld", time_tm.tm_hour, 00319 time_tm.tm_min, time_tm.tm_sec, t.tv_usec) == -1) { 00320 timestr = "OutOfMemory"; 00321 } else { 00322 timestr = time; 00323 } 00324 00325 Gtk::TreeModel::Row row = *__list->append(); 00326 row[__record.loglevel] = loglevel; 00327 row[__record.time] = timestr; 00328 row[__record.component] = component; 00329 if ( is_exception ) { 00330 row[__record.message] = std::string("[EXCEPTION] ") + message; 00331 } else { 00332 row[__record.message] = message; 00333 } 00334 if ( set_foreground ) row[__record.foreground] = color; 00335 if ( set_background ) row[__record.background] = color; 00336 00337 if (time) free(time); 00338 } 00339 00340 /** Message received event handler. 00341 * @param msg Fawkes network message just recveived. 00342 */ 00343 void 00344 LogView::on_message_received(FawkesNetworkMessage *msg) 00345 { 00346 if ( (msg->cid() == FAWKES_CID_NETWORKLOGGER) && 00347 (msg->msgid() == NetworkLogger::MSGTYPE_LOGMESSAGE) ) { 00348 00349 NetworkLoggerMessageContent* content = msg->msgc<NetworkLoggerMessageContent>(); 00350 00351 append_message(content->get_loglevel(), content->get_time(), 00352 content->get_component(), 00353 content->is_exception(), content->get_message()); 00354 00355 delete content; 00356 } 00357 } 00358 00359 /** @class LogView::LogRecord <gui_utils/logview.h> 00360 * TreeView record for LogView. 00361 */ 00362 00363 /** Constructor. */ 00364 LogView::LogRecord::LogRecord() 00365 { 00366 add(loglevel); 00367 add(time); 00368 add(component); 00369 add(message); 00370 add(foreground); 00371 add(background); 00372 } 00373 00374 00375 00376 } // end namespace fawkes