00001
00002
00003
00004
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
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
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
00057
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
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, 0, AlignTop | AlignJustify);
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
00169
00170
00171
00172
00173 setLayout(topLayout);
00174 setStyleClass("maindiv");
00175 }
00176
00177
00178
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
00218 if (fs::is_symlink(p))
00219 continue;
00220
00221
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
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
00370 if (exampleView_->model()->rowCount(selected) > 0
00371 && !exampleView_->isExpanded(selected))
00372 exampleView_->setExpanded(selected, true);
00373
00374
00375 sourceView_->setIndex(selected);
00376 }