2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
6 The main author of Bacula is Kern Sibbald, with contributions from
7 many others, a complete list can be found in the file AUTHORS.
8 This program is Free Software; you can redistribute it and/or
9 modify it under the terms of version two of the GNU General Public
10 License as published by the Free Software Foundation plus additions
11 that are listed in the file LICENSE.
13 This program is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 Bacula® is a registered trademark of John Walker.
24 The licensor of Bacula is the Free Software Foundation Europe
25 (FSFE), Fiduciary Program, Sumatrastrasse 25, 8006 Zürich,
26 Switzerland, email:ftf@fsfeurope.org.
30 * Version $Id: restore.cpp 4945 2007-05-31 01:24:28Z bartleyd2 $
34 * Kern Sibbald, February MMVII
39 #include "restoretree.h"
42 restoreTree::restoreTree()
45 m_name = "Version Browser";
47 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/browse.png")));
55 m_winregex.setPattern("^/[a-z]:/$");
56 m_slashregex.setPattern("/");
59 restoreTree::~restoreTree()
65 * Called from the constructor to set up the page widgets and connections.
67 void restoreTree::setupPage()
69 connect(refreshButton, SIGNAL(pressed()), this, SLOT(refreshButtonPushed()));
70 connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobComboChanged(int)));
71 connect(directoryTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
72 this, SLOT(directoryItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
73 connect(fileTable, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
74 this, SLOT(fileItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
75 connect(directoryTree, SIGNAL(itemExpanded(QTreeWidgetItem *)),
76 this, SLOT(directoryItemExpanded(QTreeWidgetItem *)));
79 titles << "Directories";
80 directoryTree->setHeaderLabels(titles);
81 clientCombo->addItems(m_console->client_list);
82 fileSetCombo->addItem("Any");
83 fileSetCombo->addItems(m_console->fileset_list);
84 jobCombo->addItems(m_console->job_list);
88 * When refresh button is pushed, perform a query getting the directories and
89 * use parseDirectory and addDirectory to populate the directory tree with items.
91 void restoreTree::populateDirectoryTree()
97 directoryTree->clear();
99 versionTable->clear();
101 "SELECT DISTINCT Path.Path FROM Path"
102 " LEFT OUTER JOIN File ON (File.PathId=Path.PathId)"
103 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
104 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
105 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId) WHERE";
106 m_condition = " Job.name = '" + jobCombo->itemText(jobCombo->currentIndex()) + "'";
107 int clientIndex = clientCombo->currentIndex();
108 if ((clientIndex >= 0) && (clientCombo->itemText(clientIndex) != "Any")) {
109 m_condition.append(" AND Client.Name='" + clientCombo->itemText(clientIndex) + "'");
111 int fileSetIndex = fileSetCombo->currentIndex();
112 if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
113 m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
116 if (mainWin->m_sqlDebug) {
117 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
119 QStringList directories;
120 if (m_console->sql_cmd(cmd, directories)) {
121 if (mainWin->m_miscDebug) {
122 Pmsg1(000, "Done with query %i directories\n", directories.count());
124 foreach(QString directory, directories) {
125 parseDirectory(directory);
131 * Function to parse a directory into all possible subdirectories, then add to
134 void restoreTree::parseDirectory(QString &dir_in)
136 /* Clean up the directory string remove some funny char after last '/' */
137 QRegExp rgx("[^/]$");
138 int lastslash = rgx.indexIn(dir_in);
139 dir_in.replace(lastslash, dir_in.length()-lastslash, "");
140 if (mainWin->m_miscDebug) Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
142 /* split and add if not in yet */
143 QString direct, path;
146 QStringList pathAfter, dirAfter;
147 /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/
148 * if not added into tree, then try /etc/ and somedir/ if not added, then try
149 * / and etc/ . That should succeed, then add the ones that failed in reverse */
150 while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) {
151 direct = path = dir_in;
152 path.replace(index+1,dir_in.length()-index-1,"");
153 direct.replace(0,index+1,"");
154 /* if (mainWin->m_miscDebug)
155 printf("length = %i index = %i Adding %s %s\n",
156 dir_in.length(), index,
157 path.toUtf8().data(), direct.toUtf8().data()); */
158 if (addDirectory(path, direct)) done = true;
160 if (mainWin->m_miscDebug) Pmsg0(000, "Saving for later\n");
161 pathAfter.prepend(path);
162 dirAfter.prepend(direct);
166 for (int k=0; k<pathAfter.count(); k++) {
167 if (addDirectory(pathAfter[k], dirAfter[k]))
168 if (mainWin->m_miscDebug)
169 Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
171 if (mainWin->m_miscDebug)
172 Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
177 * Function called from fill directory when a directory is found to see if this
178 * directory exists in the directory pane and then add it to the directory pane
180 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
182 QString newdir = newdirr;
183 QString fullpath = m_cwd + newdirr;
184 bool ok = true, added = false;
185 bool windrive = false;
187 if (mainWin->m_miscDebug) {
188 QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
191 Pmsg0(000, msg.toUtf8().data());
195 /* add unix '/' directory first */
196 if (m_dirPaths.empty() && (m_winregex.indexIn(fullpath,0) == -1)) {
198 QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
199 item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
202 item->setText(0, text.toUtf8().data());
203 item->setData(0, Qt::UserRole, QVariant(text));
204 if (mainWin->m_miscDebug) {
205 Pmsg1(000, "Pre Inserting %s\n",text.toUtf8().data());
207 m_dirPaths.insert(text, item);
213 if (m_winregex.indexIn(fullpath,0) == 0) {
215 /* this is a windows drive */
216 if (mainWin->m_miscDebug) {
217 Pmsg0(000, "Need to do windows \"letter\":/\n");
222 fullpath.replace(0,1,"");
226 /* is it already existent ?? */
227 if (!m_dirPaths.contains(fullpath)) {
228 QTreeWidgetItem *item = NULL;
230 /* this is the base widget */
231 item = new QTreeWidgetItem(directoryTree);
232 item->setText(0, fullpath.toUtf8().data());
233 item->setData(0, Qt::UserRole, QVariant(fullpath));
235 QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
237 /* new directories to add */
238 item = new QTreeWidgetItem(parent);
239 item->setText(0, newdir.toUtf8().data());
240 item->setData(0, Qt::UserRole, QVariant(fullpath));
243 if (mainWin->m_miscDebug) {
244 QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
247 Pmsg0(000, msg.toUtf8().data());
251 /* insert into hash */
253 if (mainWin->m_miscDebug) {
254 Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
256 m_dirPaths.insert(fullpath, item);
264 * Virtual function which is called when this page is visible on the stack
266 void restoreTree::currentStackItem()
269 if (!m_console->preventInUseConnect())
277 * Populate the tree when refresh button pushed.
279 void restoreTree::refreshButtonPushed()
281 populateDirectoryTree();
285 * Set the values of non-job combo boxes to the job defaults
287 void restoreTree::jobComboChanged(int)
289 job_defaults job_defs;
292 job_defs.job_name = jobCombo->currentText();
293 if (m_console->get_job_defaults(job_defs)) {
294 fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
295 clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
300 * Function to populate the file list table
302 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
306 QBrush blackBrush(Qt::black);
307 QString directory = item->data(0,Qt::UserRole).toString();
308 directoryLabel->setText("Present Working Directory : " + directory);
310 "SELECT DISTINCT Filename.Name"
311 " FROM File LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
312 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
313 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
314 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
315 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)";
316 cmd += " WHERE Path.Path='" + directory + "' AND Filename.Name!='' AND " + m_condition;
318 QStringList headerlist = (QStringList() << "File Name");
320 /* Also clear the version table here */
321 versionTable->clear();
322 versionTable->setRowCount(0);
323 versionTable->setColumnCount(0);
324 fileTable->setColumnCount(headerlist.size());
325 fileTable->setHorizontalHeaderLabels(headerlist);
327 if (mainWin->m_sqlDebug) {
328 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
331 if (m_console->sql_cmd(cmd, results)) {
332 m_resultCount = results.count();
334 QTableWidgetItem* tableItem;
336 QStringList fieldlist;
337 fileTable->setRowCount(results.size());
340 /* Iterate through the record returned from the query */
341 foreach (QString resultline, results) {
342 /* Iterate through fields in the record */
344 fieldlist = resultline.split("\t");
345 foreach (field, fieldlist) {
346 field = field.trimmed(); /* strip leading & trailing spaces */
347 tableItem = new QTableWidgetItem(field,1);
348 tableItem->setFlags(0);
349 tableItem->setForeground(blackBrush);
350 tableItem->setData(Qt::UserRole,QVariant(directory));
351 fileTable->setItem(row, column, tableItem);
356 fileTable->setRowCount(row);
358 fileTable->resizeColumnsToContents();
359 fileTable->resizeRowsToContents();
360 fileTable->verticalHeader()->hide();
364 * Function to populate the version table
366 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
368 if (fileTableItem == NULL)
370 QString file = fileTableItem->text();
371 QString directory = fileTableItem->data(Qt::UserRole).toString();
373 QBrush blackBrush(Qt::black);
375 "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
377 " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
378 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
379 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
380 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
381 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)";
382 cmd += " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "' AND " + m_condition;
384 QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
385 versionTable->clear();
386 versionTable->setColumnCount(headerlist.size());
387 versionTable->setHorizontalHeaderLabels(headerlist);
389 if (mainWin->m_sqlDebug) {
390 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
393 if (m_console->sql_cmd(cmd, results)) {
394 m_resultCount = results.count();
396 QTableWidgetItem* tableItem;
398 QStringList fieldlist;
399 versionTable->setRowCount(results.size());
402 /* Iterate through the record returned from the query */
403 foreach (QString resultline, results) {
404 fieldlist = resultline.split("\t");
406 /* remove directory */
407 if (fieldlist[0].trimmed() != "") {
408 /* Iterate through fields in the record */
409 foreach (field, fieldlist) {
410 field = field.trimmed(); /* strip leading & trailing spaces */
411 tableItem = new QTableWidgetItem(field,1);
412 tableItem->setFlags(0);
413 tableItem->setForeground(blackBrush);
414 tableItem->setData(Qt::UserRole,QVariant(directory));
415 versionTable->setItem(row, column, tableItem);
422 versionTable->resizeColumnsToContents();
423 versionTable->resizeRowsToContents();
424 versionTable->verticalHeader()->hide();
428 * Save user settings associated with this page
430 void restoreTree::writeSettings()
432 QSettings settings(m_console->m_dir->name(), "bat");
433 settings.beginGroup("RestoreTree");
434 settings.setValue("splitterSizes", splitter->saveState());
439 * Read and restore user settings associated with this page
441 void restoreTree::readSettings()
443 QSettings settings(m_console->m_dir->name(), "bat");
444 settings.beginGroup("RestoreTree");
445 splitter->restoreState(settings.value("splitterSizes").toByteArray());
450 * This is a funcion to accomplish the one thing I struggled to figure out what
451 * was taking so long. It add the icons, but after the tree is made.
453 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
455 int childCount = item->childCount();
456 for (int i=0; i<childCount; i++) {
457 QTreeWidgetItem *child = item->child(i);
458 child->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));