Wt examples
3.2.1
|
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, 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 * 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 }