Wt examples  4.10.4
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 
32 namespace fs = boost::filesystem;
33 
34 // Same as p.filename() in latest boost::filesystem
35 static 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
45 static 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
57 fs::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 
65 static bool comparePaths(const fs::path& p1, const fs::path& p2)
66 {
67  return filename(p1) > filename(p2);
68 }
69 
70 ExampleSourceViewer::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 
100 void 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();
132  exampleView_->resize(300, WLength::Auto);
136  exampleView_->setSelectionMode(SelectionMode::Single);
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  */
185 static 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 
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 
331  }
332  }
333 }
334 
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
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)