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 and included
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_winRegExpDrive.setPattern("^[a-z]:/$");
56 m_winRegExpPath.setPattern("^[a-z]:/");
57 m_slashregex.setPattern("/");
62 restoreTree::~restoreTree()
68 * Called from the constructor to set up the page widgets and connections.
70 void restoreTree::setupPage()
72 connect(refreshButton, SIGNAL(pressed()), this, SLOT(refreshButtonPushed()));
73 connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobComboChanged(int)));
74 connect(directoryTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
75 this, SLOT(directoryItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
76 connect(fileTable, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
77 this, SLOT(fileItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
78 connect(directoryTree, SIGNAL(itemExpanded(QTreeWidgetItem *)),
79 this, SLOT(directoryItemExpanded(QTreeWidgetItem *)));
82 titles << "Directories";
83 directoryTree->setHeaderLabels(titles);
84 clientCombo->addItems(m_console->client_list);
85 fileSetCombo->addItem("Any");
86 fileSetCombo->addItems(m_console->fileset_list);
87 jobCombo->addItems(m_console->job_list);
91 * When refresh button is pushed, perform a query getting the directories and
92 * use parseDirectory and addDirectory to populate the directory tree with items.
94 void restoreTree::populateDirectoryTree()
98 directoryTree->clear();
100 fileTable->setRowCount(0);
101 fileTable->setColumnCount(0);
102 versionTable->clear();
103 versionTable->setRowCount(0);
104 versionTable->setColumnCount(0);
106 jobTable->setRowCount(0);
107 jobTable->setColumnCount(0);
109 m_condition = " Job.name = '" + jobCombo->itemText(jobCombo->currentIndex()) + "'";
110 int clientIndex = clientCombo->currentIndex();
111 if ((clientIndex >= 0) && (clientCombo->itemText(clientIndex) != "Any")) {
112 m_condition.append(" AND Client.Name='" + clientCombo->itemText(clientIndex) + "'");
114 int fileSetIndex = fileSetCombo->currentIndex();
115 if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
116 m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
119 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
120 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)"
121 " WHERE" + m_condition +
122 " AND Job.purgedfiles=0";
125 " From Job" + m_jobQueryPart;
126 if (mainWin->m_sqlDebug) {
127 Pmsg1(000, "Query cmd : %s\n",m_jobQuery.toUtf8().data());
132 "SELECT DISTINCT Path.Path"
134 " LEFT OUTER JOIN File ON (File.PathId=Path.PathId)"
135 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
136 " WHERE Job.Jobid IN (" + m_jobQuery + ")";
137 if (mainWin->m_sqlDebug) {
138 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
140 QStringList directories;
141 if (m_console->sql_cmd(cmd, directories)) {
142 if (mainWin->m_miscDebug) {
143 Pmsg1(000, "Done with query %i directories\n", directories.count());
145 foreach(QString directory, directories) {
147 parseDirectory(directory);
153 * Function to parse a directory into all possible subdirectories, then add to
156 void restoreTree::parseDirectory(QString &dir_in)
160 /* Clean up the directory string remove some funny char after last '/' */
161 QRegExp rgx("[^/]$");
162 int lastslash = rgx.indexIn(dir_in);
163 dir_in.replace(lastslash, dir_in.length()-lastslash, "");
164 if ((mainWin->m_miscDebug) && (m_debugTrap))
165 Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
167 /* split and add if not in yet */
168 QString direct, path;
171 QStringList pathAfter, dirAfter;
172 /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/
173 * if not added into tree, then try /etc/ and somedir/ if not added, then try
174 * / and etc/ . That should succeed, then add the ones that failed in reverse */
175 while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) {
176 direct = path = dir_in;
177 path.replace(index+1,dir_in.length()-index-1,"");
178 direct.replace(0,index+1,"");
179 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
180 QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
181 .arg(dir_in.length())
185 Pmsg0(000, msg.toUtf8().data());
187 if (addDirectory(path, direct)) done = true;
189 if ((mainWin->m_miscDebug) && (m_debugTrap))
190 Pmsg0(000, "Saving for later\n");
191 pathAfter.prepend(path);
192 dirAfter.prepend(direct);
196 for (int k=0; k<pathAfter.count(); k++) {
197 if (addDirectory(pathAfter[k], dirAfter[k]))
198 if ((mainWin->m_miscDebug) && (m_debugTrap))
199 Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
201 if ((mainWin->m_miscDebug) && (m_debugTrap))
202 Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
207 * Function called from fill directory when a directory is found to see if this
208 * directory exists in the directory pane and then add it to the directory pane
210 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
212 QString newdir = newdirr;
213 QString fullpath = m_cwd + newdirr;
214 bool ok = true, added = false;
216 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
217 QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
220 Pmsg0(000, msg.toUtf8().data());
224 /* add unix '/' directory first */
225 if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullpath,0) == -1)) {
227 QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
228 item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
230 item->setText(0, text.toUtf8().data());
231 item->setData(0, Qt::UserRole, QVariant(text));
232 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
233 Pmsg1(000, "Pre Inserting %s\n",text.toUtf8().data());
235 m_dirPaths.insert(text, item);
237 /* no need to check for windows drive if unix */
238 if (m_winRegExpDrive.indexIn(m_cwd, 0) == 0) {
239 /* this is a windows drive add the base widget */
240 QTreeWidgetItem *item = item = new QTreeWidgetItem(directoryTree);
241 item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
242 item->setText(0, m_cwd);
243 item->setData(0, Qt::UserRole, QVariant(fullpath));
244 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
245 Pmsg0(000, "Added Base \"letter\":/\n");
247 m_dirPaths.insert(m_cwd, item);
251 /* is it already existent ?? */
252 if (!m_dirPaths.contains(fullpath)) {
253 QTreeWidgetItem *item = NULL;
254 QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
256 /* new directories to add */
257 item = new QTreeWidgetItem(parent);
258 item->setText(0, newdir.toUtf8().data());
259 item->setData(0, Qt::UserRole, QVariant(fullpath));
262 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
263 QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
266 Pmsg0(000, msg.toUtf8().data());
269 /* insert into hash */
271 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
272 Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
274 m_dirPaths.insert(fullpath, item);
282 * Virtual function which is called when this page is visible on the stack
284 void restoreTree::currentStackItem()
287 if (!m_console->preventInUseConnect())
295 * Populate the tree when refresh button pushed.
297 void restoreTree::refreshButtonPushed()
299 populateDirectoryTree();
303 * Set the values of non-job combo boxes to the job defaults
305 void restoreTree::jobComboChanged(int)
307 job_defaults job_defs;
310 job_defs.job_name = jobCombo->currentText();
311 if (m_console->get_job_defaults(job_defs)) {
312 fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
313 clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
318 * Function to populate the file list table
320 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
324 QBrush blackBrush(Qt::black);
325 QString directory = item->data(0,Qt::UserRole).toString();
326 directoryLabel->setText("Present Working Directory : " + directory);
328 "SELECT DISTINCT Filename.Name"
330 " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
331 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
332 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
333 " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
334 " AND Job.Jobid IN (" + m_jobQuery + ")";
337 QStringList headerlist = (QStringList() << "File Name");
339 /* Also clear the version table here */
340 versionTable->clear();
341 versionTable->setRowCount(0);
342 versionTable->setColumnCount(0);
343 fileTable->setColumnCount(headerlist.size());
344 fileTable->setHorizontalHeaderLabels(headerlist);
346 if (mainWin->m_sqlDebug) {
347 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
350 if (m_console->sql_cmd(cmd, results)) {
352 QTableWidgetItem* tableItem;
354 QStringList fieldlist;
355 fileTable->setRowCount(results.size());
358 /* Iterate through the record returned from the query */
359 foreach (QString resultline, results) {
360 /* Iterate through fields in the record */
362 fieldlist = resultline.split("\t");
363 foreach (field, fieldlist) {
364 field = field.trimmed(); /* strip leading & trailing spaces */
365 tableItem = new QTableWidgetItem(field,1);
366 tableItem->setFlags(0);
367 tableItem->setForeground(blackBrush);
368 tableItem->setData(Qt::UserRole,QVariant(directory));
369 fileTable->setItem(row, column, tableItem);
374 fileTable->setRowCount(row);
376 fileTable->resizeColumnsToContents();
377 fileTable->resizeRowsToContents();
378 fileTable->verticalHeader()->hide();
382 * Function to populate the version table
384 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
386 if (fileTableItem == NULL)
388 QString file = fileTableItem->text();
389 QString directory = fileTableItem->data(Qt::UserRole).toString();
391 QBrush blackBrush(Qt::black);
393 "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
395 " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
396 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
397 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
398 " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "'"
399 " AND Job.Jobid IN (" + m_jobQuery + ")";
401 QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
402 versionTable->clear();
403 versionTable->setColumnCount(headerlist.size());
404 versionTable->setHorizontalHeaderLabels(headerlist);
406 if (mainWin->m_sqlDebug) {
407 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
410 if (m_console->sql_cmd(cmd, results)) {
412 QTableWidgetItem* tableItem;
414 QStringList fieldlist;
415 versionTable->setRowCount(results.size());
418 /* Iterate through the record returned from the query */
419 foreach (QString resultline, results) {
420 fieldlist = resultline.split("\t");
422 /* remove directory */
423 if (fieldlist[0].trimmed() != "") {
424 /* Iterate through fields in the record */
425 foreach (field, fieldlist) {
426 field = field.trimmed(); /* strip leading & trailing spaces */
427 tableItem = new QTableWidgetItem(field,1);
428 tableItem->setFlags(0);
429 tableItem->setForeground(blackBrush);
430 tableItem->setData(Qt::UserRole,QVariant(directory));
431 versionTable->setItem(row, column, tableItem);
438 versionTable->resizeColumnsToContents();
439 versionTable->resizeRowsToContents();
440 versionTable->verticalHeader()->hide();
444 * Save user settings associated with this page
446 void restoreTree::writeSettings()
448 QSettings settings(m_console->m_dir->name(), "bat");
449 settings.beginGroup("RestoreTree");
450 settings.setValue("splitterSizes", splitter->saveState());
455 * Read and restore user settings associated with this page
457 void restoreTree::readSettings()
459 QSettings settings(m_console->m_dir->name(), "bat");
460 settings.beginGroup("RestoreTree");
461 splitter->restoreState(settings.value("splitterSizes").toByteArray());
466 * This is a funcion to accomplish the one thing I struggled to figure out what
467 * was taking so long. It add the icons, but after the tree is made.
469 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
471 int childCount = item->childCount();
472 for (int i=0; i<childCount; i++) {
473 QTreeWidgetItem *child = item->child(i);
474 child->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
478 void restoreTree::populateJobTable()
480 QBrush blackBrush(Qt::black);
481 QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Type");
483 jobTable->setColumnCount(headerlist.size());
484 jobTable->setHorizontalHeaderLabels(headerlist);
486 "SELECT Job.Jobid AS Id, Job.Endtime AS EndTime, Job.Level AS Level"
487 " FROM Job" + m_jobQueryPart +
488 " ORDER BY Job.Endtime DESC";
489 if (mainWin->m_sqlDebug) {
490 Pmsg1(000, "Query cmd : %s\n",jobQuery.toUtf8().data());
494 if (m_console->sql_cmd(jobQuery, results)) {
496 QTableWidgetItem* tableItem;
498 QStringList fieldlist;
499 jobTable->setRowCount(results.size());
502 /* Iterate through the record returned from the query */
503 foreach (QString resultline, results) {
504 fieldlist = resultline.split("\t");
506 /* remove directory */
507 if (fieldlist[0].trimmed() != "") {
508 /* Iterate through fields in the record */
509 foreach (field, fieldlist) {
510 field = field.trimmed(); /* strip leading & trailing spaces */
511 tableItem = new QTableWidgetItem(field,1);
512 tableItem->setFlags(0);
513 tableItem->setForeground(blackBrush);
514 jobTable->setItem(row, column, tableItem);
521 jobTable->resizeColumnsToContents();
522 jobTable->resizeRowsToContents();
523 jobTable->verticalHeader()->hide();