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