1 /***************************************************************************
2 *                                                                         *
3 *   This program is free software; you can redistribute it and/or modify  *
4 *   it under the terms of the GNU General Public License as published by  *
5 *   the Free Software Foundation; either version 3 of the License, or     *
6 *   (at your option) any later version.                                   *
7 *                                                                         *
8 ***************************************************************************/
9 
10 #include "SettingsSharing.h"
11 #include "HashProgress.h"
12 #include "WulforUtil.h"
13 #include "WulforSettings.h"
14 
15 #include "dcpp/stdinc.h"
16 #include "dcpp/SettingsManager.h"
17 #include "dcpp/ShareManager.h"
18 
19 #include <QListWidgetItem>
20 #include <QTreeWidget>
21 #include <QList>
22 #include <QMenu>
23 #include <QAction>
24 #include <QFileDialog>
25 #include <QInputDialog>
26 #include <QDir>
27 #include <QFile>
28 #include <QTextStream>
29 #include <QInputDialog>
30 #include <QMessageBox>
31 #include <QStack>
32 
33 using namespace dcpp;
34 
SettingsSharing(QWidget * parent)35 SettingsSharing::SettingsSharing(QWidget *parent):
36         QWidget(parent)
37 {
38     setupUi(this);
39 
40     model = NULL;
41 
42     init();
43 }
44 
~SettingsSharing()45 SettingsSharing::~SettingsSharing(){
46     delete model;
47 }
48 
showEvent(QShowEvent * e)49 void SettingsSharing::showEvent(QShowEvent *e){
50     e->accept();
51 
52     if (WSGET(WS_SHAREHEADER_STATE).isEmpty() && model){
53         for (int i = 0; i < model->columnCount(); i++)
54             treeView->setColumnWidth(i, treeView->width()/4);
55     }
56 }
57 
ok()58 void SettingsSharing::ok(){
59     SettingsManager *SM = SettingsManager::getInstance();
60 
61     SM->set(SettingsManager::FOLLOW_LINKS, checkBox_FOLLOW->isChecked());
62     SM->set(SettingsManager::USE_ADL_ONLY_OWN_LIST, checkBox_USE_ADL_ONLY_OWN_LIST->isChecked());
63     SM->set(SettingsManager::SHARE_TEMP_FILES, checkBox_SHARE_TEMP_FILES->isChecked());
64     SM->set(SettingsManager::MIN_UPLOAD_SPEED, spinBox_EXTRA->value());
65     SM->set(SettingsManager::SLOTS_PRIMARY, spinBox_UPLOAD->value());
66     SM->set(SettingsManager::MAX_HASH_SPEED, spinBox_MAXHASHSPEED->value());
67     SM->set(SettingsManager::FAST_HASH, checkBox_FASTHASH->isChecked());
68     SM->set(SettingsManager::AUTO_REFRESH_TIME, spinBox_REFRESH_TIME->value());
69     SM->set(SettingsManager::HASHING_START_DELAY, spinBox_HASHING_START_DELAY->value());
70     SM->set(SettingsManager::HASH_BUFFER_NORESERVE, checkBox_MAPNORESERVE->isChecked());
71     SM->set(SettingsManager::HASH_BUFFER_POPULATE, checkBox_MAPPOPULATE->isChecked());
72     SM->set(SettingsManager::HASH_BUFFER_PRIVATE, checkBox_MAPPRIVATE->isChecked());
73     SM->set(SettingsManager::HASH_BUFFER_SIZE_MB, comboBox_BUFSIZE->currentText().toInt());
74     SM->set(SettingsManager::SHARE_SKIP_ZERO_BYTE, checkBox_SHARE_SKIP_ZERO_BYTE->isChecked());
75 
76     QStringList list;
77     for (int k = 0; k < listWidget_SKIPLIST->count(); ++k)
78         list << listWidget_SKIPLIST->item(k)->text();
79 
80     SM->set(SettingsManager::SKIPLIST_SHARE, (list.isEmpty()? "|" : _tq(list.join("|"))));
81 
82     WBSET(WB_SIMPLE_SHARE_MODE, checkBox_SIMPLE_SHARE_MODE->isChecked());
83 
84     if (checkBox_SIMPLE_SHARE_MODE->isChecked())
85         SM->save();
86 
87     WSSET(WS_SHAREHEADER_STATE, treeView->header()->saveState().toBase64());
88     WSSET("settings-simple-share-headerstate", treeWidget_SIMPLE_MODE->header()->saveState().toBase64());
89     WBSET(WB_APP_REMOVE_NOT_EX_DIRS, checkBox_AUTOREMOVE->isChecked());
90 }
91 
init()92 void SettingsSharing::init(){
93     WulforUtil *WU = WulforUtil::getInstance();
94 
95     toolButton_ADD->setIcon(WU->getPixmap(WulforUtil::eiBOOKMARK_ADD));
96     toolButton_EDIT->setIcon(WU->getPixmap(WulforUtil::eiEDIT));
97     toolButton_DELETE->setIcon(WU->getPixmap(WulforUtil::eiEDITDELETE));
98     toolButton_BROWSE->setIcon(WU->getPixmap(WulforUtil::eiFOLDER_BLUE));
99 
100     toolButton_RECREATE->setIcon(WU->getPixmap(WulforUtil::eiRELOAD));
101 
102     checkBox_SHAREHIDDEN->setChecked(BOOLSETTING(SHARE_HIDDEN));
103     checkBox_SHARE_TEMP_FILES->setChecked(BOOLSETTING(SHARE_TEMP_FILES));
104     checkBox_FOLLOW->setChecked(BOOLSETTING(FOLLOW_LINKS));
105     checkBox_USE_ADL_ONLY_OWN_LIST->setChecked(BOOLSETTING(USE_ADL_ONLY_OWN_LIST));
106     spinBox_UPLOAD->setValue(SETTING(SLOTS_PRIMARY));
107     spinBox_MAXHASHSPEED->setValue(SETTING(MAX_HASH_SPEED));
108     spinBox_EXTRA->setValue(SETTING(MIN_UPLOAD_SPEED));
109     spinBox_REFRESH_TIME->setValue(SETTING(AUTO_REFRESH_TIME));
110     spinBox_HASHING_START_DELAY->setValue(SETTING(HASHING_START_DELAY));
111     checkBox_AUTOREMOVE->setChecked(WBGET(WB_APP_REMOVE_NOT_EX_DIRS));
112     checkBox_SHARE_SKIP_ZERO_BYTE->setChecked(BOOLSETTING(SHARE_SKIP_ZERO_BYTE));
113     checkBox_FASTHASH->setChecked(BOOLSETTING(FAST_HASH));
114     groupBox_FASTHASH->setEnabled(BOOLSETTING(FAST_HASH));
115 
116     listWidget_SKIPLIST->addItems(_q(SETTING(SKIPLIST_SHARE)).split('|', QString::SkipEmptyParts));
117 
118     label_TOTALSHARED->setText(tr("Total shared: %1")
119                                .arg(WulforUtil::formatBytes(ShareManager::getInstance()->getShareSize())));
120 
121     checkBox_SIMPLE_SHARE_MODE->setChecked(WBGET(WB_SIMPLE_SHARE_MODE));
122     treeWidget_SIMPLE_MODE->setVisible(WBGET(WB_SIMPLE_SHARE_MODE));
123     treeWidget_SIMPLE_MODE->setContextMenuPolicy(Qt::CustomContextMenu);
124     treeView->setHidden(WBGET(WB_SIMPLE_SHARE_MODE));
125 
126     checkBox_MAPNORESERVE->setChecked(SETTING(HASH_BUFFER_NORESERVE));
127     checkBox_MAPPOPULATE->setChecked(SETTING(HASH_BUFFER_POPULATE));
128     checkBox_MAPPRIVATE->setChecked(SETTING(HASH_BUFFER_PRIVATE));
129 
130     int ind = comboBox_BUFSIZE->findText(QString().setNum(SETTING(HASH_BUFFER_SIZE_MB)));
131     if (ind >= 0)
132         comboBox_BUFSIZE->setCurrentIndex(ind);
133 
134     connect(toolButton_ADD, SIGNAL(clicked()), this, SLOT(slotAddExeption()));
135     connect(toolButton_EDIT, SIGNAL(clicked()), this, SLOT(slotEditExeption()));
136     connect(toolButton_DELETE, SIGNAL(clicked()), this, SLOT(slotDeleteExeption()));
137     connect(toolButton_BROWSE, SIGNAL(clicked()), this, SLOT(slotAddDirExeption()));
138     connect(listWidget_SKIPLIST, SIGNAL(itemDoubleClicked(QListWidgetItem *)), this, SLOT(slotEditExeption()));
139 
140     connect(toolButton_RECREATE, SIGNAL(clicked()), this, SLOT(slotRecreateShare()));
141     connect(checkBox_SHAREHIDDEN, SIGNAL(clicked(bool)), this, SLOT(slotShareHidden(bool)));
142     connect(treeView->header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotHeaderMenu()));
143 
144     connect(treeWidget_SIMPLE_MODE, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(slotContextMenu(QPoint)));
145     connect(checkBox_SIMPLE_SHARE_MODE, SIGNAL(clicked()), this, SLOT(slotSimpleShareModeChanged()));
146 
147     slotSimpleShareModeChanged();
148 }
149 
updateShareView()150 void SettingsSharing::updateShareView(){
151     if (checkBox_SIMPLE_SHARE_MODE->isChecked()){
152         treeWidget_SIMPLE_MODE->clear();
153 
154         StringPairList directories = ShareManager::getInstance()->getDirectories();
155         for (auto it = directories.begin(); it != directories.end(); ++it){
156             QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget_SIMPLE_MODE);
157 
158             item->setText(0, it->second.c_str());
159             item->setText(1, it->first.c_str());
160             item->setText(2, Util::formatBytes(ShareManager::getInstance()->getShareSize(it->second)).c_str());
161             item->setText(3, QString().setNum(ShareManager::getInstance()->getShareSize(it->second)));
162         }
163     }
164 
165     label_TOTALSHARED->setText(tr("Total shared: %1")
166                                .arg(WulforUtil::formatBytes(ShareManager::getInstance()->getShareSize())));
167 }
168 
slotRecreateShare()169 void SettingsSharing::slotRecreateShare(){
170     ShareManager *SM = ShareManager::getInstance();
171 
172     SM->setDirty();
173     SM->refresh(true);
174 
175     HashProgress progress(this);
176 
177     if (progress.exec() == QDialog::Accepted){
178         updateShareView();
179     }
180 }
181 
slotShareHidden(bool share)182 void SettingsSharing::slotShareHidden(bool share){
183     SettingsManager::getInstance()->set(SettingsManager::SHARE_HIDDEN, share);
184     ShareManager::getInstance()->setDirty();
185     ShareManager::getInstance()->refresh(true);
186 
187     updateShareView();
188 }
189 
slotGetName(QModelIndex index)190 void SettingsSharing::slotGetName(QModelIndex index){
191     bool ok = false;
192     QString dir_alias = QInputDialog::getText(this, tr("Select directory"), tr("Name"),
193                                               QLineEdit::Normal, QDir(model->filePath(index)).dirName(), &ok);
194 
195     dir_alias = dir_alias.trimmed();
196 
197     if (!ok || dir_alias.isEmpty())
198         return;
199 
200     model->setAlias(index, dir_alias);
201 }
202 
slotHeaderMenu()203 void SettingsSharing::slotHeaderMenu(){
204     WulforUtil::headerMenu(treeView);
205 }
206 
slotAddExeption()207 void SettingsSharing::slotAddExeption(){
208     bool ok;
209     QString text = QInputDialog::getText(this, tr("Add item"), tr("Enter text:"), QLineEdit::Normal, "", &ok);
210 
211     if (ok && !text.isEmpty())
212         listWidget_SKIPLIST->addItem(text);
213 }
214 
slotAddDirExeption()215 void SettingsSharing::slotAddDirExeption(){
216     QString dir = QFileDialog::getExistingDirectory(this, tr("Choose the directory"), QDir::home().dirName());
217 
218     if (!dir.isEmpty()){
219         dir = QDir::toNativeSeparators(dir);
220 
221         if (!dir.endsWith(QDir::separator()))
222             dir += QDir::separator();
223 
224         listWidget_SKIPLIST->addItem(dir + "*");
225     }
226 }
227 
slotEditExeption()228 void SettingsSharing::slotEditExeption(){
229     QListWidgetItem *item = listWidget_SKIPLIST->currentItem();
230 
231     if (!item)
232         return;
233 
234     QString old_text = item->text();
235     int row = listWidget_SKIPLIST->row(item);
236     bool ok = false;
237 
238     QString new_text = QInputDialog::getText(this, tr("Add item"), tr("Enter text:"), QLineEdit::Normal, old_text, &ok);
239 
240     if (ok && !new_text.isEmpty()){
241         delete item;
242         listWidget_SKIPLIST->insertItem(row, new_text);
243         listWidget_SKIPLIST->setCurrentRow(row);
244     }
245 }
246 
slotDeleteExeption()247 void SettingsSharing::slotDeleteExeption(){
248     QListWidgetItem *item = listWidget_SKIPLIST->currentItem();
249 
250     if (item)
251         delete item;
252 }
253 
slotSimpleShareModeChanged()254 void SettingsSharing::slotSimpleShareModeChanged(){
255     if (!checkBox_SIMPLE_SHARE_MODE->isChecked()){
256         if (!model){
257             model = new ShareDirModel();
258             treeView->setModel(model);
259 
260             treeView->setSortingEnabled(true);
261             treeView->header()->setContextMenuPolicy(Qt::CustomContextMenu);
262             treeView->header()->hideSection(1);
263             treeView->header()->hideSection(2);
264 
265             if (!WSGET(WS_SHAREHEADER_STATE).isEmpty())
266                 treeView->header()->restoreState(QByteArray::fromBase64(WSGET(WS_SHAREHEADER_STATE).toUtf8()));
267 
268             connect(model, SIGNAL(getName(QModelIndex)), this, SLOT(slotGetName(QModelIndex)));
269             connect(model, SIGNAL(expandMe(QModelIndex)), treeView, SLOT(expand(QModelIndex)));
270 
271             model->beginExpanding();
272         }
273         else{
274             model->beginExpanding();
275         }
276     }
277     else{
278         treeWidget_SIMPLE_MODE->header()->restoreState(QByteArray::fromBase64((WSGET("settings-simple-share-headerstate").toUtf8())));
279 
280         updateShareView();
281     }
282 }
283 
slotContextMenu(const QPoint &)284 void SettingsSharing::slotContextMenu(const QPoint &){
285     QList<QTreeWidgetItem*> selected = treeWidget_SIMPLE_MODE->selectedItems();
286     QMenu *menu = new QMenu();
287     QAction *add_new = NULL, *rem = NULL, *rename = NULL;
288     WulforUtil *WU = WulforUtil::getInstance();
289 
290     add_new = new QAction(WU->getPixmap(WulforUtil::eiEDITADD), tr("Add"), menu);
291     menu->addAction(add_new);
292 
293     if (selected.size() == 1){
294         rename = new QAction(WU->getPixmap(WulforUtil::eiEDIT), tr("Rename"), menu);
295         menu->addAction(rename);
296     }
297 
298     if (!selected.isEmpty()){
299         rem = new QAction(WU->getPixmap(WulforUtil::eiEDITDELETE), tr("Remove"), menu);
300         menu->addAction(rem);
301     }
302 
303     QAction *res = menu->exec(QCursor::pos());
304 
305     delete menu;
306 
307     if (!res)
308         return;
309 
310     if (res == add_new){
311         QString dir = QFileDialog::getExistingDirectory(this, tr("Select directory"), QDir::homePath());
312 
313         if (dir.isEmpty())
314             return;
315 
316         dir = QDir::toNativeSeparators(dir);
317 
318         if (!dir.endsWith(PATH_SEPARATOR))
319             dir += PATH_SEPARATOR_STR;
320 
321         bool ok = false;
322         QString dir_alias = QInputDialog::getText(this, tr("Select directory"), tr("Name"),
323                                                   QLineEdit::Normal, QDir(dir).dirName(), &ok);
324 
325         dir_alias = dir_alias.trimmed();
326 
327         if (!ok || dir_alias.isEmpty())
328             return;
329 
330         try
331         {
332             ShareManager::getInstance()->addDirectory(dir.toStdString(), dir_alias.toStdString());
333         }
334         catch (const ShareException &e)
335         {
336             QMessageBox msg_box(QMessageBox::Critical,
337                                 tr("Error"),
338                                 QString::fromStdString(e.getError()),
339                                 QMessageBox::Ok);
340 
341             msg_box.exec();
342 
343             return;
344         }
345 
346         QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget_SIMPLE_MODE);
347 
348         item->setText(0, dir);
349         item->setText(1, dir_alias);
350         item->setText(2, "");
351         item->setText(3, "");
352     }
353     else if (res == rem){
354         for (const auto &i : selected)
355             ShareManager::getInstance()->removeDirectory(i->text(0).toStdString());
356     }
357     else if (res == rename){
358         QTreeWidgetItem *item = selected.at(0);
359         QString realname = item->text(0);
360         QString virtname = item->text(1);
361         bool ok = false;
362         QString new_virtname = QInputDialog::getText(this, tr("Enter new name"),
363                                                      tr("Name"), QLineEdit::Normal, virtname, &ok);
364 
365         if (!ok || new_virtname.isEmpty() || new_virtname == virtname)
366             return;
367 
368         try {
369             ShareManager::getInstance()->renameDirectory(realname.toStdString(), new_virtname.toStdString());
370         }
371         catch (const ShareException &e){
372             QMessageBox msg_box(QMessageBox::Critical,
373                                 tr("Error"),
374                                 QString::fromStdString(e.getError()),
375                                 QMessageBox::Ok);
376 
377             msg_box.exec();
378 
379             return;
380         }
381     }
382 
383     updateShareView();
384 }
385 
filePath(const QModelIndex & index) const386 QString ShareDirModel::filePath( const QModelIndex & index ) const {
387     return QDir::toNativeSeparators( QDirModel::filePath(index) );
388 }
389 
ShareDirModel(QObject * parent)390 ShareDirModel::ShareDirModel(QObject *parent){
391     QDirModel::setParent(parent);
392     QDirModel::setFilter((QDir::AllDirs | QDir::NoDotAndDotDot));
393 
394     StringPairList directories = ShareManager::getInstance()->getDirectories();
395     for (auto it = directories.begin(); it != directories.end(); ++it){
396         QString path = it->second.c_str();
397 
398         if (path.endsWith(QDir::separator()))
399             path = path.left(path.lastIndexOf(QDir::separator()));
400 
401         emit expandMe(index(path));
402 
403         checked.insert(path);
404     }
405 }
406 
~ShareDirModel()407 ShareDirModel::~ShareDirModel(){
408 
409 }
410 
flags(const QModelIndex & index) const411 Qt::ItemFlags ShareDirModel::flags(const QModelIndex& index) const{
412     Qt::ItemFlags f = QDirModel::flags(index);
413 
414     if (!index.column())
415         f |= Qt::ItemIsUserCheckable;
416 
417     QString fp = filePath(index);
418 
419     for (const QString &file : checked){
420         if (fp.startsWith(file) && (fp.split(QDir::separator()).length() != file.split(QDir::separator()).length()) && fp != file){
421             f &= ~(Qt::ItemIsSelectable | Qt::ItemIsEnabled);
422 
423             break;
424         }
425     }
426 
427     return f;
428 }
429 
data(const QModelIndex & index,int role=Qt::DisplayRole) const430 QVariant ShareDirModel::data(const QModelIndex& index, int role = Qt::DisplayRole) const{
431     if (!index.isValid())
432         return QVariant();
433 
434     QString fp = filePath(index);
435 
436     switch (role){
437         case Qt::CheckStateRole:
438         {
439             if (!index.column()){
440                 for (const QString &f : checked){
441                     if (fp.startsWith(f) && fp.length() == f.length())
442                         return Qt::Checked;
443                 }
444 
445                 return (checked.contains(fp)? Qt::Checked : Qt::Unchecked);
446             }
447 
448             break;
449         }
450         case Qt::ForegroundRole:
451         {
452             /*for (const auto &f : checked){
453                 if (f.startsWith(fp))
454                     return QColor(0x1F, 0x8F, 0x1F);
455             }*/
456 
457             break;
458         }
459         case Qt::FontRole:
460         {
461             if (!index.column()){
462                 static QFont font;
463                 font.setBold(true);
464 
465                 for (const QString &f : checked){
466                     if (f == fp)
467                         return font;
468                 }
469 
470                 if (checked.contains(fp))
471                     return font;
472             }
473 
474             break;
475         }
476     }
477 
478     return QDirModel::data(index, role);
479 }
480 
setData(const QModelIndex & index,const QVariant & value,int role=Qt::EditRole)481 bool ShareDirModel::setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole)
482 {
483     if (index.isValid() && !index.column() && role == Qt::CheckStateRole){
484 
485         if (value.toInt() == Qt::Checked)
486             emit getName(index);//checked.insert(filePath(index));
487         else{
488             try {
489                 QString path = filePath(index);
490 
491                 if (!path.endsWith(QDir::separator()))
492                     path += QDir::separator();
493 
494                 ShareManager::getInstance()->removeDirectory(path.toStdString());
495             }
496             catch (const Exception&){}
497 
498             checked.remove(filePath(index));
499         }
500 
501         return true;
502     }
503 
504     return QDirModel::setData(index, value, role);
505 }
506 
setAlias(const QModelIndex & index,const QString & alias)507 void ShareDirModel::setAlias(const QModelIndex &index, const QString &alias){
508     QString fp = filePath(index);
509 
510     if (checked.contains(fp) || !QDir(fp).exists())
511         return;
512 
513     checked.insert(fp);
514 
515     try
516     {
517         if (!fp.endsWith(QDir::separator()))
518             fp += QDir::separator();
519 
520         ShareManager::getInstance()->addDirectory(fp.toStdString(), alias.toStdString());
521     }
522     catch (const ShareException &e)
523     {
524         QMessageBox msg_box(QMessageBox::Critical,
525                             tr("Error"),
526                             QString::fromStdString(e.getError()),
527                             QMessageBox::Ok);
528 
529         msg_box.exec();
530 
531         return;
532     }
533 
534     QDirModel::setData(index, true, Qt::CheckStateRole);
535 
536     emit layoutChanged();
537 }
538 
beginExpanding()539 void ShareDirModel::beginExpanding(){
540     for (const QString &f : checked){
541         QStack<QModelIndex> stack;
542         QModelIndex i = index(f);
543 
544         while (i.isValid()){
545             stack.push(i);
546 
547             i = i.parent();
548         }
549 
550         while (!stack.isEmpty())
551             emit expandMe(stack.pop());
552     }
553 }
554