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

Generated on Tue Dec 15 2020 for the C++ Web Toolkit (Wt) by doxygen 1.8.13