Wt examples  3.2.2
/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       setExample("INVALID_DIR", "INVALID");
00095     else
00096       setExample(examplesRoot_ + example, example);
00097   }
00098 }
00099 
00100 void ExampleSourceViewer::setExample(const std::string& exampleDir,
00101                                      const std::string& example)
00102 {
00103   clear();
00104 
00105   bool exists = false;
00106   try {
00107     exists = fs::exists(exampleDir);
00108   } catch (std::exception&) {
00109   }
00110 
00111   if (!exists) {
00112     addWidget(new WText("No such example: " + exampleDir));
00113     return;
00114   }
00115 
00116   model_ = new WStandardItemModel(0, 1, this);
00117   if (examplesType_ == "CPP") {
00118     cppTraverseDir(model_->invisibleRootItem(), exampleDir);
00119   } else if (examplesType_ == "JAVA") {
00120     javaTraverseDir(model_->invisibleRootItem(), exampleDir);
00121   }
00122 
00123   WApplication::instance()->setTitle(tr("srcview.title." + example));
00124   WText *title = 
00125     new WText(tr("srcview.title." + examplesType_ + "." + example));
00126   title->setInternalPathEncoding(true);
00127 
00128   exampleView_ = new WTreeView();
00129   exampleView_->setHeaderHeight(0);
00130   exampleView_->resize(300, WLength::Auto);
00131   exampleView_->setSortingEnabled(false);
00132   exampleView_->setModel(model_);
00133   exampleView_->expandToDepth(1);
00134   exampleView_->setSelectionMode(SingleSelection);
00135   exampleView_->setAlternatingRowColors(false);
00136   exampleView_->selectionChanged().connect
00137     (this, &ExampleSourceViewer::showFile);
00138 
00139   sourceView_ = new SourceView(FileItem::FileNameRole, 
00140                                FileItem::ContentsRole,
00141                                FileItem::FilePathRole);
00142   sourceView_->setStyleClass("source-view");
00143 
00144   /*
00145    * Expand path to first file, to show something in the source viewer
00146    */
00147   WStandardItem *w = model_->item(0);
00148   do {
00149     exampleView_->setExpanded(w->index(), true);
00150     if (w->rowCount() > 0)
00151       w = w->child(0);
00152     else {
00153       exampleView_->select(w->index(), Select);
00154       w = 0;
00155     }
00156   } while (w);
00157 
00158   WVBoxLayout *topLayout = new WVBoxLayout();
00159   topLayout->addWidget(title);
00160 
00161   WHBoxLayout *gitLayout = new WHBoxLayout();
00162   gitLayout->addWidget(exampleView_, 0);
00163   gitLayout->addWidget(sourceView_, 1);
00164   topLayout->addLayout(gitLayout, 1);
00165   gitLayout->setResizable(0);
00166 
00167   /*
00168    * FIXME, in plain HTML mode, we should set a minimum size to the source
00169    * view, and remove this in enableAjax() ?
00170    */
00171   // sourceView_->setHeight("100%");
00172 
00173   setLayout(topLayout);
00174   setStyleClass("maindiv");
00175 }
00176 
00177 /*
00178  * Return the companion implementation/header file for a C++ source file.
00179  */
00180 static fs::path getCompanion(const fs::path& path) 
00181 {
00182   std::string ext = fs::extension(path);
00183 
00184   if (ext == ".h")
00185     return parent_path(path) / (stem(path) + ".C");
00186   else if (ext == ".C" || ext == ".cpp")
00187     return parent_path(path) / (stem(path) + ".h");
00188   else
00189     return fs::path();
00190 }
00191 
00192 void ExampleSourceViewer::cppTraverseDir(WStandardItem* parent, 
00193                                          const fs::path& path)
00194 {
00195   static const char *supportedFiles[] = {
00196     ".C", ".cpp", ".h", ".css", ".xml", ".png", ".gif", ".csv", ".ico", 0
00197   };
00198 
00199   FileItem* dir = new FileItem("/icons/yellow-folder-open.png", filename(path),
00200                                "");
00201   parent->appendRow(dir);
00202   parent = dir;
00203   try {
00204     std::set<fs::path> paths;
00205 
00206     fs::directory_iterator end_itr;
00207     for (fs::directory_iterator i(path); i != end_itr; ++i) 
00208       paths.insert(*i);
00209 
00210     std::vector<FileItem*> classes, files;
00211     std::vector<fs::path> dirs;
00212 
00213     while (!paths.empty()) {
00214       fs::path p = *paths.begin();
00215       paths.erase(p);
00216 
00217       // skip symbolic links and other files
00218       if (fs::is_symlink(p))
00219         continue;
00220 
00221       // skip files with an extension we do not want to handle
00222       if (fs::is_regular(p)) {
00223         std::string ext = fs::extension(p);
00224         bool supported = false;
00225         for (const char **s = supportedFiles; *s != 0; ++s)
00226           if (*s == ext) {
00227             supported = true;
00228             break;
00229           }
00230         
00231         if (!supported)
00232           continue;
00233       }
00234 
00235       // see if we have one file of a class (.C, .h)
00236       fs::path companion = getCompanion(p);
00237       if (!companion.empty()) {
00238         std::set<fs::path>::iterator it_companion = paths.find(companion);
00239  
00240           if (it_companion != paths.end()) {
00241             std::string className = stem(p);
00242             escapeText(className);
00243             std::string label = "<i>class</i> " + className;
00244 
00245             FileItem *classItem = 
00246               new FileItem("/icons/cppclass.png", label, std::string());
00247             classItem->setFlags(classItem->flags() | ItemIsXHTMLText);
00248 
00249             FileItem *header = new FileItem("/icons/document.png", filename(p),
00250                                             p.string());
00251             FileItem *cpp = new FileItem("/icons/document.png",
00252                                          filename(*it_companion),
00253                                          (*it_companion).string());
00254             classItem->appendRow(header);
00255             classItem->appendRow(cpp);
00256           
00257             classes.push_back(classItem);
00258             paths.erase(it_companion);
00259           } else {
00260             FileItem *file = new FileItem("/icons/document.png", filename(p),
00261                                           p.string());
00262             files.push_back(file);
00263           }
00264       } else if (fs::is_directory(p)) {
00265         dirs.push_back(p);
00266       } else {
00267         FileItem *file = new FileItem("/icons/document.png", filename(p),
00268                                       p.string());
00269         files.push_back(file);
00270       }
00271     }
00272 
00273     std::sort(dirs.begin(), dirs.end(), comparePaths);
00274 
00275     for (unsigned int i = 0; i < classes.size(); i++)
00276       parent->appendRow(classes[i]);
00277 
00278     for (unsigned int i = 0; i < files.size(); i++)
00279       parent->appendRow(files[i]);
00280 
00281     for (unsigned int i = 0; i < dirs.size(); i++)
00282       cppTraverseDir(parent, dirs[i]);
00283   } catch (fs::filesystem_error& e) {
00284     std::cerr << e.what() << std::endl;
00285   }
00286 }
00287 
00288 void ExampleSourceViewer::javaTraversePackages(WStandardItem *parent,
00289                                                const fs::path& srcPath,
00290                                                const std::string packageName)
00291 {
00292   fs::directory_iterator end_itr;
00293 
00294   FileItem *packageItem = 0;
00295   for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
00296     fs::path p = *i;
00297     if (fs::is_regular(p)) {
00298       if (!packageItem) {
00299         packageItem = new FileItem("/icons/package.png", packageName, "");
00300         parent->appendRow(packageItem);
00301       }
00302 
00303       FileItem *file = new FileItem("/icons/javaclass.png", filename(p),
00304                                     p.string());
00305       packageItem->appendRow(file);
00306     }
00307   }
00308 
00309   for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
00310     fs::path p = *i;
00311     if (fs::is_directory(p)) {  
00312       std::string pn = packageName;
00313       if (!pn.empty())
00314         pn += ".";
00315       pn += filename(p);
00316 
00317       javaTraversePackages(parent, p, pn);
00318     }
00319   }
00320 }
00321 
00322 void ExampleSourceViewer::javaTraverseDir(WStandardItem* parent, 
00323                                           const fs::path& path)
00324 {
00325   FileItem* dir = new FileItem("/icons/yellow-folder-open.png", filename(path),
00326                                "");
00327   parent->appendRow(dir);
00328   parent = dir;
00329 
00330   std::vector<fs::path> files, dirs;
00331 
00332   fs::directory_iterator end_itr;
00333   for (fs::directory_iterator i(path); i != end_itr; ++i) {
00334     fs::path p = *i;
00335     if (fs::is_directory(p)) {
00336       if (filename(p) == "src") {
00337         FileItem* dir = new FileItem("/icons/package-folder-open.png",
00338                                      filename(p), "");
00339         parent->appendRow(dir);
00340         javaTraversePackages(dir, p, "");
00341       } else
00342         dirs.push_back(p);
00343     } else {
00344       files.push_back(p);
00345     }
00346   }
00347 
00348   std::sort(dirs.begin(), dirs.end(), comparePaths);
00349   std::sort(files.begin(), files.end(), comparePaths);
00350 
00351   for (unsigned int i = 0; i < dirs.size(); i++)
00352     javaTraverseDir(parent, dirs[i]);
00353 
00354   for (unsigned int i = 0; i < files.size(); i++) {
00355     FileItem *file = new FileItem("/icons/document.png", filename(files[i]),
00356                                   files[i].string());
00357     parent->appendRow(file);
00358   }
00359 }
00360 
00363 void ExampleSourceViewer::showFile() {
00364   if (exampleView_->selectedIndexes().empty())
00365     return;
00366 
00367   WModelIndex selected = *exampleView_->selectedIndexes().begin();
00368 
00369   // expand a folder when clicked
00370   if (exampleView_->model()->rowCount(selected) > 0
00371       && !exampleView_->isExpanded(selected))
00372     exampleView_->setExpanded(selected, true);
00373 
00374   // (for a file,) load data in source viewer
00375   sourceView_->setIndex(selected);
00376 }

Generated on Fri Jul 27 2012 for the C++ Web Toolkit (Wt) by doxygen 1.7.5.1