Wt examples  4.11.3
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 <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 
39 namespace fs = boost::filesystem;
40 
41 // Same as p.filename() in latest boost::filesystem
42 static 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
52 static 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
64 fs::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 
72 static bool comparePaths(const fs::path& p1, const fs::path& p2)
73 {
74  return filename(p1) > filename(p2);
75 }
76 
77 ExampleSourceViewer::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 
107 void 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();
139  exampleView_->resize(300, WLength::Auto);
143  exampleView_->setSelectionMode(SelectionMode::Single);
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  */
192 static 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 
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 
338  }
339  }
340 }
341 
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
bool setIndex(const WModelIndex &index)
Sets the model index.
Definition: SourceView.C:38
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)