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