Wt examples
3.3.0
|
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 }