Back to home page

Project CMSSW displayed by LXR

 
 

    


File indexing completed on 2024-04-06 12:11:40

0001 // -*- C++ -*-
0002 //
0003 // Package:     Core
0004 // Class  :     FWGUIEventDataAdder
0005 //
0006 // Implementation:
0007 //     <Notes on implementation>
0008 //
0009 // Original Author:  Chris Jones
0010 //         Created:  Fri Jun 13 09:58:53 EDT 2008
0011 //
0012 
0013 // system include files
0014 #include <iostream>
0015 #include <sigc++/signal.h>
0016 #include <functional>
0017 #include <algorithm>
0018 #include <cctype>
0019 #include <string>
0020 
0021 #include "TGFrame.h"
0022 #include "TGTextEntry.h"
0023 #include "TGButton.h"
0024 #include "TGMsgBox.h"
0025 #include "TClass.h"
0026 #include "TFile.h"
0027 #include "TTree.h"
0028 #include "TBranch.h"
0029 
0030 // user include files
0031 #include "Fireworks/Core/src/FWGUIEventDataAdder.h"
0032 #include "Fireworks/Core/interface/FWPhysicsObjectDesc.h"
0033 #include "Fireworks/Core/interface/FWEventItemsManager.h"
0034 #include "Fireworks/Core/interface/FWEventItem.h"
0035 #include "Fireworks/Core/interface/FWItemAccessorFactory.h"
0036 #include "Fireworks/Core/interface/FWJobMetadataManager.h"
0037 #include "Fireworks/TableWidget/interface/FWTableWidget.h"
0038 #include "Fireworks/TableWidget/interface/FWTableManagerBase.h"
0039 #include "Fireworks/TableWidget/interface/FWTextTableCellRenderer.h"
0040 #include "Fireworks/Core/interface/fwLog.h"
0041 #include "Fireworks/Core/interface/FWDialogBuilder.h"
0042 //
0043 // constants, enums and typedefs
0044 //
0045 static const std::string &dataForColumn(const FWJobMetadataManager::Data &iData, int iCol) {
0046   switch (iCol) {
0047     case 0:
0048       return iData.purpose_;
0049       break;
0050     case 4:
0051       return iData.type_;
0052       break;
0053     case 1:
0054       return iData.moduleLabel_;
0055       break;
0056     case 2:
0057       return iData.productInstanceLabel_;
0058       break;
0059     case 3:
0060       return iData.processName_;
0061       break;
0062     default:
0063       break;
0064   }
0065   static const std::string s_blank;
0066   return s_blank;
0067 }
0068 
0069 static const unsigned int kNColumns = 5;
0070 class DataAdderTableManager : public FWTableManagerBase {
0071 public:
0072   DataAdderTableManager(FWJobMetadataManager *manager) : m_manager(manager), m_selectedRow(-1), m_filter() { reset(); }
0073 
0074   int numberOfRows() const override { return m_row_to_index.size(); }
0075   int numberOfColumns() const override { return kNColumns; }
0076 
0077   /** Updates the table using the passed @a filter.
0078        Notice that in this case we reset the sorting and show results by those
0079        best matching the filter.
0080      */
0081   virtual void sortWithFilter(const char *filter) {
0082     m_filter = filter;
0083     sort(-1, sortOrder());
0084     dataChanged();
0085   }
0086 
0087   int unsortedRowNumber(int iSortedRowNumber) const override { return m_row_to_index[iSortedRowNumber]; }
0088 
0089   void implSort(int col, bool sortOrder) override;
0090   std::vector<std::string> getTitles() const override {
0091     std::vector<std::string> returnValue;
0092     returnValue.reserve(kNColumns);
0093     returnValue.push_back("Purpose");
0094     returnValue.push_back("Module Label");
0095     returnValue.push_back("Product Instance Label");
0096     returnValue.push_back("Process Name");
0097     returnValue.push_back("C++ Class");
0098     return returnValue;
0099   }
0100 
0101   FWTableCellRendererBase *cellRenderer(int iSortedRowNumber, int iCol) const override {
0102     if (static_cast<int>(m_row_to_index.size()) > iSortedRowNumber) {
0103       int unsortedRow = m_row_to_index[iSortedRowNumber];
0104       const FWJobMetadataManager::Data &data = (m_manager->usableData())[unsortedRow];
0105 
0106       m_renderer.setData(dataForColumn(data, iCol), m_selectedRow == unsortedRow);
0107     } else {
0108       m_renderer.setData(std::string(), false);
0109     }
0110     return &m_renderer;
0111   }
0112 
0113   void setSelection(int row, int mask) {
0114     if (mask == 4) {
0115       if (row == m_selectedRow) {
0116         row = -1;
0117       }
0118     }
0119     changeSelection(row);
0120   }
0121 
0122   virtual const std::string title() const { return "Viewable Collections"; }
0123 
0124   int selectedRow() const { return m_selectedRow; }
0125   //virtual void sort (int col, bool reset = false);
0126   virtual bool rowIsSelected(int row) const { return m_selectedRow == row; }
0127 
0128   void reset() {
0129     changeSelection(-1);
0130     m_row_to_index.clear();
0131     m_row_to_index.reserve(m_manager->usableData().size());
0132     for (unsigned int i = 0; i < m_manager->usableData().size(); ++i) {
0133       m_row_to_index.push_back(i);
0134     }
0135     dataChanged();
0136   }
0137   sigc::signal<void(int)> indexSelected_;
0138 
0139 private:
0140   void changeSelection(int iRow) {
0141     if (iRow != m_selectedRow) {
0142       m_selectedRow = iRow;
0143       if (-1 == iRow) {
0144         indexSelected_(-1);
0145       } else {
0146         indexSelected_(iRow);
0147       }
0148       visualPropertiesChanged();
0149     }
0150   }
0151   FWJobMetadataManager *m_manager;
0152   std::vector<int> m_row_to_index;
0153   int m_selectedRow;
0154   std::string m_filter;
0155   mutable FWTextTableCellRenderer m_renderer;
0156 };
0157 
0158 namespace {
0159   void strip(std::string &source, const char *str) {
0160     std::string remove(str);
0161     while (true) {
0162       size_t found = source.find(remove);
0163       if (found == std::string::npos)
0164         break;
0165       source.erase(found, remove.size());
0166     }
0167   }
0168 
0169   /** Helper classes to handle sorting and filtering.
0170    
0171        The idea is that we sort things so that:
0172    
0173        - An item that matches is always less than one that does not.
0174        - If two items both match they are sorted according to the sorting 
0175          criteria.
0176        - If two items both do not match, the are always sorted (so that we
0177          do not waste time in sorting non matching items).
0178       
0179        Then we tell the table that the size of the available data is only
0180        the size of the matching items.
0181        
0182        Notice that the matching here does not work with regular expressions 
0183        but it matches in the case the filter string is found anywhere, 
0184        regardless of the case.
0185      */
0186   class SortAndFilter {
0187   public:
0188     SortAndFilter(const char *filter, int column, bool order, const std::vector<FWJobMetadataManager::Data> &data)
0189         : m_filter(filter), m_column(column), m_order(order), m_data(data) {
0190       simplify(m_filter);
0191       m_weights.resize(data.size());
0192 
0193       // Calculate whether or not all the entries match the given filter.
0194       // This is done only once, since it's invariant under permutations
0195       // of the data.
0196       for (size_t i = 0, e = m_weights.size(); i != e; ++i)
0197         m_weights[i] = matchesFilter(m_data[i]);
0198     }
0199 
0200     /** Makes @a str lowercase and eliminates bits we dont want to take
0201           into account in while searching.
0202         */
0203     static void simplify(std::string &str) {
0204       std::transform(str.begin(), str.end(), str.begin(), tolower);
0205       strip(str, "std::");
0206       strip(str, "edm::");
0207       strip(str, "vector<");
0208       strip(str, "clonepolicy");
0209       strip(str, "ownvector");
0210       strip(str, "rangemap<");
0211       strip(str, "strictweakordering<");
0212       strip(str, "sortedcollection<");
0213       strip(str, "reco::");
0214       strip(str, "edmnew::");
0215     }
0216 
0217     unsigned int matches(const std::string &str) const {
0218       std::string up(str);
0219       simplify(up);
0220       const char *begin = up.c_str();
0221 
0222       // If the filter is empty, we consider anything as matching
0223       // (i.e. it will not loop).
0224       // If the filter is not empty but the string to be matched is, we
0225       // consider it as if it was not matching.
0226       if ((!m_filter.empty()) && str.empty())
0227         return 0;
0228 
0229       // There are two level of matching. "Full string" and
0230       // "All characters". "Full string" matches return an higher weight
0231       // and therefore should appear on top.
0232       if (strstr(begin, m_filter.c_str()))
0233         return 2;
0234 
0235       return 0;
0236     }
0237 
0238     /** If any of the columns (including "Purpose"!!) matches, we consider
0239           the row valid.
0240           
0241           @return the best score obtained when matching strings.
0242         */
0243     unsigned int matchesFilter(const FWJobMetadataManager::Data &data) const {
0244       std::vector<unsigned int> scores;
0245       scores.reserve(10);
0246       scores.push_back(matches(data.purpose_));
0247       scores.push_back(matches(data.type_));
0248       scores.push_back(matches(data.moduleLabel_));
0249       scores.push_back(matches(data.productInstanceLabel_));
0250       scores.push_back(matches(data.processName_));
0251       std::sort(scores.begin(), scores.end());
0252       return scores.back();
0253     }
0254 
0255     /** Have a look at the class description to see the rationale behind 
0256           this.
0257         */
0258     bool operator()(const int &aIndex, const int &bIndex) {
0259       // In case no column is selected, we sort by relevance of the
0260       // filter.
0261       if (m_column == -1)
0262         return m_weights[aIndex] >= m_weights[bIndex];
0263 
0264       const FWJobMetadataManager::Data &a = m_data[aIndex];
0265       const FWJobMetadataManager::Data &b = m_data[bIndex];
0266 
0267       if (m_order)
0268         return dataForColumn(a, m_column) < dataForColumn(b, m_column);
0269       else
0270         return dataForColumn(a, m_column) > dataForColumn(b, m_column);
0271     }
0272 
0273   private:
0274     std::string m_filter;
0275     int m_column;
0276     bool m_order;
0277 
0278     const std::vector<FWJobMetadataManager::Data> &m_data;
0279     std::vector<unsigned int> m_weights;
0280   };
0281 
0282   void doSort(int column,
0283               const char *filter,
0284               bool descentSort,
0285               const std::vector<FWJobMetadataManager::Data> &iData,
0286               std::vector<int> &oRowToIndex) {
0287     std::vector<int> ordered;
0288     ordered.reserve(iData.size());
0289 
0290     for (size_t i = 0, e = iData.size(); i != e; ++i)
0291       ordered.push_back(i);
0292 
0293     SortAndFilter sorter(filter, column, descentSort, iData);
0294     // GE: Using std::sort does not work for some reason... Bah...
0295     std::stable_sort(ordered.begin(), ordered.end(), sorter);
0296 
0297     oRowToIndex.clear();
0298     oRowToIndex.reserve(ordered.size());
0299     // Only keep track of the rows that match.
0300     for (size_t i = 0, e = ordered.size(); i != e; ++i)
0301       if (sorter.matchesFilter(iData[ordered[i]]) != 0)
0302         oRowToIndex.push_back(ordered[i]);
0303   }
0304 }  // namespace
0305 
0306 void DataAdderTableManager::implSort(int column, bool sortOrder) {
0307   doSort(column, m_filter.c_str(), sortOrder, m_manager->usableData(), m_row_to_index);
0308 }
0309 
0310 //
0311 // static data member definitions
0312 //
0313 
0314 //
0315 // constructors and destructor
0316 //
0317 
0318 FWGUIEventDataAdder::FWGUIEventDataAdder(UInt_t iWidth,
0319                                          UInt_t iHeight,
0320                                          FWEventItemsManager *iManager,
0321                                          TGFrame *iParent,
0322                                          FWJobMetadataManager *iMetadataManager)
0323     : m_manager(iManager), m_metadataManager(iMetadataManager), m_parentFrame(iParent) {
0324   m_metadataManager->metadataChanged_.connect(std::bind(&FWGUIEventDataAdder::metadataUpdatedSlot, this));
0325   createWindow();
0326 }
0327 
0328 // FWGUIEventDataAdder::FWGUIEventDataAdder(const FWGUIEventDataAdder& rhs)
0329 // {
0330 //    // do actual copying here;
0331 // }
0332 
0333 FWGUIEventDataAdder::~FWGUIEventDataAdder() {
0334   /*
0335     // m_frame->Cleanup();
0336     // delete m_frame;
0337     m_frame=0;
0338     // delete m_tableWidget;
0339     m_tableWidget=0;
0340     delete m_tableManager;
0341     m_tableManager=0;
0342     */
0343 }
0344 
0345 //
0346 // assignment operators
0347 //
0348 // const FWGUIEventDataAdder& FWGUIEventDataAdder::operator=(const FWGUIEventDataAdder& rhs)
0349 // {
0350 //   //An exception safe implementation is
0351 //   FWGUIEventDataAdder temp(rhs);
0352 //   swap(rhs);
0353 //
0354 //   return *this;
0355 // }
0356 
0357 //
0358 // member functions
0359 //
0360 void FWGUIEventDataAdder::addNewItem() {
0361   TClass *theClass = TClass::GetClass(m_type.c_str());
0362   if (nullptr == theClass) {
0363     return;
0364   }
0365   const std::string moduleLabel = m_moduleLabel;
0366   if (moduleLabel.empty()) {
0367     return;
0368   }
0369 
0370   const std::string name = m_name->GetText();
0371   if (name.empty()) {
0372     return;
0373   }
0374 
0375   if (m_manager->find(name)) {
0376     TString msg("Event item '");
0377     msg += name;
0378     msg += "' is already registered. Please use another name.";
0379     fwLog(fwlog::kWarning) << msg.Data() << std::endl;
0380     new TGMsgBox(gClient->GetDefaultRoot(), m_frame, "Error - Name conflict", msg, kMBIconExclamation, kMBOk);
0381     return;
0382   }
0383 
0384   int largest = -1;
0385   if (m_manager->begin() != m_manager->end()) {
0386     if (*(m_manager->begin()))
0387       largest = (*(m_manager->begin()))->layer();
0388   }
0389   for (FWEventItemsManager::const_iterator it = m_manager->begin(), itEnd = m_manager->end(); it != itEnd; ++it) {
0390     if ((*it) && largest < (*it)->layer()) {
0391       largest = (*it)->layer();
0392     }
0393   }
0394   ++largest;
0395   std::string processName = m_processName;
0396   if (m_doNotUseProcessName->IsOn() && m_doNotUseProcessName->IsEnabled()) {
0397     processName = "";
0398   }
0399   FWPhysicsObjectDesc desc(name,
0400                            theClass,
0401                            m_purpose,
0402                            FWDisplayProperties::defaultProperties,
0403                            moduleLabel,
0404                            m_productInstanceLabel,
0405                            processName,
0406                            "",
0407                            largest);
0408   m_manager->add(desc);
0409 }
0410 
0411 void FWGUIEventDataAdder::addNewItemAndClose() {
0412   addNewItem();
0413   windowIsClosing();
0414 }
0415 
0416 void FWGUIEventDataAdder::show() {
0417   // Map main frame
0418   if (nullptr == m_frame) {
0419     createWindow();
0420   }
0421   m_frame->MapWindow();
0422 }
0423 
0424 void FWGUIEventDataAdder::windowIsClosing() {
0425   m_name->SetText("");
0426   m_search->SetText("");
0427   m_purpose.clear();
0428   m_type.clear();
0429   m_moduleLabel.clear();
0430   m_processName.clear();
0431   m_productInstanceLabel.clear();
0432   m_apply->SetEnabled(false);
0433   m_applyAndClose->SetEnabled(false);
0434 
0435   m_frame->UnmapWindow();
0436   m_frame->DontCallClose();
0437 }
0438 
0439 void FWGUIEventDataAdder::updateFilterString(const char *str) {
0440   m_tableManager->sortWithFilter(str);
0441   m_tableManager->dataChanged();
0442 }
0443 
0444 void FWGUIEventDataAdder::createWindow() {
0445   m_tableManager = new DataAdderTableManager(m_metadataManager);
0446   m_tableManager->indexSelected_.connect(
0447       std::bind(&FWGUIEventDataAdder::newIndexSelected, this, std::placeholders::_1));
0448 
0449   m_frame = new TGTransientFrame(gClient->GetDefaultRoot(), m_parentFrame, 600, 400);
0450   m_frame->Connect("CloseWindow()", "FWGUIEventDataAdder", this, "windowIsClosing()");
0451 
0452   FWDialogBuilder builder(m_frame);
0453   TGTextButton *cancelButton, *resetButton;
0454 
0455   builder.indent(10)
0456       .spaceDown(15)
0457       .addLabel("Search:", 0)
0458       .expand(false)
0459       .spaceUp(4)
0460       .floatLeft(4)
0461       .addTextEntry("", &m_search)
0462       .spaceUp(0)
0463       .frameSpaceDown(10)
0464       .addLabel("Viewable Collections", 8)
0465       .frameSpaceDown(5)
0466       .addTable(m_tableManager, &m_tableWidget)
0467       .expand(true, true)
0468       .addLabel("Name:", 0)
0469       .expand(false)
0470       .spaceUp(4)
0471       .floatLeft(4)
0472       .addTextEntry("", &m_name)
0473       .spaceUp(0)
0474       .floatLeft(4)
0475       .addTextButton(" Reset  ", &resetButton)
0476       .expand(false, false)
0477       .frameSpaceUpDown(5)
0478       .addCheckbox(
0479           "Do not use Process Name and "
0480           "instead only get this data "
0481           "from the most recent Process",
0482           &m_doNotUseProcessName)
0483       .frameSpaceDown(15)
0484       .hSpacer()
0485       .floatLeft(0)
0486       .addTextButton(" Close  ", &cancelButton)
0487       .expand(false)
0488       .floatLeft(4)
0489       .addTextButton(" Add Data  ", &m_apply)
0490       .expand(false)
0491       .floatLeft(4)
0492       .addTextButton(" Add Data && Close  ", &m_applyAndClose)
0493       .expand(false)
0494       .spaceRight(25)
0495       .spaceDown(15);
0496 
0497   m_search->Connect("TextChanged(const char *)", "FWGUIEventDataAdder", this, "updateFilterString(const char *)");
0498   m_search->SetEnabled(true);
0499   m_tableWidget->SetBackgroundColor(0xffffff);
0500   m_tableWidget->SetLineSeparatorColor(0x000000);
0501   m_tableWidget->SetHeaderBackgroundColor(0xececec);
0502   m_tableWidget->Connect("rowClicked(Int_t,Int_t,Int_t,Int_t,Int_t)",
0503                          "FWGUIEventDataAdder",
0504                          this,
0505                          "rowClicked(Int_t,Int_t,Int_t,Int_t,Int_t)");
0506 
0507   m_tableWidget->disableGrowInWidth();
0508   m_name->SetState(true);
0509   resetButton->SetEnabled(true);
0510   resetButton->Connect("Clicked()", "FWGUIEventDataAdder", this, "resetNameEntry()");
0511   m_doNotUseProcessName->SetState(kButtonDown);
0512   cancelButton->Connect("Clicked()", "FWGUIEventDataAdder", this, "windowIsClosing()");
0513   cancelButton->SetEnabled(true);
0514   m_apply->Connect("Clicked()", "FWGUIEventDataAdder", this, "addNewItem()");
0515   m_applyAndClose->Connect("Clicked()", "FWGUIEventDataAdder", this, "addNewItemAndClose()");
0516 
0517   m_frame->SetWindowName("Add Collection");
0518   m_frame->MapSubwindows();
0519   m_frame->Layout();
0520 }
0521 
0522 /** Slot for the metadataChanged signal of the FWJobMetadataManager.
0523     Whenever metadata changes, for whatever reason (e.g. a new file), we
0524     need to update the table.
0525   */
0526 void FWGUIEventDataAdder::metadataUpdatedSlot(void) {
0527   m_tableManager->reset();
0528   m_tableManager->sort(0, true);
0529 }
0530 
0531 void FWGUIEventDataAdder::resetNameEntry() {
0532   m_name->SetText(m_apply->IsEnabled() ? m_moduleLabel.c_str() : "", kFALSE);
0533 }
0534 
0535 void FWGUIEventDataAdder::newIndexSelected(int iSelectedIndex) {
0536   if (-1 != iSelectedIndex) {
0537     std::vector<FWJobMetadataManager::Data> &metadata = m_metadataManager->usableData();
0538     m_purpose = metadata[iSelectedIndex].purpose_;
0539     m_type = metadata[iSelectedIndex].type_;
0540     std::string oldModuleLabel = m_moduleLabel;
0541     m_moduleLabel = metadata[iSelectedIndex].moduleLabel_;
0542     m_productInstanceLabel = metadata[iSelectedIndex].productInstanceLabel_;
0543     m_processName = metadata[iSelectedIndex].processName_;
0544 
0545     if (strlen(m_name->GetText()) == 0 || oldModuleLabel == m_name->GetText()) {
0546       m_name->SetText(m_moduleLabel.c_str());
0547     }
0548     m_apply->SetEnabled(true);
0549     m_applyAndClose->SetEnabled(true);
0550 
0551     //Check to see if this is the last process, if it is then we can let the user decide
0552     // to not use the process name when doing the lookup.  This makes a saved configuration
0553     // more robust.  However, if they choose a collection not from the last process then we need the
0554     // process name in order to correctly get the data they want
0555     bool isMostRecentProcess = true;
0556     int index = 0;
0557     for (std::vector<FWJobMetadataManager::Data>::iterator it = metadata.begin(), itEnd = metadata.end();
0558          it != itEnd && isMostRecentProcess;
0559          ++it, ++index) {
0560       if (index == iSelectedIndex) {
0561         continue;
0562       }
0563       if (it->moduleLabel_ == m_moduleLabel && it->purpose_ == m_purpose && it->type_ == m_type &&
0564           it->productInstanceLabel_ == m_productInstanceLabel) {
0565         //see if this process is newer than the data requested
0566 
0567         for (size_t pni = 0, pne = m_metadataManager->processNamesInJob().size(); pni != pne; ++pni) {
0568           const std::string &processName = m_metadataManager->processNamesInJob()[pni];
0569           if (m_processName == processName)
0570             break;
0571 
0572           if (it->processName_ == processName) {
0573             isMostRecentProcess = false;
0574             break;
0575           }
0576         }
0577       }
0578     }
0579     if (isMostRecentProcess) {
0580       if (!m_doNotUseProcessName->IsEnabled()) {
0581         m_doNotUseProcessName->SetEnabled(true);
0582       }
0583     } else {
0584       //NOTE: must remember state before we get here because 'enable' and 'on' are mutually
0585       // exlcusive :(
0586       m_doNotUseProcessName->SetEnabled(false);
0587     }
0588   }
0589 }
0590 
0591 void FWGUIEventDataAdder::rowClicked(Int_t iRow, Int_t iButton, Int_t iKeyMod, Int_t, Int_t) {
0592   if (iButton == kButton1) {
0593     m_tableManager->setSelection(iRow, iKeyMod);
0594   }
0595 }
0596 
0597 //
0598 // const member functions
0599 //
0600 
0601 //
0602 // static member functions
0603 //