Wt examples 3.3.12
TreeViewDragDrop.C
Go to the documentation of this file.
1/*
2 * Copyright (C) 2008 Emweb bvba, Kessel-Lo, Belgium.
3 *
4 * See the LICENSE file for terms of use.
5 */
6#include <fstream>
7
8#include <Wt/WApplication>
9#include <Wt/WComboBox>
10#include <Wt/WContainerWidget>
11#include <Wt/WDatePicker>
12#include <Wt/WDateValidator>
13#include <Wt/WDialog>
14#include <Wt/WEnvironment>
15#include <Wt/WIntValidator>
16#include <Wt/WItemDelegate>
17#include <Wt/WLabel>
18#include <Wt/WLineEdit>
19#include <Wt/WMessageBox>
20#include <Wt/WPushButton>
21#include <Wt/WRegExpValidator>
22#include <Wt/WGridLayout>
23#include <Wt/WPopupMenu>
24#include <Wt/WSortFilterProxyModel>
25#include <Wt/WStandardItem>
26#include <Wt/WStandardItemModel>
27#include <Wt/WTableView>
28#include <Wt/WTreeView>
29#include <Wt/WText>
30#include <Wt/WVBoxLayout>
31
32#include <Wt/Chart/WPieChart>
33
34#include "CsvUtil.h"
35#include "FolderView.h"
36
37using namespace Wt;
38
43
52{
53public:
58
61 virtual std::string mimeType() const {
63 }
64
67
70};
71
74
78class FileEditDialog : public WDialog
79{
80public:
82 : WDialog("Edit..."),
83 model_(model),
84 item_(item)
85 {
86 int modelRow = item_.row();
87
89
90 /*
91 * Create the form widgets, and load them with data from the model.
92 */
93
94 // name
95 nameEdit_ = new WLineEdit(asString(model_->data(modelRow, 1)));
96
97 // type
98 typeEdit_ = new WComboBox();
99 typeEdit_->addItem("Document");
100 typeEdit_->addItem("Spreadsheet");
101 typeEdit_->addItem("Presentation");
103 (typeEdit_->findText(asString(model_->data(modelRow, 2))));
104
105 // size
106 sizeEdit_ = new WLineEdit(asString(model_->data(modelRow, 3)));
108 (new WIntValidator(0, std::numeric_limits<int>::max(), this));
109
110 // created
114 createdPicker_->setDate(boost::any_cast<WDate>(model_->data(modelRow, 4)));
115
116 // modified
120 modifiedPicker_->setDate(boost::any_cast<WDate>(model_->data(modelRow, 5)));
121
122 /*
123 * Use a grid layout for the labels and fields
124 */
125 WGridLayout *layout = new WGridLayout();
126
127 WLabel *l;
128 int row = 0;
129
130 layout->addWidget(l = new WLabel("Name:"), row, 0);
131 layout->addWidget(nameEdit_, row, 1);
133 ++row;
134
135 layout->addWidget(l = new WLabel("Type:"), row, 0);
136 layout->addWidget(typeEdit_, row, 1);
138 ++row;
139
140 layout->addWidget(l = new WLabel("Size:"), row, 0);
141 layout->addWidget(sizeEdit_, row, 1);
143 ++row;
144
145 layout->addWidget(l = new WLabel("Created:"), row, 0);
146 layout->addWidget(createdPicker_->lineEdit(), row, 1);
147 layout->addWidget(createdPicker_, row, 2);
149 ++row;
150
151 layout->addWidget(l = new WLabel("Modified:"), row, 0);
152 layout->addWidget(modifiedPicker_->lineEdit(), row, 1);
153 layout->addWidget(modifiedPicker_, row, 2);
155 ++row;
156
157 WPushButton *b;
158 WContainerWidget *buttons = new WContainerWidget();
159 buttons->addWidget(b = new WPushButton("Save"));
160 b->clicked().connect(this, &WDialog::accept);
162 buttons->addWidget(b = new WPushButton("Cancel"));
163 b->clicked().connect(this, &WDialog::reject);
164
165 /*
166 * Focus the form widget that corresonds to the selected item.
167 */
168 switch (item.column()) {
169 case 2:
170 typeEdit_->setFocus(); break;
171 case 3:
172 sizeEdit_->setFocus(); break;
173 case 4:
174 createdPicker_->lineEdit()->setFocus(); break;
175 case 5:
176 modifiedPicker_->lineEdit()->setFocus(); break;
177 default:
178 nameEdit_->setFocus(); break;
179 }
180
181 layout->addWidget(buttons, row, 0, 0, 3, AlignCenter);
182 layout->setColumnStretch(1, 1);
183
184 contents()->setLayout(layout);
185
187
188 show();
189 }
190
191private:
194
198
200 {
201 if (result == WDialog::Accepted) {
202 /*
203 * Update the model with data from the edit widgets.
204 *
205 * You will want to do some validation here...
206 *
207 * Note that we directly update the source model to avoid
208 * problems caused by the dynamic sorting of the proxy model,
209 * which reorders row numbers, and would cause us to switch to editing
210 * the wrong data.
211 */
213 int modelRow = item_.row();
214
215 WAbstractProxyModel *proxyModel = dynamic_cast<WAbstractProxyModel *>(m);
216 if (proxyModel) {
217 m = proxyModel->sourceModel();
218 modelRow = proxyModel->mapToSource(item_).row();
219 }
220
221 m->setData(modelRow, 1, boost::any(nameEdit_->text()));
222 m->setData(modelRow, 2, boost::any(typeEdit_->currentText()));
223 m->setData(modelRow, 3, boost::any(boost::lexical_cast<int>
224 (sizeEdit_->text().toUTF8())));
225 m->setData(modelRow, 4, boost::any(createdPicker_->date()));
226 m->setData(modelRow, 5, boost::any(modifiedPicker_->date()));
227 }
228
229 delete this;
230 }
231
232};
233
238{
239public:
243 : WApplication(env),
244 popup_(0),
246 {
247 setCssTheme("polished");
248
249 /*
250 * Create the data models.
251 */
252 folderModel_ = new WStandardItemModel(0, 1, this);
254
255 fileModel_ = new FileModel(this);
257
258 /*
259 The header items are also endered using an ItemDelegate, and thus
260 support other data, e.g.:
261
262 fileModel_->setHeaderFlags(0, Horizontal, HeaderIsUserCheckable);
263 fileModel_->setHeaderData(0, Horizontal,
264 std::string("icons/file.gif"),
265 Wt::DecorationRole);
266 */
272
273 /*
274 * Setup the user interface.
275 */
276 createUI();
277 }
278
280 delete popup_;
281 delete popupActionBox_;
282 }
283
284private:
287
290
293
295 std::map<std::string, WString> folderNameMap_;
296
299
302
305
308
311 void createUI() {
312 WContainerWidget *w = root();
313 w->setStyleClass("maindiv");
314
315 /*
316 * The main layout is a 3x2 grid layout.
317 */
318 WGridLayout *layout = new WGridLayout();
319 layout->addWidget(createTitle("Folders"), 0, 0);
320 layout->addWidget(createTitle("Files"), 0, 1);
321 layout->addWidget(folderView(), 1, 0);
322 layout->setColumnResizable(0);
323
324 // select the first folder
326
327 WVBoxLayout *vbox = new WVBoxLayout();
328 vbox->addWidget(fileView(), 1);
329 vbox->addWidget(pieChart(), 1);
330 vbox->setResizable(0);
331
332 layout->addLayout(vbox, 1, 1);
333
334 layout->addWidget(aboutDisplay(), 2, 0, 1, 2);
335
336 /*
337 * Let row 1 and column 1 take the excess space.
338 */
339 layout->setRowStretch(1, 1);
340 layout->setColumnStretch(1, 1);
341
342 w->setLayout(layout);
343 }
344
348 WText *result = new WText(title);
349 result->setInline(false);
350 result->setStyleClass("title");
351
352 return result;
353 }
354
358 WTreeView *treeView = new FolderView();
359
360 /*
361 * To support right-click, we need to disable the built-in browser
362 * context menu.
363 *
364 * Note that disabling the context menu and catching the
365 * right-click does not work reliably on all browsers.
366 */
367 treeView->setAttributeValue
368 ("oncontextmenu",
369 "event.cancelBubble = true; event.returnValue = false; return false;");
370 treeView->setModel(folderModel_);
371 treeView->resize(200, WLength::Auto);
373 treeView->expandToDepth(1);
374 treeView->selectionChanged()
376
377 treeView->mouseWentUp().connect(this, &TreeViewDragDrop::showPopup);
378
379 folderView_ = treeView;
380
381 return treeView;
382 }
383
387 WTableView *tableView = new WTableView();
388
389 tableView->setAlternatingRowColors(true);
390
391 tableView->setModel(fileFilterModel_);
393 tableView->setDragEnabled(true);
394
395 tableView->setColumnWidth(0, 100);
396 tableView->setColumnWidth(1, 150);
397 tableView->setColumnWidth(2, 100);
398 tableView->setColumnWidth(3, 60);
399 tableView->setColumnWidth(4, 100);
400 tableView->setColumnWidth(5, 100);
401
402 WItemDelegate *delegate = new WItemDelegate(this);
404 tableView->setItemDelegateForColumn(4, delegate);
405 tableView->setItemDelegateForColumn(5, delegate);
406
407 tableView->setColumnAlignment(3, AlignRight);
408 tableView->setColumnAlignment(4, AlignRight);
409 tableView->setColumnAlignment(5, AlignRight);
410
411 tableView->sortByColumn(1, AscendingOrder);
412
413 tableView->doubleClicked().connect(this, &TreeViewDragDrop::editFile);
414
415 fileView_ = tableView;
416
417 return tableView;
418 }
419
422 void editFile(const WModelIndex& item) {
423 new FileEditDialog(fileView_->model(), item);
424 }
425
429 using namespace Chart;
430
431 WPieChart *chart = new WPieChart();
432 // chart->setPreferredMethod(WPaintedWidget::PngImage);
433 chart->setModel(fileFilterModel_);
434 chart->setTitle("File sizes");
435
436 chart->setLabelsColumn(1); // Name
437 chart->setDataColumn(3); // Size
438
439 chart->setPerspectiveEnabled(true, 0.2);
440 chart->setDisplayLabels(Outside | TextLabel);
441
442 if (!WApplication::instance()->environment().ajax()) {
443 chart->resize(500, 200);
444 chart->setMargin(WLength::Auto, Left | Right);
446 w->addWidget(chart);
447 w->setStyleClass("about");
448 return w;
449 } else {
450 chart->setStyleClass("about");
451 return chart;
452 }
453 }
454
458 WText *result = new WText(WString::tr("about-text"));
459 result->setStyleClass("about");
460 return result;
461 }
462
467 if (folderView_->selectedIndexes().empty())
468 return;
469
470 WModelIndex selected = *folderView_->selectedIndexes().begin();
471 boost::any d = selected.data(UserRole);
472 if (!d.empty()) {
473 std::string folder = boost::any_cast<std::string>(d);
474
475 // For simplicity, we assume here that the folder-id does not
476 // contain special regexp characters, otherwise these need to be
477 // escaped -- or use the \Q \E qutoing escape regular expression
478 // syntax (and escape \E)
480 }
481 }
482
485 void showPopup(const WModelIndex& item, const WMouseEvent& event) {
486 if (event.button() == WMouseEvent::RightButton) {
487 // Select the item, it was not yet selected.
488 if (!folderView_->isSelected(item))
489 folderView_->select(item);
490
491 if (!popup_) {
492 popup_ = new WPopupMenu();
493 popup_->addItem("icons/folder_new.gif", "Create a New Folder");
494 popup_->addItem("Rename this Folder")->setCheckable(true);
495 popup_->addItem("Delete this Folder");
497 popup_->addItem("Folder Details");
499 popup_->addItem("Application Inventory");
500 popup_->addItem("Hardware Inventory");
502
503 WPopupMenu *subMenu = new WPopupMenu();
504 subMenu->addItem("Sub Item 1");
505 subMenu->addItem("Sub Item 2");
506 popup_->addMenu("File Deployments", subMenu);
507
508 /*
509 * This is one method of executing a popup, which does not block a
510 * thread for a reentrant event loop, and thus scales.
511 *
512 * Alternatively you could call WPopupMenu::exec(), which returns
513 * the result, but while waiting for it, blocks the thread.
514 */
516 }
517
518 if (popup_->isHidden())
519 popup_->popup(event);
520 else
521 popup_->hide();
522 }
523 }
524
527 void popupAction() {
528 if (popup_->result()) {
529 /*
530 * You could also bind extra data to an item using setData() and
531 * check here for the action asked. For now, we just use the text.
532 */
533 WString text = popup_->result()->text();
534 popup_->hide();
535
536 popupActionBox_ = new WMessageBox("Sorry.","Action '" + text
537 + "' is not implemented.", NoIcon, Ok);
539 .connect(this, &TreeViewDragDrop::dialogDone);
541 } else {
542 popup_->hide();
543 }
544 }
545
548 void dialogDone() {
549 delete popupActionBox_;
550 popupActionBox_ = 0;
551 }
552
562
563 std::ifstream f((appRoot() + "data/files.csv").c_str());
564
565 if (!f)
566 throw std::runtime_error("Could not read: data/files.csv");
567
569
570 for (int i = 0; i < fileModel_->rowCount(); ++i) {
571 WStandardItem *item = fileModel_->item(i, 0);
572 item->setFlags(item->flags() | ItemIsDragEnabled);
573 item->setIcon("icons/file.gif");
574
575 std::string folderId = item->text().toUTF8();
576
577 item->setData(boost::any(folderId), UserRole);
578 item->setText(folderNameMap_[folderId]);
579
583 }
584 }
585
590 item->setData(boost::any(d), DisplayRole);
591 }
592
596 int i = boost::lexical_cast<int>(item->text());
597 item->setData(boost::any(i), EditRole);
598 }
599
603 WStandardItem *level1, *level2;
604
605 folderModel_->appendRow(level1 = createFolderItem("San Fransisco"));
606 level1->appendRow(level2 = createFolderItem("Investors", "sf-investors"));
607 level1->appendRow(level2 = createFolderItem("Fellows", "sf-fellows"));
608
609 folderModel_->appendRow(level1 = createFolderItem("Sophia Antipolis"));
610 level1->appendRow(level2 = createFolderItem("R&D", "sa-r_d"));
611 level1->appendRow(level2 = createFolderItem("Services", "sa-services"));
612 level1->appendRow(level2 = createFolderItem("Support", "sa-support"));
613 level1->appendRow(level2 = createFolderItem("Billing", "sa-billing"));
614
615 folderModel_->appendRow(level1 = createFolderItem("New York"));
616 level1->appendRow(level2 = createFolderItem("Marketing", "ny-marketing"));
617 level1->appendRow(level2 = createFolderItem("Sales", "ny-sales"));
618 level1->appendRow(level2 = createFolderItem("Advisors", "ny-advisors"));
619
621 (WString::fromUTF8("Frankfürt")));
622 level1->appendRow(level2 = createFolderItem("Sales", "frank-sales"));
623
625 boost::any(std::string("SandBox")));
626 }
627
633 const std::string& folderId = std::string())
634 {
635 WStandardItem *result = new WStandardItem(location);
636
637 if (!folderId.empty()) {
638 result->setData(boost::any(folderId));
639 result->setFlags(result->flags() | ItemIsDropEnabled);
640 folderNameMap_[folderId] = location;
641 } else
642 result->setFlags(result->flags().clear(ItemIsSelectable));
643
644 result->setIcon("icons/folder.gif");
645
646 return result;
647 }
648
649};
650
652{
653 WApplication *app = new TreeViewDragDrop(env);
655 app->setTitle("WTreeView Drag & Drop");
656 app->useStyleSheet("styles.css");
657 app->messageResourceBundle().use(WApplication::appRoot() + "about");
658 app->refresh();
659
660 return app;
661}
662
663int main(int argc, char **argv)
664{
665 return WRun(argc, argv, &createApplication);
666}
667
WApplication * createApplication(const WEnvironment &env)
int main(int argc, char **argv)
A dialog for editing a 'file'.
void handleFinish(DialogCode result)
WLineEdit * nameEdit_
WAbstractItemModel * model_
WLineEdit * sizeEdit_
WDatePicker * createdPicker_
WDatePicker * modifiedPicker_
WComboBox * typeEdit_
FileEditDialog(WAbstractItemModel *model, const WModelIndex &item)
A specialized standard item model which report a specific drag and drop mime type.
static WString dateEditFormat
Date edit format.
virtual std::string mimeType() const
Return the mime type.
FileModel(WObject *parent)
Constructor.
static WString dateDisplayFormat
Date display format.
A specialized treeview that supports a custom drop event.
Definition FolderView.h:20
static const char * FileSelectionMimeType
Constant that indicates the mime type for a selection of files.
Definition FolderView.h:26
Main application class.
void folderChanged()
Change the filter on the file view when the selected folder changes.
WText * createTitle(const WString &title)
Creates a title widget.
WWidget * pieChart()
Creates the chart.
WMessageBox * popupActionBox_
Message box to confirm the poup menu action.
WSortFilterProxyModel * fileFilterModel_
The sort filter proxy model that adapts fileModel_.
std::map< std::string, WString > folderNameMap_
Maps folder id's to folder descriptions.
void editFile(const WModelIndex &item)
Edit a particular row.
WStandardItem * createFolderItem(const WString &location, const std::string &folderId=std::string())
Create a folder item.
WTreeView * folderView()
Creates the folder WTreeView.
WPopupMenu * popup_
Popup menu on the folder view.
void createUI()
Setup the user interface.
virtual ~TreeViewDragDrop()
WWidget * aboutDisplay()
Creates the hints text.
WTableView * fileView()
Creates the file table view (a WTableView)
WStandardItemModel * folderModel_
The folder model (used by folderView_)
WTableView * fileView_
The file view.
WStandardItemModel * fileModel_
The file model (used by fileView_)
void convertToDate(WStandardItem *item)
Convert a string to a date.
void showPopup(const WModelIndex &item, const WMouseEvent &event)
Show a popup for a folder item.
TreeViewDragDrop(const WEnvironment &env)
Constructor.
void popupAction()
Process the result of the popup menu.
void populateFiles()
Populate the files model.
void convertToNumber(WStandardItem *item)
Convert a string to a number.
void dialogDone()
Process the result of the message box.
WTreeView * folderView_
The folder view.
void populateFolders()
Populate the folders model.
Wt::Signals::connection connect(const F &function)
virtual Wt::Signals::connection connect(WObject *target, WObject::Method method)
WObject * parent() const
virtual bool setHeaderData(int section, Orientation orientation, const boost::any &value, int role=EditRole)
virtual bool setData(const WModelIndex &index, const boost::any &value, int role=EditRole)
virtual WModelIndex index(int row, int column, const WModelIndex &parent=WModelIndex()) const=0
virtual boost::any data(const WModelIndex &index, int role=DisplayRole) const=0
virtual int rowCount(const WModelIndex &parent=WModelIndex()) const=0
WModelIndexSet selectedIndexes() const
void setDragEnabled(bool enable)
Signal< WModelIndex, WMouseEvent > & mouseWentUp()
Signal & selectionChanged()
WAbstractItemModel * model() const
void select(const WModelIndex &index, SelectionFlag option=Select)
void setItemDelegateForColumn(int column, WAbstractItemDelegate *delegate)
bool isSelected(const WModelIndex &index) const
virtual void setColumnAlignment(int column, AlignmentFlag alignment)
Signal< WModelIndex, WMouseEvent > & doubleClicked()
void setSelectionMode(SelectionMode mode)
void sortByColumn(int column, SortOrder order)
virtual WModelIndex mapToSource(const WModelIndex &proxyIndex) const=0
WAbstractItemModel * sourceModel() const
virtual void refresh()
void setCssTheme(const std::string &name)
WEnvironment & env()
WMessageResourceBundle & messageResourceBundle()
friend friend class WContainerWidget
WContainerWidget * root() const
void setTwoPhaseRenderingThreshold(int size)
void setTitle(const WString &title)
static std::string appRoot()
void useStyleSheet(const WLink &link, const std::string &media="all")
const WEnvironment & environment() const
const WString & title() const
void addWidget(WWidget *widget, int stretch=0, WFlags< AlignmentFlag > alignment=0)
void setResizable(int index, bool enabled=true, const WLength &initialSize=WLength::Auto)
const WString currentText() const
void setCurrentIndex(int index)
int findText(const WString &text, WFlags< MatchFlag > flags=MatchExactly|MatchCaseSensitive)
void addItem(const WString &text)
virtual void setAttributeValue(const std::string &name, const WString &value)
virtual void resize(const WLength &width, const WLength &height)
virtual bool isHidden() const
void setLayout(WLayout *layout)
virtual void addWidget(WWidget *widget)
WDate date() const
void setFormat(const WString &format)
void setDate(const WDate &date)
WLineEdit * lineEdit() const
static WDate fromString(const WString &s)
Signal< DialogCode > & finished()
WContainerWidget * contents() const
DialogCode result() const
virtual void accept()
virtual void reject()
virtual WValidator * validator() const
void setValidator(WValidator *validator)
void setColumnStretch(int column, int stretch)
void setColumnResizable(int column, bool enabled=true, const WLength &initialSize=WLength::Auto)
void addWidget(WWidget *widget, int row, int column, WFlags< AlignmentFlag > alignment=0)
void addLayout(WLayout *layout, int row, int column, WFlags< AlignmentFlag > alignment=0)
void setRowStretch(int row, int stretch)
EventSignal & enterPressed()
EventSignal< WMouseEvent > & clicked()
void setTextFormat(const WString &format)
void setBuddy(WFormWidget *buddy)
static WLength Auto
const WString & text() const
const WString & text() const
void setCheckable(bool checkable)
WMenuItem * addItem(const WString &label, WWidget *contents=0, WMenuItem::LoadPolicy policy=WMenuItem::LazyLoading)
WMenuItem * addSeparator()
WMenuItem * addMenu(const WString &text, WMenu *menu)
Signal< StandardButton > & buttonClicked()
static StandardButton show(const WString &caption, const WString &text, WFlags< StandardButton > buttons, const WAnimation &animation=WAnimation())
void use(const std::string &path, bool loadInMemory=true)
int row() const
boost::any data(int role=DisplayRole) const
int column() const
Button button() const
Signal & aboutToHide()
void popup(const WPoint &point)
WMenuItem * result() const
void setFilterRole(int role)
void setFilterRegExp(const WString &pattern)
void setDynamicSortFilter(bool enable)
virtual void setSourceModel(WAbstractItemModel *sourceModel)
void setFilterKeyColumn(int column)
WStandardItem * invisibleRootItem() const
WStandardItem * item(int row, int column=0) const
void appendRow(const std::vector< WStandardItem * > &items)
void setIcon(const std::string &uri)
void setText(const WString &text)
WString text() const
WFlags< ItemFlag > flags() const
void appendRow(const std::vector< WStandardItem * > &items)
void setFlags(WFlags< ItemFlag > flags)
void setRowCount(int rows)
virtual void setData(const boost::any &data, int role=UserRole)
static WString tr(const char *key)
static WString fromUTF8(const std::string &value, bool checkValid=false)
std::string toUTF8() const
virtual void setModel(WAbstractItemModel *model)
virtual void setColumnWidth(int column, const WLength &width)
virtual void setAlternatingRowColors(bool enable)
virtual void setModel(WAbstractItemModel *model)
virtual void resize(const WLength &width, const WLength &height)
void expandToDepth(int depth)
void setMandatory(bool how)
virtual void setFocus(bool focus)
virtual void setInline(bool isInline)
virtual void setStyleClass(const WString &styleClass)
WString asString(const boost::any &v, const WString &formatString=WString())
DisplayRole
EditRole
UserRole
ItemIsDragEnabled
ItemIsSelectable
ItemIsDropEnabled
AscendingOrder
Horizontal
SingleSelection
ExtendedSelection
AlignCenter
AlignRight
void readFromCsv(std::istream &f, Wt::WAbstractItemModel *model, int numRows, bool firstLineIsHeaders)
Utility function that reads a model from a CSV file.
Definition CsvUtil.C:56

Generated on Fri May 17 2024 for the C++ Web Toolkit (Wt) by doxygen 1.9.8