1 #include "tab_deck_storage.h"
2 
3 #include "abstractclient.h"
4 #include "deck_loader.h"
5 #include "decklist.h"
6 #include "pb/command_deck_del.pb.h"
7 #include "pb/command_deck_del_dir.pb.h"
8 #include "pb/command_deck_download.pb.h"
9 #include "pb/command_deck_new_dir.pb.h"
10 #include "pb/command_deck_upload.pb.h"
11 #include "pb/response.pb.h"
12 #include "pb/response_deck_download.pb.h"
13 #include "pb/response_deck_upload.pb.h"
14 #include "pending_command.h"
15 #include "remotedecklist_treewidget.h"
16 #include "settingscache.h"
17 
18 #include <QAction>
19 #include <QApplication>
20 #include <QFileSystemModel>
21 #include <QGroupBox>
22 #include <QHBoxLayout>
23 #include <QHeaderView>
24 #include <QInputDialog>
25 #include <QMessageBox>
26 #include <QToolBar>
27 #include <QTreeView>
28 #include <QVBoxLayout>
29 
TabDeckStorage(TabSupervisor * _tabSupervisor,AbstractClient * _client)30 TabDeckStorage::TabDeckStorage(TabSupervisor *_tabSupervisor, AbstractClient *_client)
31     : Tab(_tabSupervisor), client(_client)
32 {
33     localDirModel = new QFileSystemModel(this);
34     localDirModel->setRootPath(SettingsCache::instance().getDeckPath());
35     localDirModel->sort(0, Qt::AscendingOrder);
36 
37     localDirView = new QTreeView;
38     localDirView->setModel(localDirModel);
39     localDirView->setColumnHidden(1, true);
40     localDirView->setRootIndex(localDirModel->index(localDirModel->rootPath(), 0));
41     localDirView->setSortingEnabled(true);
42     localDirView->header()->setSectionResizeMode(QHeaderView::ResizeToContents);
43     localDirView->header()->setSortIndicator(0, Qt::AscendingOrder);
44 
45     leftToolBar = new QToolBar;
46     leftToolBar->setOrientation(Qt::Horizontal);
47     leftToolBar->setIconSize(QSize(32, 32));
48     QHBoxLayout *leftToolBarLayout = new QHBoxLayout;
49     leftToolBarLayout->addStretch();
50     leftToolBarLayout->addWidget(leftToolBar);
51     leftToolBarLayout->addStretch();
52 
53     QVBoxLayout *leftVbox = new QVBoxLayout;
54     leftVbox->addWidget(localDirView);
55     leftVbox->addLayout(leftToolBarLayout);
56     leftGroupBox = new QGroupBox;
57     leftGroupBox->setLayout(leftVbox);
58 
59     rightToolBar = new QToolBar;
60     rightToolBar->setOrientation(Qt::Horizontal);
61     rightToolBar->setIconSize(QSize(32, 32));
62     QHBoxLayout *rightToolBarLayout = new QHBoxLayout;
63     rightToolBarLayout->addStretch();
64     rightToolBarLayout->addWidget(rightToolBar);
65     rightToolBarLayout->addStretch();
66 
67     serverDirView = new RemoteDeckList_TreeWidget(client);
68 
69     QVBoxLayout *rightVbox = new QVBoxLayout;
70     rightVbox->addWidget(serverDirView);
71     rightVbox->addLayout(rightToolBarLayout);
72     rightGroupBox = new QGroupBox;
73     rightGroupBox->setLayout(rightVbox);
74 
75     QHBoxLayout *hbox = new QHBoxLayout;
76     hbox->addWidget(leftGroupBox);
77     hbox->addWidget(rightGroupBox);
78 
79     aOpenLocalDeck = new QAction(this);
80     aOpenLocalDeck->setIcon(QPixmap("theme:icons/pencil"));
81     connect(aOpenLocalDeck, SIGNAL(triggered()), this, SLOT(actOpenLocalDeck()));
82     aUpload = new QAction(this);
83     aUpload->setIcon(QPixmap("theme:icons/arrow_right_green"));
84     connect(aUpload, SIGNAL(triggered()), this, SLOT(actUpload()));
85     aDeleteLocalDeck = new QAction(this);
86     aDeleteLocalDeck->setIcon(QPixmap("theme:icons/remove_row"));
87     connect(aDeleteLocalDeck, SIGNAL(triggered()), this, SLOT(actDeleteLocalDeck()));
88     aOpenRemoteDeck = new QAction(this);
89     aOpenRemoteDeck->setIcon(QPixmap("theme:icons/pencil"));
90     connect(aOpenRemoteDeck, SIGNAL(triggered()), this, SLOT(actOpenRemoteDeck()));
91     aDownload = new QAction(this);
92     aDownload->setIcon(QPixmap("theme:icons/arrow_left_green"));
93     connect(aDownload, SIGNAL(triggered()), this, SLOT(actDownload()));
94     aNewFolder = new QAction(this);
95     aNewFolder->setIcon(qApp->style()->standardIcon(QStyle::SP_FileDialogNewFolder));
96     connect(aNewFolder, SIGNAL(triggered()), this, SLOT(actNewFolder()));
97     aDeleteRemoteDeck = new QAction(this);
98     aDeleteRemoteDeck->setIcon(QPixmap("theme:icons/remove_row"));
99     connect(aDeleteRemoteDeck, SIGNAL(triggered()), this, SLOT(actDeleteRemoteDeck()));
100 
101     leftToolBar->addAction(aOpenLocalDeck);
102     leftToolBar->addAction(aUpload);
103     leftToolBar->addAction(aDeleteLocalDeck);
104     rightToolBar->addAction(aOpenRemoteDeck);
105     rightToolBar->addAction(aDownload);
106     rightToolBar->addAction(aNewFolder);
107     rightToolBar->addAction(aDeleteRemoteDeck);
108 
109     retranslateUi();
110 
111     QWidget *mainWidget = new QWidget(this);
112     mainWidget->setLayout(hbox);
113     setCentralWidget(mainWidget);
114 }
115 
retranslateUi()116 void TabDeckStorage::retranslateUi()
117 {
118     leftGroupBox->setTitle(tr("Local file system"));
119     rightGroupBox->setTitle(tr("Server deck storage"));
120 
121     aOpenLocalDeck->setText(tr("Open in deck editor"));
122     aUpload->setText(tr("Upload deck"));
123     aOpenRemoteDeck->setText(tr("Open in deck editor"));
124     aDownload->setText(tr("Download deck"));
125     aNewFolder->setText(tr("New folder"));
126     aDeleteLocalDeck->setText(tr("Delete"));
127     aDeleteRemoteDeck->setText(tr("Delete"));
128 }
129 
actOpenLocalDeck()130 void TabDeckStorage::actOpenLocalDeck()
131 {
132     QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
133     if (localDirModel->isDir(curLeft))
134         return;
135     QString filePath = localDirModel->filePath(curLeft);
136 
137     DeckLoader deckLoader;
138     if (!deckLoader.loadFromFile(filePath, DeckLoader::CockatriceFormat))
139         return;
140 
141     emit openDeckEditor(&deckLoader);
142 }
143 
actUpload()144 void TabDeckStorage::actUpload()
145 {
146     QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
147     if (localDirModel->isDir(curLeft))
148         return;
149     QString filePath = localDirModel->filePath(curLeft);
150     QFile deckFile(filePath);
151     QFileInfo deckFileInfo(deckFile);
152     DeckLoader deck;
153     if (!deck.loadFromFile(filePath, DeckLoader::CockatriceFormat))
154         return;
155     if (deck.getName().isEmpty()) {
156         bool ok;
157         QString deckName = QInputDialog::getText(this, tr("Enter deck name"),
158                                                  tr("This decklist does not have a name.\nPlease enter a name:"),
159                                                  QLineEdit::Normal, deckFileInfo.completeBaseName(), &ok);
160         if (!ok)
161             return;
162         if (deckName.isEmpty())
163             deckName = tr("Unnamed deck");
164         deck.setName(deckName);
165     }
166 
167     QString targetPath;
168     RemoteDeckList_TreeModel::Node *curRight = serverDirView->getCurrentItem();
169     if (!curRight)
170         return;
171     if (!dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight))
172         curRight = curRight->getParent();
173     targetPath = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight)->getPath();
174 
175     Command_DeckUpload cmd;
176     cmd.set_path(targetPath.toStdString());
177     cmd.set_deck_list(deck.writeToString_Native().toStdString());
178 
179     PendingCommand *pend = client->prepareSessionCommand(cmd);
180     connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
181             SLOT(uploadFinished(Response, CommandContainer)));
182     client->sendCommand(pend);
183 }
184 
uploadFinished(const Response & r,const CommandContainer & commandContainer)185 void TabDeckStorage::uploadFinished(const Response &r, const CommandContainer &commandContainer)
186 {
187     if (r.response_code() != Response::RespOk)
188         return;
189 
190     const Response_DeckUpload &resp = r.GetExtension(Response_DeckUpload::ext);
191     const Command_DeckUpload &cmd = commandContainer.session_command(0).GetExtension(Command_DeckUpload::ext);
192 
193     serverDirView->addFileToTree(resp.new_file(), serverDirView->getNodeByPath(QString::fromStdString(cmd.path())));
194 }
195 
actDeleteLocalDeck()196 void TabDeckStorage::actDeleteLocalDeck()
197 {
198     QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
199     if (localDirModel->isDir(curLeft))
200         return;
201 
202     if (QMessageBox::warning(this, tr("Delete local file"),
203                              tr("Are you sure you want to delete \"%1\"?").arg(localDirModel->fileName(curLeft)),
204                              QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
205         return;
206 
207     localDirModel->remove(curLeft);
208 }
209 
actOpenRemoteDeck()210 void TabDeckStorage::actOpenRemoteDeck()
211 {
212     RemoteDeckList_TreeModel::FileNode *curRight =
213         dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(serverDirView->getCurrentItem());
214     if (!curRight)
215         return;
216 
217     Command_DeckDownload cmd;
218     cmd.set_deck_id(curRight->getId());
219 
220     PendingCommand *pend = client->prepareSessionCommand(cmd);
221     connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
222             SLOT(openRemoteDeckFinished(Response, CommandContainer)));
223     client->sendCommand(pend);
224 }
225 
openRemoteDeckFinished(const Response & r,const CommandContainer & commandContainer)226 void TabDeckStorage::openRemoteDeckFinished(const Response &r, const CommandContainer &commandContainer)
227 {
228     if (r.response_code() != Response::RespOk)
229         return;
230 
231     const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
232     const Command_DeckDownload &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDownload::ext);
233 
234     DeckLoader loader;
235     if (!loader.loadFromRemote(QString::fromStdString(resp.deck()), cmd.deck_id()))
236         return;
237 
238     emit openDeckEditor(&loader);
239 }
240 
actDownload()241 void TabDeckStorage::actDownload()
242 {
243     QString filePath;
244     QModelIndex curLeft = localDirView->selectionModel()->currentIndex();
245     if (!curLeft.isValid())
246         filePath = localDirModel->rootPath();
247     else {
248         while (!localDirModel->isDir(curLeft))
249             curLeft = curLeft.parent();
250         filePath = localDirModel->filePath(curLeft);
251     }
252 
253     RemoteDeckList_TreeModel::FileNode *curRight =
254         dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(serverDirView->getCurrentItem());
255     if (!curRight)
256         return;
257     filePath += QString("/deck_%1.cod").arg(curRight->getId());
258 
259     Command_DeckDownload cmd;
260     cmd.set_deck_id(curRight->getId());
261 
262     PendingCommand *pend = client->prepareSessionCommand(cmd);
263     pend->setExtraData(filePath);
264     connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
265             SLOT(downloadFinished(Response, CommandContainer, QVariant)));
266     client->sendCommand(pend);
267 }
268 
downloadFinished(const Response & r,const CommandContainer &,const QVariant & extraData)269 void TabDeckStorage::downloadFinished(const Response &r,
270                                       const CommandContainer & /*commandContainer*/,
271                                       const QVariant &extraData)
272 {
273     if (r.response_code() != Response::RespOk)
274         return;
275 
276     const Response_DeckDownload &resp = r.GetExtension(Response_DeckDownload::ext);
277     QString filePath = extraData.toString();
278 
279     DeckLoader deck(QString::fromStdString(resp.deck()));
280     deck.saveToFile(filePath, DeckLoader::CockatriceFormat);
281 }
282 
actNewFolder()283 void TabDeckStorage::actNewFolder()
284 {
285     QString folderName = QInputDialog::getText(this, tr("New folder"), tr("Name of new folder:"));
286     if (folderName.isEmpty())
287         return;
288 
289     // '/' isn't a valid filename character on *nix so we're choosing to replace it with a different arbitrary
290     // character.
291     std::string folder = folderName.toStdString();
292     std::replace(folder.begin(), folder.end(), '/', '-');
293 
294     QString targetPath;
295     RemoteDeckList_TreeModel::Node *curRight = serverDirView->getCurrentItem();
296     if (!curRight)
297         return;
298     if (!dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight))
299         curRight = curRight->getParent();
300     RemoteDeckList_TreeModel::DirectoryNode *dir = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight);
301     targetPath = dir->getPath();
302 
303     Command_DeckNewDir cmd;
304     cmd.set_path(targetPath.toStdString());
305     cmd.set_dir_name(folder);
306 
307     PendingCommand *pend = client->prepareSessionCommand(cmd);
308     connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
309             SLOT(newFolderFinished(Response, CommandContainer)));
310     client->sendCommand(pend);
311 }
312 
newFolderFinished(const Response & response,const CommandContainer & commandContainer)313 void TabDeckStorage::newFolderFinished(const Response &response, const CommandContainer &commandContainer)
314 {
315     if (response.response_code() != Response::RespOk)
316         return;
317 
318     const Command_DeckNewDir &cmd = commandContainer.session_command(0).GetExtension(Command_DeckNewDir::ext);
319     serverDirView->addFolderToTree(QString::fromStdString(cmd.dir_name()),
320                                    serverDirView->getNodeByPath(QString::fromStdString(cmd.path())));
321 }
322 
actDeleteRemoteDeck()323 void TabDeckStorage::actDeleteRemoteDeck()
324 {
325     PendingCommand *pend;
326     RemoteDeckList_TreeModel::Node *curRight = serverDirView->getCurrentItem();
327     if (!curRight)
328         return;
329     RemoteDeckList_TreeModel::DirectoryNode *dir = dynamic_cast<RemoteDeckList_TreeModel::DirectoryNode *>(curRight);
330     if (dir) {
331         QString path = dir->getPath();
332         if (path.isEmpty())
333             return;
334         if (QMessageBox::warning(this, tr("Delete remote folder"),
335                                  tr("Are you sure you want to delete \"%1\"?").arg(path),
336                                  QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
337             return;
338         Command_DeckDelDir cmd;
339         cmd.set_path(path.toStdString());
340         pend = client->prepareSessionCommand(cmd);
341         connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
342                 SLOT(deleteFolderFinished(Response, CommandContainer)));
343     } else {
344         RemoteDeckList_TreeModel::FileNode *deckNode = dynamic_cast<RemoteDeckList_TreeModel::FileNode *>(curRight);
345         if (QMessageBox::warning(this, tr("Delete remote deck"),
346                                  tr("Are you sure you want to delete \"%1\"?").arg(deckNode->getName()),
347                                  QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
348             return;
349 
350         Command_DeckDel cmd;
351         cmd.set_deck_id(deckNode->getId());
352         pend = client->prepareSessionCommand(cmd);
353         connect(pend, SIGNAL(finished(Response, CommandContainer, QVariant)), this,
354                 SLOT(deleteDeckFinished(Response, CommandContainer)));
355     }
356 
357     client->sendCommand(pend);
358 }
359 
deleteDeckFinished(const Response & response,const CommandContainer & commandContainer)360 void TabDeckStorage::deleteDeckFinished(const Response &response, const CommandContainer &commandContainer)
361 {
362     if (response.response_code() != Response::RespOk)
363         return;
364 
365     const Command_DeckDel &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDel::ext);
366     RemoteDeckList_TreeModel::Node *toDelete = serverDirView->getNodeById(cmd.deck_id());
367     if (toDelete)
368         serverDirView->removeNode(toDelete);
369 }
370 
deleteFolderFinished(const Response & response,const CommandContainer & commandContainer)371 void TabDeckStorage::deleteFolderFinished(const Response &response, const CommandContainer &commandContainer)
372 {
373     if (response.response_code() != Response::RespOk)
374         return;
375 
376     const Command_DeckDelDir &cmd = commandContainer.session_command(0).GetExtension(Command_DeckDelDir::ext);
377     RemoteDeckList_TreeModel::Node *toDelete = serverDirView->getNodeByPath(QString::fromStdString(cmd.path()));
378     if (toDelete)
379         serverDirView->removeNode(toDelete);
380 }
381