Wt examples  4.9.0
Loading...
Searching...
No Matches
ExampleSourceViewer.C
Go to the documentation of this file.
1/*
2 * Copyright (C) 2009 Emweb bv, Herent, Belgium
3 *
4 * See the LICENSE file for terms of use.
5 */
6
7#include <iostream>
8#include <stdlib.h>
9#include <algorithm>
10
11#include <Wt/WApplication.h>
12#include <Wt/WContainerWidget.h>
13#include <Wt/WEnvironment.h>
14#include <Wt/WLineEdit.h>
15#include <Wt/WGridLayout.h>
16#include <Wt/WHBoxLayout.h>
17#include <Wt/WPushButton.h>
18#include <Wt/WTable.h>
19#include <Wt/WText.h>
20#include <Wt/WTreeView.h>
21#include <Wt/WVBoxLayout.h>
22#include <Wt/WViewWidget.h>
23
24#include <boost/filesystem/operations.hpp>
25#include <boost/filesystem/exception.hpp>
26#include <boost/filesystem/convenience.hpp>
27#include <boost/algorithm/string.hpp>
28
29#include "ExampleSourceViewer.h"
30#include "FileItem.h"
31
32namespace fs = boost::filesystem;
33
34// Same as p.filename() in latest boost::filesystem
35static std::string filename(const fs::path& p)
36{
37#if BOOST_FILESYSTEM_VERSION < 3
38 return p.empty() ? std::string() : *--p.end();
39#else
40 return p.empty() ? std::string() : (*--p.end()).string();
41#endif
42}
43
44// Same as p.stem() in latest boost::filesystem
45static std::string stem(const fs::path& p)
46{
47 std::string fn = filename(p);
48 std::size_t pos = fn.find('.');
49 if (pos == std::string::npos)
50 return fn;
51 else
52 return fn.substr(0, pos);
53}
54
55// Should be same as p.parent_path() in latest boost::filesystem
56// This is not entirely according to fs::path::parent_path() in 1.39.0
57fs::path parent_path(const fs::path& p)
58{
59 std::string fn = filename(p);
60 std::string path = p.string();
61
62 return path.substr(0, path.length() - fn.length() - 1);
63}
64
65static bool comparePaths(const fs::path& p1, const fs::path& p2)
66{
67 return filename(p1) > filename(p2);
68}
69
70ExampleSourceViewer::ExampleSourceViewer(const std::string& deployPath,
71 const std::string& examplesRoot,
72 const std::string& examplesType)
73 : deployPath_(deployPath),
74 examplesRoot_(examplesRoot),
75 examplesType_(examplesType)
76{
77 wApp->internalPathChanged().connect
79
81}
82
84{
85 WApplication *app = wApp;
86
87 if (app->internalPathMatches(deployPath_)) {
88 std::string example = app->internalPathNextPart(deployPath_);
89
90 if (example.find("..") != std::string::npos
91 || example.find('/') != std::string::npos
92 || example.find('\\') != std::string::npos) {
93 app->setInternalPathValid(false);
94 setExample("INVALID_DIR", "INVALID");
95 } else
96 setExample(examplesRoot_ + example, example);
97 }
98}
99
100void ExampleSourceViewer::setExample(const std::string& exampleDir,
101 const std::string& example)
102{
103 clear();
104
105 bool exists = false;
106 try {
107 exists = fs::exists(exampleDir);
108 } catch (std::exception&) {
109 }
110
111 if (!exists) {
112 WApplication::instance()->setInternalPathValid(false);
113 addWidget(std::make_unique<WText>("No such example: " + exampleDir));
114 return;
115 }
116
117 model_ = std::make_shared<WStandardItemModel>(0, 1);
118 if (examplesType_ == "CPP") {
119 cppTraverseDir(model_->invisibleRootItem(), exampleDir);
120 } else if (examplesType_ == "JAVA") {
121 javaTraverseDir(model_->invisibleRootItem(), exampleDir);
122 }
123
124 WApplication::instance()->setTitle(tr("srcview.title." + example));
125 std::unique_ptr<WText> title(std::make_unique<WText>(
126 tr("srcview.title." + examplesType_ + "." + example)));
127 title->setInternalPathEncoding(true);
128
129 auto exampleView = std::make_unique<WTreeView>();
130 exampleView_ = exampleView.get();
131 exampleView_->setHeaderHeight(0);
132 exampleView_->resize(300, WLength::Auto);
133 exampleView_->setSortingEnabled(false);
134 exampleView_->setModel(model_);
135 exampleView_->expandToDepth(1);
136 exampleView_->setSelectionMode(SelectionMode::Single);
137 exampleView_->setAlternatingRowColors(false);
138 exampleView_->selectionChanged().connect
140
141 auto sourceView =
142 std::make_unique<SourceView>(FileItem::FileNameRole,
145 sourceView_ = sourceView.get();
146 sourceView_->setStyleClass("source-view");
147
148 /*
149 * Expand path to first file, to show something in the source viewer
150 */
151 WStandardItem *w = model_->item(0);
152 do {
153 exampleView_->setExpanded(w->index(), true);
154 if (w->rowCount() > 0)
155 w = w->child(0);
156 else {
157 exampleView_->select(w->index());
158 w = 0;
159 }
160 } while (w);
161
162 auto topLayout = std::make_unique<WVBoxLayout>();
163 topLayout->addWidget(std::move(title));
164
165 auto gitLayout = std::make_unique<WHBoxLayout>();
166 WHBoxLayout *g = gitLayout.get();
167 gitLayout->addWidget(std::move(exampleView), 0);
168 gitLayout->addWidget(std::move(sourceView), 1);
169 topLayout->addLayout(std::move(gitLayout), 1);
170 g->setResizable(0);
171
172 /*
173 * FIXME, in plain HTML mode, we should set a minimum size to the source
174 * view, and remove this in enableAjax() ?
175 */
176 // sourceView_->setHeight("100%");
177
178 setLayout(std::move(topLayout));
179 setStyleClass("maindiv");
180}
181
182/*
183 * Return the companion implementation/header file for a C++ source file.
184 */
185static fs::path getCompanion(const fs::path& path)
186{
187 std::string ext = fs::extension(path);
188
189 if (ext == ".h")
190 return parent_path(path) / (stem(path) + ".C");
191 else if (ext == ".C" || ext == ".cpp")
192 return parent_path(path) / (stem(path) + ".h");
193 else
194 return fs::path();
195}
196
197void ExampleSourceViewer::cppTraverseDir(WStandardItem* parent,
198 const fs::path& path)
199{
200 static const char *supportedFiles[] = {
201 ".C", ".cpp", ".h", ".css", ".xml", ".png", ".gif", ".csv", ".ico", 0
202 };
203
204 auto dir = std::make_unique<FileItem>("/icons/yellow-folder-open.png",
205 filename(path),
206 "");
207 FileItem *dirPtr = dir.get();
208 parent->appendRow(std::move(dir));
209 parent = dirPtr;
210 try {
211 std::set<fs::path> paths;
212
213 fs::directory_iterator end_itr;
214 for (fs::directory_iterator i(path); i != end_itr; ++i)
215 paths.insert(*i);
216
217 std::vector<std::unique_ptr<FileItem>> classes, files;
218 std::vector<fs::path> dirs;
219
220 while (!paths.empty()) {
221 fs::path p = *paths.begin();
222 paths.erase(p);
223
224 // skip symbolic links and other files
225 if (fs::is_symlink(p))
226 continue;
227
228 // skip files with an extension we do not want to handle
229 if (fs::is_regular(p)) {
230 std::string ext = fs::extension(p);
231 bool supported = false;
232 for (const char **s = supportedFiles; *s != 0; ++s)
233 if (*s == ext) {
234 supported = true;
235 break;
236 }
237
238 if (!supported)
239 continue;
240 }
241
242 // see if we have one file of a class (.C, .h)
243 fs::path companion = getCompanion(p);
244 if (!companion.empty()) {
245 std::set<fs::path>::iterator it_companion = paths.find(companion);
246
247 if (it_companion != paths.end()) {
248 std::string className = stem(p);
249 escapeText(className);
250 std::string label = "<i>class</i> " + className;
251
252 std::unique_ptr<FileItem> classItem =
253 std::make_unique<FileItem>("/icons/cppclass.png", label, std::string());
254 classItem->setFlags(classItem->flags() | ItemFlag::XHTMLText);
255
256 auto header
257 = std::make_unique<FileItem>("/icons/document.png", filename(p),
258 p.string());
259 auto cpp
260 = std::make_unique<FileItem>("/icons/document.png",
261 filename(*it_companion),
262 (*it_companion).string());
263 classItem->appendRow(std::move(header));
264 classItem->appendRow(std::move(cpp));
265
266 classes.push_back(std::move(classItem));
267 paths.erase(it_companion);
268 } else {
269 auto file
270 = std::make_unique<FileItem>("/icons/document.png", filename(p),
271 p.string());
272 files.push_back(std::move(file));
273 }
274 } else if (fs::is_directory(p)) {
275 dirs.push_back(p);
276 } else {
277 auto file
278 = std::make_unique<FileItem>("/icons/document.png", filename(p),
279 p.string());
280 files.push_back(std::move(file));
281 }
282 }
283
284 std::sort(dirs.begin(), dirs.end(), comparePaths);
285
286 for (unsigned int i = 0; i < classes.size(); i++)
287 parent->appendRow(std::move(classes[i]));
288
289 for (unsigned int i = 0; i < files.size(); i++)
290 parent->appendRow(std::move(files[i]));
291
292 for (unsigned int i = 0; i < dirs.size(); i++)
293 cppTraverseDir(parent, dirs[i]);
294 } catch (fs::filesystem_error& e) {
295 std::cerr << e.what() << std::endl;
296 }
297}
298
300 const fs::path& srcPath,
301 const std::string packageName)
302{
303 fs::directory_iterator end_itr;
304
305 FileItem *packageItem = nullptr;
306 for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
307 fs::path p = *i;
308 if (fs::is_regular(p)) {
309 if (!packageItem) {
310 auto item = std::make_unique<FileItem>("/icons/package.png", packageName, "");
311 packageItem = item.get();
312 parent->appendRow(std::move(item));
313 }
314
315 auto file
316 = std::make_unique<FileItem>("/icons/javaclass.png", filename(p),
317 p.string());
318 packageItem->appendRow(std::move(file));
319 }
320 }
321
322 for (fs::directory_iterator i(srcPath); i != end_itr; ++i) {
323 fs::path p = *i;
324 if (fs::is_directory(p)) {
325 std::string pn = packageName;
326 if (!pn.empty())
327 pn += ".";
328 pn += filename(p);
329
330 javaTraversePackages(parent, p, pn);
331 }
332 }
333}
334
335void ExampleSourceViewer::javaTraverseDir(WStandardItem* parent,
336 const fs::path& path)
337{
338 auto dir
339 = std::make_unique<FileItem>("/icons/yellow-folder-open.png",
340 filename(path),"");
341 FileItem *dirPtr = dir.get();
342 parent->appendRow(std::move(dir));
343 parent = dirPtr;
344
345 std::vector<fs::path> files, dirs;
346
347 fs::directory_iterator end_itr;
348 for (fs::directory_iterator i(path); i != end_itr; ++i) {
349 fs::path p = *i;
350 if (fs::is_directory(p)) {
351 if (filename(p) == "src") {
352 auto dir
353 = std::make_unique<FileItem>("/icons/package-folder-open.png",
354 filename(p), "");
355 FileItem *dirPtr = dir.get();
356 parent->appendRow(std::move(dir));
357 javaTraversePackages(dirPtr, p, "");
358 } else
359 dirs.push_back(p);
360 } else {
361 files.push_back(p);
362 }
363 }
364
365 std::sort(dirs.begin(), dirs.end(), comparePaths);
366 std::sort(files.begin(), files.end(), comparePaths);
367
368 for (auto item : dirs)
369 javaTraverseDir(parent, item);
370
371 for (auto item : files) {
372 auto file
373 = std::make_unique<FileItem>("/icons/document.png", filename(item),
374 item.string());
375 parent->appendRow(std::move(file));
376 }
377}
378
382 if (exampleView_->selectedIndexes().empty())
383 return;
384
385 WModelIndex selected = *exampleView_->selectedIndexes().begin();
386
387 // expand a folder when clicked
388 if (exampleView_->model()->rowCount(selected) > 0
389 && !exampleView_->isExpanded(selected))
390 exampleView_->setExpanded(selected, true);
391
392 // (for a file,) load data in source viewer
393 sourceView_->setIndex(selected);
394}
static fs::path getCompanion(const fs::path &path)
static std::string stem(const fs::path &p)
static std::string filename(const fs::path &p)
static bool comparePaths(const fs::path &p1, const fs::path &p2)
fs::path parent_path(const fs::path &p)
void cppTraverseDir(WStandardItem *parent, const boost::filesystem::path &path)
void setExample(const std::string &exampleDir, const std::string &example)
std::shared_ptr< WStandardItemModel > model_
ExampleSourceViewer(const std::string &deployPath, const std::string &examplesRoot, const std::string &examplesType)
Constructor.
void javaTraversePackages(WStandardItem *parent, const boost::filesystem::path &srcPath, const std::string packageName)
void showFile()
Displayed the currently selected file.
void javaTraverseDir(WStandardItem *parent, const boost::filesystem::path &path)
WStandardItem which stores a file.
Definition: FileItem.h:31
static const Wt::ItemDataRole FileNameRole
Definition: FileItem.h:35
static const Wt::ItemDataRole FilePathRole
Definition: FileItem.h:34
static const Wt::ItemDataRole ContentsRole
Definition: FileItem.h:33
bool setIndex(const WModelIndex &index)
Sets the model index.
Definition: SourceView.C:32