rosout_list_control.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (c) 2008, Willow Garage, Inc.
00003  * All rights reserved.
00004  *
00005  * Redistribution and use in source and binary forms, with or without
00006  * modification, are permitted provided that the following conditions are met:
00007  *
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 Willow Garage, Inc. nor the names of its
00014  *       contributors may be used to endorse or promote products derived from
00015  *       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 THE COPYRIGHT OWNER OR CONTRIBUTORS BE
00021  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
00022  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
00023  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
00024  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
00025  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
00026  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00027  * POSSIBILITY OF SUCH DAMAGE.
00028  */
00029 
00030 #include "rosout_list_control.h"
00031 #include "rosout_generated.h"
00032 #include "rosout_panel.h"
00033 #include "rosout_text_filter.h"
00034 
00035 #include <ros/assert.h>
00036 
00037 #include <boost/algorithm/string/join.hpp>
00038 #include <boost/regex.hpp>
00039 
00040 #include <wx/imaglist.h>
00041 #include <wx/artprov.h>
00042 #include <wx/clipbrd.h>
00043 
00044 #include <sstream>
00045 
00046 namespace rxtools
00047 {
00048 
00049 RosoutListControl::RosoutListControl(wxWindow* parent, wxWindowID id, const wxPoint& pos, const wxSize& size, long style, const wxValidator& validator,
00050                                      const wxString& name)
00051 : wxListCtrl(parent, id, pos, size, style, validator, name)
00052 , last_selection_(-1)
00053 , scrollbar_at_bottom_(true)
00054 , disable_scroll_to_bottom_(false)
00055 #if __WXMAC__
00056 , manual_selection_(false)
00057 #endif
00058 {
00059   wxListItem item;
00060   item.SetText(wxT("Message"));
00061   item.SetWidth(600);
00062   InsertColumn(columns::Message, item);
00063   item.SetText(wxT("Severity"));
00064   item.SetWidth(100);
00065   InsertColumn(columns::Severity, item);
00066   item.SetText(wxT("Node"));
00067   item.SetWidth(200);
00068   InsertColumn(columns::Node, item);
00069   item.SetText(wxT("Time"));
00070   item.SetWidth(200);
00071   InsertColumn(columns::Time, item);
00072   item.SetText(wxT("Topics"));
00073   item.SetWidth(200);
00074   InsertColumn(columns::Topics, item);
00075   item.SetText(wxT("Location"));
00076   item.SetWidth(600);
00077   InsertColumn(columns::Location, item);
00078 
00079 
00080   wxImageList* image_list = new wxImageList(16, 16);
00081   fatal_image_id_ = image_list->Add(wxArtProvider::GetIcon(wxART_CROSS_MARK, wxART_OTHER, wxSize(16, 16)));
00082   error_image_id_ = image_list->Add(wxArtProvider::GetIcon(wxART_ERROR, wxART_OTHER, wxSize(16, 16)));
00083   warning_image_id_ = image_list->Add(wxArtProvider::GetIcon(wxART_WARNING, wxART_OTHER, wxSize(16, 16)));
00084   info_image_id_ = image_list->Add(wxArtProvider::GetIcon(wxART_INFORMATION, wxART_OTHER, wxSize(16, 16)));
00085   debug_image_id_ = image_list->Add(wxArtProvider::GetIcon(wxART_HELP, wxART_OTHER, wxSize(16, 16)));
00086 
00087   AssignImageList(image_list, wxIMAGE_LIST_SMALL);
00088 
00089   Connect(wxEVT_COMMAND_LIST_ITEM_ACTIVATED, wxListEventHandler(RosoutListControl::onItemActivated), NULL, this);
00090   Connect(wxEVT_COMMAND_LIST_ITEM_RIGHT_CLICK, wxListEventHandler(RosoutListControl::onItemRightClick), NULL, this);
00091   Connect(wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler(RosoutListControl::onItemSelected), NULL, this);
00092   Connect(wxEVT_CHAR, wxKeyEventHandler(RosoutListControl::onChar));
00093 
00094 }
00095 
00096 RosoutListControl::~RosoutListControl()
00097 {
00098 }
00099 
00100 void RosoutListControl::setModel(RosoutPanel* model)
00101 {
00102   model_ = model;
00103 }
00104 
00105 wxString RosoutListControl::getSeverityText(const rosgraph_msgs::LogConstPtr& message) const
00106 {
00107   switch (message->level)
00108   {
00109   case rosgraph_msgs::Log::DEBUG:
00110     return wxT("Debug");
00111   case rosgraph_msgs::Log::INFO:
00112     return wxT("Info");
00113   case rosgraph_msgs::Log::WARN:
00114     return wxT("Warn");
00115   case rosgraph_msgs::Log::ERROR:
00116     return wxT("Error");
00117   case rosgraph_msgs::Log::FATAL:
00118     return wxT("Fatal");
00119   }
00120 
00121   return wxT("Unknown");
00122 }
00123 
00124 int RosoutListControl::OnGetItemImage(long item) const
00125 {
00126   ROS_ASSERT(model_);
00127 
00128   rosgraph_msgs::LogConstPtr message = model_->getMessageByIndex(item);
00129   if (!message)
00130   {
00131     return -1;
00132   }
00133 
00134   switch (message->level)
00135   {
00136   case rosgraph_msgs::Log::DEBUG:
00137     return debug_image_id_;
00138   case rosgraph_msgs::Log::INFO:
00139     return info_image_id_;
00140   case rosgraph_msgs::Log::WARN:
00141     return warning_image_id_;
00142   case rosgraph_msgs::Log::ERROR:
00143     return error_image_id_;
00144   case rosgraph_msgs::Log::FATAL:
00145     return fatal_image_id_;
00146   }
00147 
00148   return -1;
00149 }
00150 
00151 wxListItemAttr * RosoutListControl::OnGetItemAttr(long item) const
00152 {
00153 #if 0
00154   ROS_ASSERT(model_);
00155 
00156   const rosgraph_msgs::Log& message = model_->getMessageByIndex(item);
00157 
00158   switch( message->level )
00159   {
00160     case rosgraph_msgs::Log::DEBUG:
00161     attr_.SetBackgroundColour( wxColour( 204, 255, 204 ) );
00162     break;
00163     case rosgraph_msgs::Log::INFO:
00164     attr_.SetBackgroundColour( *wxWHITE );
00165     break;
00166     case rosgraph_msgs::Log::WARN:
00167     attr_.SetBackgroundColour( wxColour( 255, 255, 153 ) );
00168     break;
00169     case rosgraph_msgs::Log::ERROR:
00170     attr_.SetBackgroundColour( wxColour( 255, 153, 0 ) );
00171     break;
00172     case rosgraph_msgs::Log::FATAL:
00173     attr_.SetBackgroundColour( *wxRED );
00174     break;
00175     default:
00176     ROS_BREAK();
00177   }
00178 #endif
00179 
00180   return &attr_;
00181 }
00182 
00183 wxString RosoutListControl::OnGetItemText(long item, long column) const
00184 {
00185   ROS_ASSERT(model_);
00186 
00187   rosgraph_msgs::LogConstPtr message = model_->getMessageByIndex(item);
00188   if (!message)
00189   {
00190     return wxString();
00191   }
00192 
00193   switch (column)
00194   {
00195   case columns::Severity:
00196   {
00197     return getSeverityText(message);
00198   }
00199     break;
00200   case columns::Time:
00201   {
00202     std::stringstream ss;
00203     ss << message->header.stamp;
00204     return wxString::FromAscii(ss.str().c_str());
00205   }
00206   case columns::Message:
00207   {
00208     std::string msg = message->msg;
00209     size_t pos = std::string::npos;
00210     while (true)
00211     {
00212       pos = msg.find('\n');
00213       if (pos == std::string::npos)
00214       {
00215         break;
00216       }
00217 
00218       msg.replace(pos, 1, "\\n");
00219     }
00220     while (true)
00221     {
00222       pos = msg.find('\r');
00223       if (pos == std::string::npos)
00224       {
00225         break;
00226       }
00227 
00228       msg.replace(pos, 1, "\\r");
00229     }
00230     return wxString::FromAscii(msg.c_str());
00231   }
00232   case columns::Topics:
00233   {
00234     std::stringstream ss;
00235     typedef std::vector<std::string> V_string;
00236     V_string::const_iterator it = message->topics.begin();
00237     V_string::const_iterator end = message->topics.end();
00238     for (; it != end; ++it)
00239     {
00240       if (it != message->topics.begin())
00241       {
00242         ss << ", ";
00243       }
00244 
00245       ss << *it;
00246     }
00247 
00248     return wxString::FromAscii(ss.str().c_str());
00249   }
00250   case columns::Location:
00251   {
00252     wxString str;
00253     str << wxString::FromAscii(message->file.c_str()) << wxT(":") << wxString::FromAscii(message->function.c_str()) << wxT(":") << message->line;
00254     return str;
00255   }
00256   case columns::Node:
00257   {
00258     return wxString::FromAscii(message->name.c_str());
00259   }
00260   }
00261 
00262   ROS_BREAK();
00263   return wxT("Unknown Column");
00264 }
00265 
00266 void TextboxDialog::onChar(wxKeyEvent& event)
00267 {
00268   int key = event.GetKeyCode();
00269   if (key == WXK_ESCAPE)
00270   {
00271     wxObject* obj = event.GetEventObject();
00272     wxWindow* win = wxDynamicCast(obj, wxWindow);
00273     if (win)
00274     {
00275       if (win->GetParent())
00276       {
00277         win->GetParent()->Close();
00278       }
00279       else
00280       {
00281         win->Close();
00282       }
00283     }
00284   }
00285   // For some reason the built-in ctrl-C handling doesn't work in linux
00286   else if (key == 3)
00287   {
00288     wxObject* obj = event.GetEventObject();
00289     wxRichTextCtrl* text = wxDynamicCast(obj, wxRichTextCtrl);
00290     if (text)
00291     {
00292       text->Copy();
00293     }
00294 
00295     event.Skip();
00296   }
00297   else
00298   {
00299     event.Skip();
00300   }
00301 }
00302 
00303 void RosoutListControl::onItemActivated(wxListEvent& event)
00304 {
00305   ROS_ASSERT(model_);
00306 
00307   rosgraph_msgs::LogConstPtr message = model_->getMessageByIndex(event.GetIndex());
00308   if (!message)
00309   {
00310     return;
00311   }
00312 
00313   TextboxDialog* dialog = new TextboxDialog(this, wxID_ANY);
00314   dialog->Show();
00315   dialog->text_->SetFocus();
00316 
00317   wxRichTextCtrl& t = *dialog->text_;
00318 
00319   // Node name
00320   {
00321     t.BeginBold();
00322     t.WriteText(wxT("Node: "));
00323     t.EndBold();
00324     t.WriteText(wxString::FromAscii(message->name.c_str()));
00325     t.Newline();
00326   }
00327 
00328   // Time
00329   {
00330     t.BeginBold();
00331     t.WriteText(wxT("Time: "));
00332     t.EndBold();
00333     std::stringstream ss;
00334     ss << message->header.stamp;
00335     t.WriteText(wxString::FromAscii(ss.str().c_str()));
00336     t.Newline();
00337   }
00338 
00339   // Severity
00340   {
00341     t.BeginBold();
00342     t.WriteText(wxT("Severity: "));
00343     t.EndBold();
00344     t.WriteText(getSeverityText(message));
00345     t.Newline();
00346   }
00347 
00348   // Location
00349   {
00350     if (!message->file.empty())
00351     {
00352       t.BeginBold();
00353       t.WriteText(wxT("Location: "));
00354       t.EndBold();
00355       std::stringstream ss;
00356       ss << message->file << ":" << message->function << ":" << message->line;
00357       t.WriteText(wxString::FromAscii(ss.str().c_str()));
00358       t.Newline();
00359     }
00360   }
00361 
00362   // Published Topics
00363   {
00364     t.BeginBold();
00365     t.WriteText(wxT("Published Topics: "));
00366     t.EndBold();
00367     t.WriteText(wxString::FromAscii(boost::join(message->topics, ", ").c_str()));
00368     t.Newline();
00369   }
00370 
00371   // Message
00372   {
00373     t.Newline();
00374     t.BeginTextColour(wxColour(127, 61, 2));
00375     t.BeginBold();
00376     t.WriteText(wxString::FromAscii(message->msg.c_str()));
00377     t.EndTextColour();
00378     t.EndBold();
00379   }
00380 }
00381 
00382 std::string escapeForRegex(const std::string& str)
00383 {
00384   const static boost::regex esc("[\\^\\.\\$\\|\\(\\)\\[\\]\\*\\+\\?\\/\\\\]");
00385   const static std::string rep("\\\\\\1&");
00386   return boost::regex_replace(str, esc, rep, boost::match_default | boost::format_sed);
00387 }
00388 
00389 void addFilter(RosoutPanel* model, const std::string& text, uint32_t field_mask, bool include, bool new_window)
00390 {
00391   if (new_window)
00392   {
00393     RosoutFrame* frame = model->createNewFrame();
00394     model = frame->rosout_panel_;
00395     model->clearFilters();
00396   }
00397 
00398   RosoutTextFilterPtr filter = model->createTextFilter();
00399   filter->setFilterType(include ? RosoutTextFilter::Include : RosoutTextFilter::Exclude);
00400   filter->setFieldMask(field_mask);
00401   filter->setText("^" + escapeForRegex(text) + "$");
00402   filter->setUseRegex(true);
00403   model->refilter();
00404 }
00405 
00406 rosgraph_msgs::LogConstPtr RosoutListControl::getSelectedMessage()
00407 {
00408   if (last_selection_ == -1)
00409   {
00410     return rosgraph_msgs::LogConstPtr();
00411   }
00412 
00413   return model_->getMessageByIndex(last_selection_);
00414 }
00415 
00416 void RosoutListControl::onExcludeLocation(wxCommandEvent& event)
00417 {
00418   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00419   {
00420     std::stringstream ss;
00421     ss << message->file << ":" << message->function << ":" << message->line;
00422     addFilter(model_, ss.str(), RosoutTextFilter::Location, false, false);
00423   }
00424 }
00425 
00426 void RosoutListControl::onExcludeNode(wxCommandEvent& event)
00427 {
00428   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00429   {
00430     addFilter(model_, message->name, RosoutTextFilter::Node, false, false);
00431   }
00432 }
00433 
00434 void RosoutListControl::onExcludeMessage(wxCommandEvent& event)
00435 {
00436   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00437   {
00438     addFilter(model_, message->msg, RosoutTextFilter::Message, false, false);
00439   }
00440 }
00441 
00442 void RosoutListControl::onExcludeLocationNewWindow(wxCommandEvent& event)
00443 {
00444   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00445   {
00446     std::stringstream ss;
00447     ss << message->file << ":" << message->function << ":" << message->line;
00448     addFilter(model_, ss.str(), RosoutTextFilter::Location, false, true);
00449   }
00450 }
00451 
00452 void RosoutListControl::onExcludeNodeNewWindow(wxCommandEvent& event)
00453 {
00454   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00455   {
00456     addFilter(model_, message->name, RosoutTextFilter::Node, false, true);
00457   }
00458 }
00459 
00460 void RosoutListControl::onExcludeMessageNewWindow(wxCommandEvent& event)
00461 {
00462   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00463   {
00464     addFilter(model_, message->msg, RosoutTextFilter::Message, false, true);
00465   }
00466 }
00467 
00468 void RosoutListControl::onIncludeLocation(wxCommandEvent& event)
00469 {
00470   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00471   {
00472     std::stringstream ss;
00473     ss << message->file << ":" << message->function << ":" << message->line;
00474     addFilter(model_, ss.str(), RosoutTextFilter::Location, true, false);
00475   }
00476 }
00477 
00478 void RosoutListControl::onIncludeNode(wxCommandEvent& event)
00479 {
00480   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00481   {
00482     addFilter(model_, message->name, RosoutTextFilter::Node, true, false);
00483   }
00484 }
00485 
00486 void RosoutListControl::onIncludeMessage(wxCommandEvent& event)
00487 {
00488   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00489   {
00490     addFilter(model_, message->msg, RosoutTextFilter::Message, true, false);
00491   }
00492 }
00493 
00494 void RosoutListControl::onIncludeLocationNewWindow(wxCommandEvent& event)
00495 {
00496   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00497   {
00498     std::stringstream ss;
00499     ss << message->file << ":" << message->function << ":" << message->line;
00500     addFilter(model_, ss.str(), RosoutTextFilter::Location, true, true);
00501   }
00502 }
00503 
00504 void RosoutListControl::onIncludeNodeNewWindow(wxCommandEvent& event)
00505 {
00506   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00507   {
00508     addFilter(model_, message->name, RosoutTextFilter::Node, true, true);
00509   }
00510 }
00511 
00512 void RosoutListControl::onIncludeMessageNewWindow(wxCommandEvent& event)
00513 {
00514   if (rosgraph_msgs::LogConstPtr message = getSelectedMessage())
00515   {
00516     addFilter(model_, message->msg, RosoutTextFilter::Message, true, true);
00517   }
00518 }
00519 
00520 void RosoutListControl::copySelectionToClipboard(bool message_only)
00521 {
00522   updateSelection();
00523 
00524   std::stringstream ss;
00525   S_int32::const_iterator it = selection_.begin();
00526   S_int32::const_iterator end = selection_.end();
00527   for (; it != end; ++it)
00528   {
00529     int32_t index = *it;
00530 
00531     if (it != selection_.begin())
00532     {
00533       ss << std::endl << std::endl;
00534     }
00535 
00536     rosgraph_msgs::LogConstPtr message = model_->getMessageByIndex(index);
00537     if (message)
00538     {
00539       if (message_only)
00540       {
00541         ss << message->msg;
00542       }
00543       else
00544       {
00545         ss << *message;
00546       }
00547     }
00548   }
00549 
00550   if (wxTheClipboard->Open())
00551   {
00552     wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(ss.str().c_str())));
00553     wxTheClipboard->Close();
00554   }
00555 }
00556 
00557 void RosoutListControl::onCopy(wxCommandEvent& event)
00558 {
00559   copySelectionToClipboard(false);
00560 }
00561 
00562 void RosoutListControl::onCopyMessageOnly(wxCommandEvent& event)
00563 {
00564   copySelectionToClipboard(true);
00565 }
00566 
00567 void RosoutListControl::onChar(wxKeyEvent& event)
00568 {
00569   int key = event.GetKeyCode();
00570   // Ctrl-C (copy)
00571   if (key == 3)
00572   {
00573     copySelectionToClipboard(false);
00574     event.Skip();
00575   }
00576 }
00577 
00578 void RosoutListControl::onItemRightClick(wxListEvent& event)
00579 {
00580   updateSelection();
00581   ROS_ASSERT(model_);
00582 
00583   wxMenu* menu = new wxMenu(wxT(""));
00584   wxMenuItem* item = 0;
00585 
00586   item = menu->Append(wxID_COPY, wxT("&Copy\tCtrl+C"));
00587   Connect(wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onCopy));
00588   item = menu->Append(wxID_ANY, wxT("Copy &Message Only"));
00589   Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onCopyMessageOnly), NULL, this);
00590 
00591   // Can only create new filters on 1 message at a time
00592   if (selection_.size() == 1)
00593   {
00594     rosgraph_msgs::LogConstPtr message = model_->getMessageByIndex(event.GetIndex());
00595     if (message)
00596     {
00597       wxMenu* exclude_menu = new wxMenu(wxT(""));
00598       wxMenu* include_menu = new wxMenu(wxT(""));
00599 
00600       // Setup the include menu
00601       {
00602         if (!message->file.empty())
00603         {
00604           item = include_menu->Append(wxID_ANY, wxT("This Location"));
00605           Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeLocation), NULL, this);
00606           item = include_menu->Append(wxID_ANY, wxT("This Location (New Window)"));
00607           Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeLocationNewWindow), NULL, this);
00608         }
00609 
00610         item = include_menu->Append(wxID_ANY, wxT("This Node"));
00611         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeNode), NULL, this);
00612         item = include_menu->Append(wxID_ANY, wxT("This Node (New Window)"));
00613         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeNodeNewWindow), NULL, this);
00614         item = include_menu->Append(wxID_ANY, wxT("This Message"));
00615         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeMessage), NULL, this);
00616         item = include_menu->Append(wxID_ANY, wxT("This Message (New Window)"));
00617         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onIncludeMessageNewWindow), NULL, this);
00618 
00619         menu->AppendSubMenu(include_menu, wxT("&Include"));
00620       }
00621 
00622       // Setup the exclude menu
00623       {
00624         if (!message->file.empty())
00625         {
00626           item = exclude_menu->Append(wxID_ANY, wxT("This Location"));
00627           Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeLocation), NULL, this);
00628           item = exclude_menu->Append(wxID_ANY, wxT("This Location (New Window)"));
00629           Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeLocationNewWindow), NULL, this);
00630         }
00631 
00632         item = exclude_menu->Append(wxID_ANY, wxT("This Node"));
00633         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeNode), NULL, this);
00634         item = exclude_menu->Append(wxID_ANY, wxT("This Node (New Window)"));
00635         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeNodeNewWindow), NULL, this);
00636         item = exclude_menu->Append(wxID_ANY, wxT("This Message"));
00637         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeMessage), NULL, this);
00638         item = exclude_menu->Append(wxID_ANY, wxT("This Message (New Window)"));
00639         Connect(item->GetId(), wxEVT_COMMAND_MENU_SELECTED, wxCommandEventHandler(RosoutListControl::onExcludeMessageNewWindow), NULL, this);
00640 
00641         menu->AppendSubMenu(exclude_menu, wxT("&Exclude"));
00642       }
00643     }
00644   }
00645 
00646   PopupMenu(menu);
00647 }
00648 
00649 void RosoutListControl::updateSelection()
00650 {
00651   selection_.clear();
00652 
00653   int32_t index = -1;
00654   while (true)
00655   {
00656     index = GetNextItem(index, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
00657     if (index == -1)
00658     {
00659       break;
00660     }
00661 
00662     selection_.insert(index);
00663   }
00664 }
00665 
00666 void RosoutListControl::onItemSelected(wxListEvent& event)
00667 {
00668   last_selection_ = event.GetIndex();
00669 
00670   updateSelection();
00671 
00672 #if __WXMAC__
00673   if (!manual_selection_)
00674   {
00675 #endif
00676 
00677   disable_scroll_to_bottom_ = true;
00678 
00679 #if __WXMAC__
00680   }
00681   manual_selection_ = false;
00682 #endif
00683 }
00684 
00685 void RosoutListControl::preItemChanges()
00686 {
00688   scrollbar_at_bottom_ = false;
00689   int count_per_page = GetCountPerPage();
00690   int scroll_pos = GetScrollPos(wxVERTICAL);
00691 #if __WXMAC__
00692   // wxListCtrl::GetScrollPos has different behavior on OSX, adjust for that
00693   //--count_per_page;
00694   int32_t item_height = 20;
00695   if (GetItemCount() > 0)
00696   {
00697     wxRect rect;
00698     if (GetItemRect(0, rect))
00699     {
00700       // For some reason this is always returning -1 right now, so we default to 20 (above)
00701       if (rect.GetHeight() > 0)
00702       {
00703         item_height = rect.GetHeight();
00704       }
00705     }
00706   }
00707 
00708   scroll_pos /= item_height;
00709 #endif
00710   if (scroll_pos + count_per_page >= GetItemCount())
00711   {
00712     scrollbar_at_bottom_ = true;
00713   }
00714 
00715   Freeze();
00716 }
00717 
00718 void RosoutListControl::postItemChanges()
00719 {
00720   if (!disable_scroll_to_bottom_ && scrollbar_at_bottom_ && GetItemCount() > 0)
00721   {
00722     EnsureVisible(GetItemCount() - 1);
00723   }
00724 
00725   disable_scroll_to_bottom_ = false;
00726 
00727   // OSX has a problem where it unselects whatever was selected after adding items.
00728 #if __WXMAC__
00729   setSelection(selection_);
00730 #endif
00731 
00732   Thaw();
00733 
00734   // This for some reason prevents the control from flickering: http://wiki.wxwidgets.org/Flicker-Free_Drawing#No-flickering_for_wxListCtrl_with_wxLC_REPORT_.7C_wxLC_VIRTUAL_style
00735 #if wxMAJOR_VERSION == 2 and wxMINOR_VERSION == 8 // If wxWidgets 2.8.x
00736   wxIdleEvent idle;
00737   wxTheApp->SendIdleEvents(this, idle);
00738 #endif
00739 }
00740 
00741 void RosoutListControl::setSelection(const S_int32& sel)
00742 {
00743 #if __WXMAC__
00744   manual_selection_ = true;
00745 #endif
00746 
00747   // Select anything in the new selection set
00748   {
00749     S_int32::const_iterator it = sel.begin();
00750     S_int32::const_iterator end = sel.end();
00751     for (; it != end; ++it)
00752     {
00753       int32_t index = *it;
00754       ROS_ASSERT(index >= 0);
00755 
00756       SetItemState(index, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
00757     }
00758   }
00759 
00760   // Deselect anything that was selected but is no longer
00761   {
00762     S_int32::const_iterator it = selection_.begin();
00763     S_int32::const_iterator end = selection_.end();
00764     for (; it != end; ++it)
00765     {
00766       int32_t index = *it;
00767       ROS_ASSERT(index >= 0);
00768 
00769       SetItemState(index, 0, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
00770     }
00771   }
00772 
00773   selection_ = sel;
00774 }
00775 
00776 } // namespace rxtools


rxtools
Author(s): Josh Faust, Rob Wheeler, Ken Conley
autogenerated on Mon Oct 6 2014 07:25:59