Wt examples  3.2.3
/home/koen/project/wt/public-git/wt/examples/wt-homepage/ExampleSourceViewer.C
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2009 Emweb bvba
00003  *
00004  * See the LICENSE file for terms of use.
00005  */
00006 
00007 #include <iostream>
00008 #include <stdlib.h>
00009 #include <algorithm>
00010 
00011 #include <Wt/WApplication>
00012 #include <Wt/WContainerWidget>
00013 #include <Wt/WEnvironment>
00014 #include <Wt/WLineEdit>
00015 #include <Wt/WGridLayout>
00016 #include <Wt/WHBoxLayout>
00017 #include <Wt/WPushButton>
00018 #include <Wt/WTable>
00019 #include <Wt/WText>
00020 #include <Wt/WTreeView>
00021 #include <Wt/WVBoxLayout>
00022 #include <Wt/WViewWidget>
00023 
00024 #include <boost/filesystem/operations.hpp>
00025 #include <boost/filesystem/exception.hpp>
00026 #include <boost/filesystem/convenience.hpp>
00027 #include <boost/algorithm/string.hpp>
00028 
00029 #include "ExampleSourceViewer.h"
00030 #include "FileItem.h"
00031 
00032 using namespace Wt; 
00033 namespace fs = boost::filesystem;
00034 
00035 // Same as p.filename() in latest boost::filesystem
00036 static std::string filename(const fs::path& p)
00037 {
00038 #if BOOST_FILESYSTEM_VERSION < 3
00039   return p.empty() ? std::string() : *--p.end();
00040 #else
00041   return p.empty() ? std::string() : (*--p.end()).string();
00042 #endif
00043 }
00044 
00045 // Same as p.stem() in latest boost::filesystem
00046 static std::string stem(const fs::path& p)
00047 {
00048   std::string fn = filename(p);
00049   std::size_t pos = fn.find('.');
00050   if (pos == std::string::npos)
00051     return fn;
00052   else
00053     return fn.substr(0, pos);
00054 }
00055 
00056 // Should be same as p.parent_path() in latest boost::filesystem
00057 // This is not entirely according to fs::path::parent_path() in 1.39.0
00058 fs::path parent_path(const fs::path& p)
00059 {
00060   std::string fn = filename(p);
00061   std::string path = p.string();
00062 
00063   return path.substr(0, path.length() - fn.length() - 1);
00064 }
00065 
00066 static bool comparePaths(const fs::path& p1, const fs::path& p2)
00067 {
00068   return filename(p1) > filename(p2);
00069 }
00070 
00071 ExampleSourceViewer::ExampleSourceViewer(const std::string& deployPath,
00072                                          const std::string& examplesRoot,
00073                                          const std::string& examplesType)
00074   : deployPath_(deployPath),
00075     examplesRoot_(examplesRoot),
00076     examplesType_(examplesType)
00077 {
00078   wApp->internalPathChanged().connect
00079     (this, &ExampleSourceViewer::handlePathChange);
00080 
00081   handlePathChange();
00082 }
00083 
00084 void ExampleSourceViewer::handlePathChange()
00085 {
00086   WApplication *app = wApp;
00087 
00088   if (app->internalPathMatches(deployPath_)) {
00089     std::string example = app->internalPathNextPart(deployPath_);
00090 
00091     if (example.find("..") != std::string::npos
00092         || example.find('/') != std::string::npos
00093         || example.find('\\') != std::string::npos) {
00094       app->setInternalPathValid(false);
00095       setExample("INVALID_DIR", "INVALID");
00096     } else
00097       setExample(examplesRoot_ + example, example);
00098   }
00099 }
00100 
00101 void ExampleSourceViewer::setExample(const std::string& exampleDir,
00102                                      const std::string& example)
00103 {
00104   clear();
00105 
00106   bool exists = false;
00107   try {
00108     exists = fs::exists(exampleDir);
00109   } catch (std::exception&) {
00110   }
00111 
00112   if (!exists) {
00113     WApplication::instance()->setInternalPathValid(false);
00114     addWidget(new WText("No such example: " + exampleDir));
00115     return;
00116   }
00117 
00118   model_ = new WStandardItemModel(0, 1, this);
00119   if (examplesType_ == "CPP") {
00120     cppTraverseDir(model_->invisibleRootItem(), exampleDir);
00121   } else if (examplesType_ == "JAVA") {
00122     javaTraverseDir(model_->invisibleRootItem(), exampleDir);
00123   }
00124 
00125   WApplication::instance()->setTitle(tr("srcview.title." + example));
00126   WText *title = 
00127     new WText(tr("srcview.title." + examplesType_ + "." + example));
00128   title->setInternalPathEncoding(true);
00129 
00130   exampleView_ = new WTreeView();
00131   exampleView_->setHeaderHeight(0);
00132   exampleView_->resize(300, WLength::Auto);
00133   exampleView_->setSortingEnabled(false);
00134   exampleView_->setModel(model_);
00135   exampleView_->expandToDepth(1);
00136   exampleView_->setSelectionMode(SingleSelection);
00137   exampleView_->setAlternatingRowColors(false);
00138   exampleView_->selectionChanged().connect
00139     (this, &ExampleSourceViewer::showFile);
00140 
00141   sourceView_ = new SourceView(FileItem::FileNameRole, 
00142                                FileItem::ContentsRole,
00143                                FileItem::FilePathRole);
00144   sourceView_->setStyleClass("source-view");
00145 
00146   /*
00147    * Expand path to first file, to show something in the source viewer
00148    */
00149   WStandardItem *w = model_->item(0);
00150   do {
00151     exampleView_->setExpanded(w->index(), true);
00152     if (w->rowCount() > 0)
00153       w = w->child(0);
00154     else {
00155       exampleView_->select(w->index(), Select);
00156       w = 0;
00157     }
00158   } while (w);
00159 
00160   WVBoxLayout *topLayout = new WVBoxLayout();
00161   topLayout->addWidget(title);
00162 
00163   WHBoxLayout *gitLayout = new WHBoxLayout();
00164   gitLayout->addWidget(exampleView_, 0);
00165   gitLayout->addWidget(sourceView_, 1);
00166   topLayout->addLayout(gitLayout, 1);
00167   gitLayout->setResizable(0);
00168 
00169   /*
00170    * FIXME, in plain HTML mode, we should set a minimum size to the source
00171    * view, and remove this in enableAjax() ?
00172    */
00173   // sourceView_->setHeight("100%");
00174 
00175   setLayout(topLayout);
00176   setStyleClass("maindiv");
00177 }
00178 
00179 /*
00180  * Return the companion implementation/header file for a C++ source file.
00181  */
00182 static fs::path getCompanion(const fs::path& path) 
00183 {
00184   std::string ext = fs::extension(path);
00185 
00186   if (ext == ".h")
00187     return parent_path(path) / (stem(path) + ".C");
00188   else if (ext == ".C" || ext == ".cpp")
00189     return parent_path(path) / (stem(path) + ".h");
00190   else
00191     return fs::path();
00192 }
00193 
00194 void ExampleSourceViewer::cppTraverseDir(WStandardItem* parent, 
00195                                          const fs::path& path)
00196 {
00197   static const char *supportedFiles[] = {
00198     ".C", ".cpp", ".h", ".css", ".xml", ".png", ".gif", ".csv", ".ico", 0
00199   };
00200 
00201   FileItem* dir = new FileItem("/icons/yellow-folder-open.png", filename(path),
00202                                "");
00203   parent->appendRow(dir);
00204   parent = dir;
00205   try {
00206     std::set<fs::path> paths;
00207 
00208     fs::directory_iterator end_itr;
00209     for (fs::directory_iterator i(path); i != end_itr; ++i) 
00210       paths.insert(*i);
00211 
00212     std::vector<FileItem*> classes, files;
00213     std::vector<fs::path> dirs;
00214 
00215     while (!paths.empty()) {
00216       fs::path p = *paths.begin();
00217       paths.erase(p);
00218 
00219       // skip symbolic links and other files
00220       if (fs::is_symlink(p))
00221         continue;
00222 
00223       // skip files with an extension we do not want to handle
00224       if (fs::is_regular(p)) {
00225         std::string ext = fs::extension(p);
00226         bool supported = false;
00227         for (const char **s = supportedFiles; *s != 0; ++s)
00228           if (*s == ext) {
00229             supported = true;
00230             break;
00231           }
00232         
00233         if (!supported)
00234           continue;
00235       }
00236 
00237       // see if we have one file of a class (.C, .h)
00238       fs::path companion = getCompanion(p);
00239       if (!companion.empty()) {
00240         std::set<fs::path>::iterator it_companion = paths.find(companion);
00241  
00242           if (it_companion != paths.end()) {
00243             std::string className = stem(p);
00244             escapeText(className);
00245             std::string label = "<i>class</i> " + className;
00246 
00247             FileItem *classItem = 
00248               new FileItem("/icons/cppclass.png", label, std::string());
00249             classItem->setFlags(classItem->flags() | ItemIsXHTMLText);
00250 
00251             FileItem *header = new FileItem("/icons/document.png", filename(p),
00252                                             p.string());
00253             FileItem *cpp = new FileItem("/icons/document.png",
00254                                          filename(*it_companion),
00255                                          (*it_companion).string());
00256             classItem->appendRow(header);
00257             classItem->appendRow(cpp);
00258           
00259             classes.push_back(classItem);
00260             paths.erase(it_companion);
00261           } else {
00262             FileItem *file = new FileItem("/icons/document.png", filename(p),
00263                                           p.string());
00264             files.push_back(file);
00265           }
00266       } else if (fs::is_directory(p)) {
00267         dirs.push_back(p);
00268       } else {
00269         FileItem *file = new FileItem("/icons/document.png", filename(p),
00270                                       p.string());
00271         files.push_back(file);
00272       }
00273     }
00274 
00275     std::sort(dirs.begin(), dirs.end(), comparePaths);
00276 
00277     for (unsigned int i = 0; i < classes.size(); i++)
00278       parent->appendRow(classes[i]);
00279 
00280     for (unsigned int i = 0; i < files.size(); i++)
00281       parent->appendRow(files[i]);
00282 
00283     for (unsigned int i = 0; i < dirs.size(); i++)
00284       cppTraverseDir(parent, dirs[i]);
00285   } catch (fs::filesystem_error& e) {
00286     std::cerr << e.what() << std::endl;
00287   }
00288 }
00289 
00290 void ExampleSourceViewer::javaTraversePackages(WStandardItem *parent,
00291                                                const fs::path& srcPath,
00292                                                const std::string packageName)
00293 {
00294   fs::directory_iterator end_itr;
00295 
00296   FileItem *packageItem = 0;
00297   for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
00298     fs::path p = *i;
00299     if (fs::is_regular(p)) {
00300       if (!packageItem) {
00301         packageItem = new FileItem("/icons/package.png", packageName, "");
00302         parent->appendRow(packageItem);
00303       }
00304 
00305       FileItem *file = new FileItem("/icons/javaclass.png", filename(p),
00306                                     p.string());
00307       packageItem->appendRow(file);
00308     }
00309   }
00310 
00311   for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
00312     fs::path p = *i;
00313     if (fs::is_directory(p)) {  
00314       std::string pn = packageName;
00315       if (!pn.empty())
00316         pn += ".";
00317       pn += filename(p);
00318 
00319       javaTraversePackages(parent, p, pn);
00320     }
00321   }
00322 }
00323 
00324 void ExampleSourceViewer::javaTraverseDir(WStandardItem* parent, 
00325                                           const fs::path& path)
00326 {
00327   FileItem* dir = new FileItem("/icons/yellow-folder-open.png", filename(path),
00328                                "");
00329   parent->appendRow(dir);
00330   parent = dir;
00331 
00332   std::vector<fs::path> files, dirs;
00333 
00334   fs::directory_iterator end_itr;
00335   for (fs::directory_iterator i(path); i != end_itr; ++i) {
00336     fs::path p = *i;
00337     if (fs::is_directory(p)) {
00338       if (filename(p) == "src") {
00339         FileItem* dir = new FileItem("/icons/package-folder-open.png",
00340                                      filename(p), "");
00341         parent->appendRow(dir);
00342         javaTraversePackages(dir, p, "");
00343       } else
00344         dirs.push_back(p);
00345     } else {
00346       files.push_back(p);
00347     }
00348   }
00349 
00350   std::sort(dirs.begin(), dirs.end(), comparePaths);
00351   std::sort(files.begin(), files.end(), comparePaths);
00352 
00353   for (unsigned int i = 0; i < dirs.size(); i++)
00354     javaTraverseDir(parent, dirs[i]);
00355 
00356   for (unsigned int i = 0; i < files.size(); i++) {
00357     FileItem *file = new FileItem("/icons/document.png", filename(files[i]),
00358                                   files[i].string());
00359     parent->appendRow(file);
00360   }
00361 }
00362 
00365 void ExampleSourceViewer::showFile() {
00366   if (exampleView_->selectedIndexes().empty())
00367     return;
00368 
00369   WModelIndex selected = *exampleView_->selectedIndexes().begin();
00370 
00371   // expand a folder when clicked
00372   if (exampleView_->model()->rowCount(selected) > 0
00373       && !exampleView_->isExpanded(selected))
00374     exampleView_->setExpanded(selected, true);
00375 
00376   // (for a file,) load data in source viewer
00377   sourceView_->setIndex(selected);
00378 }

Generated on Thu Nov 1 2012 for the C++ Web Toolkit (Wt) by doxygen 1.7.5.1