00001
00002
00003
00004
00005
00006 #include <fstream>
00007
00008 #include <Wt/WApplication>
00009 #include <Wt/WComboBox>
00010 #include <Wt/WContainerWidget>
00011 #include <Wt/WDatePicker>
00012 #include <Wt/WDateValidator>
00013 #include <Wt/WDialog>
00014 #include <Wt/WEnvironment>
00015 #include <Wt/WIntValidator>
00016 #include <Wt/WItemDelegate>
00017 #include <Wt/WLabel>
00018 #include <Wt/WLineEdit>
00019 #include <Wt/WMessageBox>
00020 #include <Wt/WPushButton>
00021 #include <Wt/WRegExpValidator>
00022 #include <Wt/WGridLayout>
00023 #include <Wt/WPopupMenu>
00024 #include <Wt/WSortFilterProxyModel>
00025 #include <Wt/WStandardItem>
00026 #include <Wt/WStandardItemModel>
00027 #include <Wt/WTableView>
00028 #include <Wt/WTreeView>
00029 #include <Wt/WText>
00030 #include <Wt/WVBoxLayout>
00031
00032 #include <Wt/Chart/WPieChart>
00033
00034 #include "CsvUtil.h"
00035 #include "FolderView.h"
00036
00037 using namespace Wt;
00038
00043
00051 class FileModel : public WStandardItemModel
00052 {
00053 public:
00056 FileModel(WObject *parent)
00057 : WStandardItemModel(parent) { }
00058
00061 virtual std::string mimeType() const {
00062 return FolderView::FileSelectionMimeType;
00063 }
00064
00066 static WString dateDisplayFormat;
00067
00069 static WString dateEditFormat;
00070 };
00071
00072 WString FileModel::dateDisplayFormat(WString::fromUTF8("MMM dd, yyyy"));
00073 WString FileModel::dateEditFormat(WString::fromUTF8("dd-MM-yyyy"));
00074
00078 class FileEditDialog : public WDialog
00079 {
00080 public:
00081 FileEditDialog(WAbstractItemModel *model, const WModelIndex& item)
00082 : WDialog("Edit..."),
00083 model_(model),
00084 item_(item)
00085 {
00086 int modelRow = item_.row();
00087
00088 resize(300, WLength::Auto);
00089
00090
00091
00092
00093
00094
00095 nameEdit_ = new WLineEdit(asString(model_->data(modelRow, 1)));
00096
00097
00098 typeEdit_ = new WComboBox();
00099 typeEdit_->addItem("Document");
00100 typeEdit_->addItem("Spreadsheet");
00101 typeEdit_->addItem("Presentation");
00102 typeEdit_->setCurrentIndex
00103 (typeEdit_->findText(asString(model_->data(modelRow, 2))));
00104
00105
00106 sizeEdit_ = new WLineEdit(asString(model_->data(modelRow, 3)));
00107 sizeEdit_->setValidator
00108 (new WIntValidator(0, std::numeric_limits<int>::max(), this));
00109
00110
00111 createdPicker_ = new WDatePicker();
00112 createdPicker_->lineEdit()->validator()->setMandatory(true);
00113 createdPicker_->setFormat(FileModel::dateEditFormat);
00114 createdPicker_->setDate(boost::any_cast<WDate>(model_->data(modelRow, 4)));
00115
00116
00117 modifiedPicker_ = new WDatePicker();
00118 modifiedPicker_->lineEdit()->validator()->setMandatory(true);
00119 modifiedPicker_->setFormat(FileModel::dateEditFormat);
00120 modifiedPicker_->setDate(boost::any_cast<WDate>(model_->data(modelRow, 5)));
00121
00122
00123
00124
00125 WGridLayout *layout = new WGridLayout();
00126
00127 WLabel *l;
00128 int row = 0;
00129
00130 layout->addWidget(l = new WLabel("Name:"), row, 0);
00131 layout->addWidget(nameEdit_, row, 1);
00132 l->setBuddy(nameEdit_);
00133 ++row;
00134
00135 layout->addWidget(l = new WLabel("Type:"), row, 0);
00136 layout->addWidget(typeEdit_, row, 1, AlignTop);
00137 l->setBuddy(typeEdit_);
00138 ++row;
00139
00140 layout->addWidget(l = new WLabel("Size:"), row, 0);
00141 layout->addWidget(sizeEdit_, row, 1);
00142 l->setBuddy(sizeEdit_);
00143 ++row;
00144
00145 layout->addWidget(l = new WLabel("Created:"), row, 0);
00146 layout->addWidget(createdPicker_->lineEdit(), row, 1);
00147 layout->addWidget(createdPicker_, row, 2);
00148 l->setBuddy(createdPicker_->lineEdit());
00149 ++row;
00150
00151 layout->addWidget(l = new WLabel("Modified:"), row, 0);
00152 layout->addWidget(modifiedPicker_->lineEdit(), row, 1);
00153 layout->addWidget(modifiedPicker_, row, 2);
00154 l->setBuddy(modifiedPicker_->lineEdit());
00155 ++row;
00156
00157 WPushButton *b;
00158 WContainerWidget *buttons = new WContainerWidget();
00159 buttons->addWidget(b = new WPushButton("Save"));
00160 b->clicked().connect(this, &WDialog::accept);
00161 contents()->enterPressed().connect(this, &WDialog::accept);
00162 buttons->addWidget(b = new WPushButton("Cancel"));
00163 b->clicked().connect(this, &WDialog::reject);
00164
00165
00166
00167
00168 switch (item.column()) {
00169 case 2:
00170 typeEdit_->setFocus(); break;
00171 case 3:
00172 sizeEdit_->setFocus(); break;
00173 case 4:
00174 createdPicker_->lineEdit()->setFocus(); break;
00175 case 5:
00176 modifiedPicker_->lineEdit()->setFocus(); break;
00177 default:
00178 nameEdit_->setFocus(); break;
00179 }
00180
00181 layout->addWidget(buttons, row, 0, 0, 3, AlignCenter);
00182 layout->setColumnStretch(1, 1);
00183
00184 contents()->setLayout(layout, AlignTop | AlignJustify);
00185
00186 finished().connect(this, &FileEditDialog::handleFinish);
00187
00188 show();
00189 }
00190
00191 private:
00192 WAbstractItemModel *model_;
00193 WModelIndex item_;
00194
00195 WLineEdit *nameEdit_, *sizeEdit_;
00196 WComboBox *typeEdit_;
00197 WDatePicker *createdPicker_, *modifiedPicker_;
00198
00199 void handleFinish(DialogCode result)
00200 {
00201 if (result == WDialog::Accepted) {
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212 WAbstractItemModel *m = model_;
00213 int modelRow = item_.row();
00214
00215 WAbstractProxyModel *proxyModel = dynamic_cast<WAbstractProxyModel *>(m);
00216 if (proxyModel) {
00217 m = proxyModel->sourceModel();
00218 modelRow = proxyModel->mapToSource(item_).row();
00219 }
00220
00221 m->setData(modelRow, 1, boost::any(nameEdit_->text()));
00222 m->setData(modelRow, 2, boost::any(typeEdit_->currentText()));
00223 m->setData(modelRow, 3, boost::any(boost::lexical_cast<int>
00224 (sizeEdit_->text().toUTF8())));
00225 m->setData(modelRow, 4, boost::any(createdPicker_->date()));
00226 m->setData(modelRow, 5, boost::any(modifiedPicker_->date()));
00227 }
00228
00229 delete this;
00230 }
00231
00232 };
00233
00237 class TreeViewDragDrop : public WApplication
00238 {
00239 public:
00242 TreeViewDragDrop(const WEnvironment &env)
00243 : WApplication(env),
00244 popup_(0),
00245 popupActionBox_(0)
00246 {
00247 setCssTheme("polished");
00248
00249
00250
00251
00252 folderModel_ = new WStandardItemModel(0, 1, this);
00253 populateFolders();
00254
00255 fileModel_ = new FileModel(this);
00256 populateFiles();
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 fileFilterModel_ = new WSortFilterProxyModel(this);
00269 fileFilterModel_->setSourceModel(fileModel_);
00270 fileFilterModel_->setDynamicSortFilter(true);
00271 fileFilterModel_->setFilterKeyColumn(0);
00272 fileFilterModel_->setFilterRole(UserRole);
00273
00274
00275
00276
00277 createUI();
00278 }
00279
00280 virtual ~TreeViewDragDrop() {
00281 delete popup_;
00282 delete popupActionBox_;
00283 }
00284
00285 private:
00287 WStandardItemModel *folderModel_;
00288
00290 WStandardItemModel *fileModel_;
00291
00293 WSortFilterProxyModel *fileFilterModel_;
00294
00296 std::map<std::string, WString> folderNameMap_;
00297
00299 WTreeView *folderView_;
00300
00302 WTableView *fileView_;
00303
00305 WPopupMenu *popup_;
00306
00308 WMessageBox *popupActionBox_;
00309
00312 void createUI() {
00313 WContainerWidget *w = root();
00314 w->setStyleClass("maindiv");
00315
00316
00317
00318
00319 WGridLayout *layout = new WGridLayout();
00320 layout->addWidget(createTitle("Folders"), 0, 0);
00321 layout->addWidget(createTitle("Files"), 0, 1);
00322 layout->addWidget(folderView(), 1, 0);
00323 layout->setColumnResizable(0);
00324
00325
00326 folderView_->select(folderModel_->index(0, 0, folderModel_->index(0, 0)));
00327
00328 WVBoxLayout *vbox = new WVBoxLayout();
00329 vbox->addWidget(fileView(), 1);
00330 vbox->addWidget(pieChart(), 1);
00331 vbox->setResizable(0);
00332
00333 layout->addLayout(vbox, 1, 1);
00334
00335 layout->addWidget(aboutDisplay(), 2, 0, 1, 2, AlignTop);
00336
00337
00338
00339
00340 layout->setRowStretch(1, 1);
00341 layout->setColumnStretch(1, 1);
00342
00343 w->setLayout(layout);
00344 }
00345
00348 WText *createTitle(const WString& title) {
00349 WText *result = new WText(title);
00350 result->setInline(false);
00351 result->setStyleClass("title");
00352
00353 return result;
00354 }
00355
00358 WTreeView *folderView() {
00359 WTreeView *treeView = new FolderView();
00360
00361
00362
00363
00364
00365
00366
00367
00368 treeView->setAttributeValue
00369 ("oncontextmenu",
00370 "event.cancelBubble = true; event.returnValue = false; return false;");
00371 treeView->setModel(folderModel_);
00372 treeView->resize(200, WLength::Auto);
00373 treeView->setSelectionMode(SingleSelection);
00374 treeView->expandToDepth(1);
00375 treeView->selectionChanged()
00376 .connect(this, &TreeViewDragDrop::folderChanged);
00377
00378 treeView->mouseWentUp().connect(this, &TreeViewDragDrop::showPopup);
00379
00380 folderView_ = treeView;
00381
00382 return treeView;
00383 }
00384
00387 WTableView *fileView() {
00388 WTableView *tableView = new WTableView();
00389
00390 tableView->setAlternatingRowColors(true);
00391
00392 tableView->setModel(fileFilterModel_);
00393 tableView->setSelectionMode(ExtendedSelection);
00394 tableView->setDragEnabled(true);
00395
00396 tableView->setColumnWidth(0, 100);
00397 tableView->setColumnWidth(1, 150);
00398 tableView->setColumnWidth(2, 100);
00399 tableView->setColumnWidth(3, 60);
00400 tableView->setColumnWidth(4, 100);
00401 tableView->setColumnWidth(5, 100);
00402
00403 WItemDelegate *delegate = new WItemDelegate(this);
00404 delegate->setTextFormat(FileModel::dateDisplayFormat);
00405 tableView->setItemDelegateForColumn(4, delegate);
00406 tableView->setItemDelegateForColumn(5, delegate);
00407
00408 tableView->setColumnAlignment(3, AlignRight);
00409 tableView->setColumnAlignment(4, AlignRight);
00410 tableView->setColumnAlignment(5, AlignRight);
00411
00412 tableView->sortByColumn(1, AscendingOrder);
00413
00414 tableView->doubleClicked().connect(this, &TreeViewDragDrop::editFile);
00415
00416 fileView_ = tableView;
00417
00418 return tableView;
00419 }
00420
00423 void editFile(const WModelIndex& item) {
00424 new FileEditDialog(fileView_->model(), item);
00425 }
00426
00429 WWidget *pieChart() {
00430 using namespace Chart;
00431
00432 WPieChart *chart = new WPieChart();
00433 chart->setModel(fileFilterModel_);
00434 chart->setTitle("File sizes");
00435
00436 chart->setLabelsColumn(1);
00437 chart->setDataColumn(3);
00438
00439 chart->setPerspectiveEnabled(true, 0.2);
00440 chart->setDisplayLabels(Outside | TextLabel);
00441
00442 if (!WApplication::instance()->environment().ajax()) {
00443 chart->resize(500, 200);
00444 chart->setMargin(WLength::Auto, Left | Right);
00445 WContainerWidget *w = new WContainerWidget();
00446 w->addWidget(chart);
00447 w->setStyleClass("about");
00448 return w;
00449 } else {
00450 chart->setStyleClass("about");
00451 return chart;
00452 }
00453 }
00454
00457 WWidget *aboutDisplay() {
00458 WText *result = new WText(WString::tr("about-text"));
00459 result->setStyleClass("about");
00460 return result;
00461 }
00462
00466 void folderChanged() {
00467 if (folderView_->selectedIndexes().empty())
00468 return;
00469
00470 WModelIndex selected = *folderView_->selectedIndexes().begin();
00471 boost::any d = selected.data(UserRole);
00472 if (!d.empty()) {
00473 std::string folder = boost::any_cast<std::string>(d);
00474
00475
00476
00477
00478
00479 fileFilterModel_->setFilterRegExp(folder);
00480 }
00481 }
00482
00485 void showPopup(const WModelIndex& item, const WMouseEvent& event) {
00486 if (event.button() == WMouseEvent::RightButton) {
00487
00488 if (!folderView_->isSelected(item))
00489 folderView_->select(item);
00490
00491 if (!popup_) {
00492 popup_ = new WPopupMenu();
00493 popup_->addItem("icons/folder_new.gif", "Create a New Folder");
00494 popup_->addItem("Rename this Folder")->setCheckable(true);
00495 popup_->addItem("Delete this Folder");
00496 popup_->addSeparator();
00497 popup_->addItem("Folder Details");
00498 popup_->addSeparator();
00499 popup_->addItem("Application Inventory");
00500 popup_->addItem("Hardware Inventory");
00501 popup_->addSeparator();
00502
00503 WPopupMenu *subMenu = new WPopupMenu();
00504 subMenu->addItem("Sub Item 1");
00505 subMenu->addItem("Sub Item 2");
00506 popup_->addMenu("File Deployments", subMenu);
00507
00508
00509
00510
00511
00512
00513
00514
00515 popup_->aboutToHide().connect(this, &TreeViewDragDrop::popupAction);
00516 }
00517
00518 if (popup_->isHidden())
00519 popup_->popup(event);
00520 else
00521 popup_->hide();
00522 }
00523 }
00524
00527 void popupAction() {
00528 if (popup_->result()) {
00529
00530
00531
00532
00533 WString text = popup_->result()->text();
00534 popup_->hide();
00535
00536 popupActionBox_ = new WMessageBox("Sorry.","Action '" + text
00537 + "' is not implemented.", NoIcon, Ok);
00538 popupActionBox_->buttonClicked()
00539 .connect(this, &TreeViewDragDrop::dialogDone);
00540 popupActionBox_->show();
00541 } else {
00542 popup_->hide();
00543 }
00544 }
00545
00548 void dialogDone() {
00549 delete popupActionBox_;
00550 popupActionBox_ = 0;
00551 }
00552
00560 void populateFiles() {
00561 fileModel_->invisibleRootItem()->setRowCount(0);
00562
00563 std::ifstream f((appRoot() + "data/files.csv").c_str());
00564
00565 if (!f)
00566 throw std::runtime_error("Could not read: data/files.csv");
00567
00568 readFromCsv(f, fileModel_);
00569
00570 for (int i = 0; i < fileModel_->rowCount(); ++i) {
00571 WStandardItem *item = fileModel_->item(i, 0);
00572 item->setFlags(item->flags() | ItemIsDragEnabled);
00573 item->setIcon("icons/file.gif");
00574
00575 std::string folderId = item->text().toUTF8();
00576
00577 item->setData(boost::any(folderId), UserRole);
00578 item->setText(folderNameMap_[folderId]);
00579
00580 convertToDate(fileModel_->item(i, 4));
00581 convertToDate(fileModel_->item(i, 5));
00582 }
00583 }
00584
00587 void convertToDate(WStandardItem *item) {
00588 WDate d = WDate::fromString(item->text(), FileModel::dateEditFormat);
00589 item->setData(boost::any(d), DisplayRole);
00590 }
00591
00594 void populateFolders() {
00595 WStandardItem *level1, *level2;
00596
00597 folderModel_->appendRow(level1 = createFolderItem("San Fransisco"));
00598 level1->appendRow(level2 = createFolderItem("Investors", "sf-investors"));
00599 level1->appendRow(level2 = createFolderItem("Fellows", "sf-fellows"));
00600
00601 folderModel_->appendRow(level1 = createFolderItem("Sophia Antipolis"));
00602 level1->appendRow(level2 = createFolderItem("R&D", "sa-r_d"));
00603 level1->appendRow(level2 = createFolderItem("Services", "sa-services"));
00604 level1->appendRow(level2 = createFolderItem("Support", "sa-support"));
00605 level1->appendRow(level2 = createFolderItem("Billing", "sa-billing"));
00606
00607 folderModel_->appendRow(level1 = createFolderItem("New York"));
00608 level1->appendRow(level2 = createFolderItem("Marketing", "ny-marketing"));
00609 level1->appendRow(level2 = createFolderItem("Sales", "ny-sales"));
00610 level1->appendRow(level2 = createFolderItem("Advisors", "ny-advisors"));
00611
00612 folderModel_->appendRow(level1 = createFolderItem
00613 (WString::fromUTF8("Frankfürt")));
00614 level1->appendRow(level2 = createFolderItem("Sales", "frank-sales"));
00615
00616 folderModel_->setHeaderData(0, Horizontal,
00617 boost::any(std::string("SandBox")));
00618 }
00619
00624 WStandardItem *createFolderItem(const WString& location,
00625 const std::string& folderId = std::string())
00626 {
00627 WStandardItem *result = new WStandardItem(location);
00628
00629 if (!folderId.empty()) {
00630 result->setData(boost::any(folderId));
00631 result->setFlags(result->flags() | ItemIsDropEnabled);
00632 folderNameMap_[folderId] = location;
00633 } else
00634 result->setFlags(result->flags().clear(ItemIsSelectable));
00635
00636 result->setIcon("icons/folder.gif");
00637
00638 return result;
00639 }
00640
00641 };
00642
00643 WApplication *createApplication(const WEnvironment& env)
00644 {
00645 WApplication *app = new TreeViewDragDrop(env);
00646 app->setTwoPhaseRenderingThreshold(0);
00647 app->setTitle("WTreeView Drag & Drop");
00648 app->useStyleSheet("styles.css");
00649 app->messageResourceBundle().use(WApplication::appRoot() + "about");
00650 app->refresh();
00651
00652 return app;
00653 }
00654
00655 int main(int argc, char **argv)
00656 {
00657 return WRun(argc, argv, &createApplication);
00658 }
00659