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