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 versionTable->clear();
102 "SELECT DISTINCT Path.Path FROM Path"
103 " LEFT OUTER JOIN File ON (File.PathId=Path.PathId)"
104 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
105 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
106 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId) WHERE";
107 m_condition = " Job.name = '" + jobCombo->itemText(jobCombo->currentIndex()) + "'";
108 int clientIndex = clientCombo->currentIndex();
109 if ((clientIndex >= 0) && (clientCombo->itemText(clientIndex) != "Any")) {
110 m_condition.append(" AND Client.Name='" + clientCombo->itemText(clientIndex) + "'");
112 int fileSetIndex = fileSetCombo->currentIndex();
113 if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
114 m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
117 if (mainWin->m_sqlDebug) {
118 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
120 QStringList directories;
121 if (m_console->sql_cmd(cmd, directories)) {
122 if (mainWin->m_miscDebug) {
123 Pmsg1(000, "Done with query %i directories\n", directories.count());
125 foreach(QString directory, directories) {
127 parseDirectory(directory);
133 * Function to parse a directory into all possible subdirectories, then add to
136 void restoreTree::parseDirectory(QString &dir_in)
140 /* Clean up the directory string remove some funny char after last '/' */
141 QRegExp rgx("[^/]$");
142 int lastslash = rgx.indexIn(dir_in);
143 dir_in.replace(lastslash, dir_in.length()-lastslash, "");
144 if ((mainWin->m_miscDebug) && (m_debugTrap))
145 Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
147 /* split and add if not in yet */
148 QString direct, path;
151 QStringList pathAfter, dirAfter;
152 /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/
153 * if not added into tree, then try /etc/ and somedir/ if not added, then try
154 * / and etc/ . That should succeed, then add the ones that failed in reverse */
155 while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) {
156 direct = path = dir_in;
157 path.replace(index+1,dir_in.length()-index-1,"");
158 direct.replace(0,index+1,"");
159 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
160 QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
161 .arg(dir_in.length())
165 Pmsg0(000, msg.toUtf8().data());
167 if (addDirectory(path, direct)) done = true;
169 if ((mainWin->m_miscDebug) && (m_debugTrap))
170 Pmsg0(000, "Saving for later\n");
171 pathAfter.prepend(path);
172 dirAfter.prepend(direct);
176 for (int k=0; k<pathAfter.count(); k++) {
177 if (addDirectory(pathAfter[k], dirAfter[k]))
178 if ((mainWin->m_miscDebug) && (m_debugTrap))
179 Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
181 if ((mainWin->m_miscDebug) && (m_debugTrap))
182 Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
187 * Function called from fill directory when a directory is found to see if this
188 * directory exists in the directory pane and then add it to the directory pane
190 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
192 QString newdir = newdirr;
193 QString fullpath = m_cwd + newdirr;
194 bool ok = true, added = false;
196 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
197 QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
200 Pmsg0(000, msg.toUtf8().data());
204 /* add unix '/' directory first */
205 if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullpath,0) == -1)) {
207 QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
208 item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
210 item->setText(0, text.toUtf8().data());
211 item->setData(0, Qt::UserRole, QVariant(text));
212 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
213 Pmsg1(000, "Pre Inserting %s\n",text.toUtf8().data());
215 m_dirPaths.insert(text, item);
217 /* no need to check for windows drive if unix */
218 if (m_winRegExpDrive.indexIn(m_cwd, 0) == 0) {
219 /* this is a windows drive add the base widget */
220 QTreeWidgetItem *item = item = new QTreeWidgetItem(directoryTree);
221 item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
222 item->setText(0, m_cwd);
223 item->setData(0, Qt::UserRole, QVariant(fullpath));
224 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
225 Pmsg0(000, "Added Base \"letter\":/\n");
227 m_dirPaths.insert(m_cwd, item);
231 /* is it already existent ?? */
232 if (!m_dirPaths.contains(fullpath)) {
233 QTreeWidgetItem *item = NULL;
234 QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
236 /* new directories to add */
237 item = new QTreeWidgetItem(parent);
238 item->setText(0, newdir.toUtf8().data());
239 item->setData(0, Qt::UserRole, QVariant(fullpath));
242 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
243 QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
246 Pmsg0(000, msg.toUtf8().data());
249 /* insert into hash */
251 if ((mainWin->m_miscDebug) && (m_debugTrap)) {
252 Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
254 m_dirPaths.insert(fullpath, item);
262 * Virtual function which is called when this page is visible on the stack
264 void restoreTree::currentStackItem()
267 if (!m_console->preventInUseConnect())
275 * Populate the tree when refresh button pushed.
277 void restoreTree::refreshButtonPushed()
279 populateDirectoryTree();
283 * Set the values of non-job combo boxes to the job defaults
285 void restoreTree::jobComboChanged(int)
287 job_defaults job_defs;
290 job_defs.job_name = jobCombo->currentText();
291 if (m_console->get_job_defaults(job_defs)) {
292 fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
293 clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
298 * Function to populate the file list table
300 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
304 QBrush blackBrush(Qt::black);
305 QString directory = item->data(0,Qt::UserRole).toString();
306 directoryLabel->setText("Present Working Directory : " + directory);
308 "SELECT DISTINCT Filename.Name"
309 " FROM File LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
310 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
311 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
312 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
313 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)";
314 cmd += " WHERE Path.Path='" + directory + "' AND Filename.Name!='' AND " + m_condition;
316 QStringList headerlist = (QStringList() << "File Name");
318 /* Also clear the version table here */
319 versionTable->clear();
320 versionTable->setRowCount(0);
321 versionTable->setColumnCount(0);
322 fileTable->setColumnCount(headerlist.size());
323 fileTable->setHorizontalHeaderLabels(headerlist);
325 if (mainWin->m_sqlDebug) {
326 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
329 if (m_console->sql_cmd(cmd, results)) {
330 m_resultCount = results.count();
332 QTableWidgetItem* tableItem;
334 QStringList fieldlist;
335 fileTable->setRowCount(results.size());
338 /* Iterate through the record returned from the query */
339 foreach (QString resultline, results) {
340 /* Iterate through fields in the record */
342 fieldlist = resultline.split("\t");
343 foreach (field, fieldlist) {
344 field = field.trimmed(); /* strip leading & trailing spaces */
345 tableItem = new QTableWidgetItem(field,1);
346 tableItem->setFlags(0);
347 tableItem->setForeground(blackBrush);
348 tableItem->setData(Qt::UserRole,QVariant(directory));
349 fileTable->setItem(row, column, tableItem);
354 fileTable->setRowCount(row);
356 fileTable->resizeColumnsToContents();
357 fileTable->resizeRowsToContents();
358 fileTable->verticalHeader()->hide();
362 * Function to populate the version table
364 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
366 if (fileTableItem == NULL)
368 QString file = fileTableItem->text();
369 QString directory = fileTableItem->data(Qt::UserRole).toString();
371 QBrush blackBrush(Qt::black);
373 "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
375 " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
376 " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
377 " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
378 " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
379 " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)";
380 cmd += " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "' AND " + m_condition;
382 QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
383 versionTable->clear();
384 versionTable->setColumnCount(headerlist.size());
385 versionTable->setHorizontalHeaderLabels(headerlist);
387 if (mainWin->m_sqlDebug) {
388 Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
391 if (m_console->sql_cmd(cmd, results)) {
392 m_resultCount = results.count();
394 QTableWidgetItem* tableItem;
396 QStringList fieldlist;
397 versionTable->setRowCount(results.size());
400 /* Iterate through the record returned from the query */
401 foreach (QString resultline, results) {
402 fieldlist = resultline.split("\t");
404 /* remove directory */
405 if (fieldlist[0].trimmed() != "") {
406 /* Iterate through fields in the record */
407 foreach (field, fieldlist) {
408 field = field.trimmed(); /* strip leading & trailing spaces */
409 tableItem = new QTableWidgetItem(field,1);
410 tableItem->setFlags(0);
411 tableItem->setForeground(blackBrush);
412 tableItem->setData(Qt::UserRole,QVariant(directory));
413 versionTable->setItem(row, column, tableItem);
420 versionTable->resizeColumnsToContents();
421 versionTable->resizeRowsToContents();
422 versionTable->verticalHeader()->hide();
426 * Save user settings associated with this page
428 void restoreTree::writeSettings()
430 QSettings settings(m_console->m_dir->name(), "bat");
431 settings.beginGroup("RestoreTree");
432 settings.setValue("splitterSizes", splitter->saveState());
437 * Read and restore user settings associated with this page
439 void restoreTree::readSettings()
441 QSettings settings(m_console->m_dir->name(), "bat");
442 settings.beginGroup("RestoreTree");
443 splitter->restoreState(settings.value("splitterSizes").toByteArray());
448 * This is a funcion to accomplish the one thing I struggled to figure out what
449 * was taking so long. It add the icons, but after the tree is made.
451 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
453 int childCount = item->childCount();
454 for (int i=0; i<childCount; i++) {
455 QTreeWidgetItem *child = item->child(i);
456 child->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));