1 /************************************************************************
2  **
3  **  Copyright (C) 2020-2021 Kevin B. Hendricks, Stratford Ontario Canada
4  **
5  **  This file is part of Sigil.
6  **
7  **  Sigil is free software: you can redistribute it and/or modify
8  **  it under the terms of the GNU General Public License as published by
9  **  the Free Software Foundation, either version 3 of the License, or
10  **  (at your option) any later version.
11  **
12  **  Sigil is distributed in the hope that it will be useful,
13  **  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  **  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  **  GNU General Public License for more details.
16  **
17  **  You should have received a copy of the GNU General Public License
18  **  along with Sigil.  If not, see <http://www.gnu.org/licenses/>.
19  **
20  *************************************************************************/
21 #include <QString>
22 #include <QList>
23 #include <QDir>
24 #include <QFile>
25 #include <QFileInfo>
26 #include <QTableWidgetItem>
27 #include <QMessageBox>
28 #include <QApplication>
29 #include <QtConcurrent>
30 #include <QFuture>
31 
32 #include <QDebug>
33 
34 #include "Misc/Utility.h"
35 #include "Misc/SettingsStore.h"
36 #include "EmbedPython/PythonRoutines.h"
37 #include "Dialogs/RepoLog.h"
38 #include "Dialogs/ManageRepos.h"
39 
40 static const QString SETTINGS_GROUP = "manage_repos";
41 
ManageRepos(QWidget * parent)42 ManageRepos::ManageRepos(QWidget *parent)
43     : QDialog(parent),
44       m_repoList(QStringList())
45 {
46     ui.setupUi(this);
47     ReadSettings();
48     InitializeTable();
49     ConnectSignalsToSlots();
50 }
51 
~ManageRepos()52 ManageRepos::~ManageRepos()
53 {
54     WriteSettings();
55 }
56 
SetRepoList()57 void ManageRepos::SetRepoList()
58 {
59     QDir  d(Utility::DefinePrefsDir() + "/repo");
60     if (!d.exists()) {
61         return;
62     }
63     // get the directory names in this directory that start with "epub_"
64     m_repoList = d.entryList(QStringList("epub_*"), QDir::Dirs|QDir::NoDotAndDotDot);
65 }
66 
GetBookInfo(const QString & reponame)67 QStringList ManageRepos::GetBookInfo(const QString& reponame)
68 {
69     QStringList bookinfo;
70     QString infopath = Utility::DefinePrefsDir() + "/repo/" + reponame + "/.bookinfo";
71     QFileInfo fi(infopath);
72     if (fi.exists() && fi.isFile() && fi.isReadable()) {
73         QString data = Utility::ReadUnicodeTextFile(infopath);
74         bookinfo = data.split("\n");
75     }
76     return bookinfo;
77 }
78 
InitializeTable()79 void ManageRepos::InitializeTable()
80 {
81     m_repoList.clear();
82     SetRepoList();
83     int nrows = 0;
84     // clear out the table but do NOT clear out column headings
85     while (ui.repoTable->rowCount() > 0) {
86         ui.repoTable->removeRow(0);
87     }
88     foreach(QString rp, m_repoList) {
89         QStringList fields = GetBookInfo(rp);
90         if (!fields.isEmpty() && (fields.count() >= 5)) {
91             ui.repoTable->insertRow(nrows);
92             SetRepoTableRow(fields,nrows);
93             nrows++;
94         }
95     }
96     ui.repoTable->resizeColumnsToContents();
97     ui.repoTable->setSortingEnabled(true);
98 }
99 
WriteSettings()100 void ManageRepos::WriteSettings()
101 {
102     SettingsStore settings;
103     settings.beginGroup(SETTINGS_GROUP);
104     settings.setValue("geometry", saveGeometry());
105     settings.endGroup();
106 }
107 
SetRepoTableRow(const QStringList & fields,int row)108 void ManageRepos::SetRepoTableRow(const QStringList &fields, int row)
109 {
110     bool sortingOn = ui.repoTable->isSortingEnabled();
111     ui.repoTable->setSortingEnabled(false);
112     ui.repoTable->setItem(row,ManageRepos::FileField, new QTableWidgetItem(fields.at(ManageRepos::FileField)));
113     ui.repoTable->setItem(row,ManageRepos::TitleField, new QTableWidgetItem(fields.at(ManageRepos::TitleField)));
114     ui.repoTable->setItem(row,ManageRepos::ModifiedField, new QTableWidgetItem(fields.at(ManageRepos::ModifiedField)));
115     ui.repoTable->setItem(row,ManageRepos::VersionField, new QTableWidgetItem(fields.at(ManageRepos::VersionField)));
116     ui.repoTable->setItem(row,ManageRepos::UUIDField, new QTableWidgetItem(fields.at(ManageRepos::UUIDField)));
117     ui.repoTable->setSortingEnabled(sortingOn);
118 }
119 
ReadSettings()120 void ManageRepos::ReadSettings()
121 {
122     SettingsStore settings;
123     settings.beginGroup(SETTINGS_GROUP);
124     QByteArray geometry = settings.value("geometry").toByteArray();
125     if (!geometry.isNull()) {
126         restoreGeometry(geometry);
127     }
128     settings.endGroup();
129 }
130 
RepoSelected(int row,int col)131 void ManageRepos::RepoSelected(int row, int col)
132 {
133     ui.repoTable->setCurrentCell(row, col);
134 }
135 
ShowLog()136 void ManageRepos::ShowLog()
137 {
138     // limited to work with one selection at a time to prevent row mixup upon removal
139     QList<QTableWidgetItem *> itemlist = ui.repoTable->selectedItems();
140     if (itemlist.isEmpty()) {
141         Utility::DisplayStdWarningDialog(tr("Nothing is Selected."));
142         return;
143     }
144     int row = ui.repoTable->row(itemlist.at(0));
145     QString bookid = ui.repoTable->item(row, ManageRepos::UUIDField)->text();
146     QString localRepo = Utility::DefinePrefsDir() + "/repo/";
147 
148     QApplication::setOverrideCursor(Qt::WaitCursor);
149 
150     // generate the repo log using python in a separate thread since this
151     // may take a while depending on the speed of the filesystem
152     PythonRoutines pr;
153     QFuture<QString> future = QtConcurrent::run(&pr,
154                                                 &PythonRoutines::GenerateRepoLogSummaryInPython,
155                                                 localRepo,
156                                                 bookid);
157     future.waitForFinished();
158     QString logData = future.result();
159 
160     QApplication::restoreOverrideCursor();
161 
162     RepoLog log(tr("Repository Log"), logData, this);
163     log.exec();
164 }
165 
166 
RemoveRepo()167 void ManageRepos::RemoveRepo()
168 {
169     // limited to work with one selection at a time to prevent row mixup upon removal
170     QList<QTableWidgetItem *> itemlist = ui.repoTable->selectedItems();
171     if (itemlist.isEmpty()) {
172         Utility::DisplayStdWarningDialog(tr("Nothing is Selected."));
173         return;
174     }
175     ui.repoTable->setSortingEnabled(false);
176     int row = ui.repoTable->row(itemlist.at(0));
177     QString bookid = ui.repoTable->item(row, ManageRepos::UUIDField)->text();
178     QString reponame = "epub_" + bookid;
179     QString repopath = Utility::DefinePrefsDir() + "/repo/" + reponame;
180     bool success = Utility::removeDir(repopath);
181     if (success) {
182         m_repoList.removeOne(reponame);
183         ui.repoTable->removeRow(row);
184     } else {
185         qDebug() << "Error removing Repo: " << repopath;
186     }
187     ui.repoTable->resizeColumnsToContents();
188     ui.repoTable->setSortingEnabled(true);
189 }
190 
RemoveAllRepos()191 void ManageRepos::RemoveAllRepos()
192 {
193     QMessageBox msgBox;
194 
195     msgBox.setIcon(QMessageBox::Warning);
196     msgBox.setWindowFlags(Qt::Window | Qt::WindowStaysOnTopHint);
197     msgBox.setWindowTitle(tr("Remove All Repositories"));
198     msgBox.setText(tr("Are you sure sure you want to remove all checkpoint repositories?"));
199     QPushButton *yesButton = msgBox.addButton(QMessageBox::Yes);
200     QPushButton *noButton  = msgBox.addButton(QMessageBox::No);
201     msgBox.setDefaultButton(noButton);
202     msgBox.exec();
203     if (msgBox.clickedButton() == yesButton) {
204         foreach(QString reponame, m_repoList) {
205             QString repopath = Utility::DefinePrefsDir() + "/repo/" + reponame;
206             Utility::removeDir(repopath);
207         }
208         InitializeTable();
209     }
210 }
211 
ConnectSignalsToSlots()212 void ManageRepos::ConnectSignalsToSlots()
213 {
214     connect(ui.logButton,       SIGNAL(clicked()),                  this, SLOT(ShowLog()));
215     connect(ui.removeButton,    SIGNAL(clicked()),                  this, SLOT(RemoveRepo()));
216     connect(ui.removeAllButton, SIGNAL(clicked()),                  this, SLOT(RemoveAllRepos()));
217     connect(ui.repoTable,       SIGNAL(cellDoubleClicked(int,int)), this, SLOT(RepoSelected(int,int)));
218 }
219