]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Changes to fix getting restoretree or "Version Browser" to work with
[bacula/bacula] / bacula / src / qt-console / restore / restoretree.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
5
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.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28  
29 /*
30  *   Version $Id: restore.cpp 4945 2007-05-31 01:24:28Z bartleyd2 $
31  *
32  *  Restore Class 
33  *
34  *   Kern Sibbald, February MMVII
35  *
36  */ 
37
38 #include "bat.h"
39 #include "restoretree.h"
40 #include "pages.h"
41
42 restoreTree::restoreTree()
43 {
44    setupUi(this);
45    m_name = "Version Browser";
46    pgInitialize();
47    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/browse.png")));
49
50    m_closeable = true;
51    m_populated = false;
52
53    readSettings();
54    dockPage();
55    m_winRegExpDrive.setPattern("^[a-z]:/$");
56    m_winRegExpPath.setPattern("^[a-z]:/");
57    m_slashregex.setPattern("/");
58    m_debugCnt = 0;
59    m_debugTrap = true;
60 }
61
62 restoreTree::~restoreTree()
63 {
64    writeSettings();
65 }
66
67 /*
68  * Called from the constructor to set up the page widgets and connections.
69  */
70 void restoreTree::setupPage()
71 {
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 *)));
80
81    QStringList titles;
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);
88 }
89
90 /*
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.
93  */
94 void restoreTree::populateDirectoryTree()
95 {
96    m_slashTrap = false;
97    m_dirPaths.clear();
98    directoryTree->clear();
99    fileTable->clear();
100    versionTable->clear();
101    QString cmd =
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) + "'");
111    }
112    int fileSetIndex = fileSetCombo->currentIndex();
113    if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
114       m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
115    }
116    cmd += m_condition;
117    if (mainWin->m_sqlDebug) {
118       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
119    }
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());
124       }
125       foreach(QString directory, directories) {
126          m_debugCnt += 1;
127          parseDirectory(directory);
128       }
129    }
130 }
131
132 /*
133  * Function to parse a directory into all possible subdirectories, then add to
134  * The tree.
135  */
136 void restoreTree::parseDirectory(QString &dir_in)
137 {
138    if (m_debugCnt > 2)
139       m_debugTrap = false;
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());
146
147    /* split and add if not in yet */
148    QString direct, path;
149    int index;
150    bool done = false;
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())
162                     .arg(index)
163                     .arg(path)
164                     .arg(direct);
165          Pmsg0(000, msg.toUtf8().data());
166       }
167       if (addDirectory(path, direct)) done = true;
168       else {
169          if ((mainWin->m_miscDebug) && (m_debugTrap))
170             Pmsg0(000, "Saving for later\n");
171          pathAfter.prepend(path);
172          dirAfter.prepend(direct);
173       }
174       dir_in = path;
175    }
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());
180       else
181          if ((mainWin->m_miscDebug) && (m_debugTrap))
182             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
183    }
184 }
185
186 /*
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
189  */
190 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
191 {
192    QString newdir = newdirr;
193    QString fullpath = m_cwd + newdirr;
194    bool ok = true, added = false;
195
196    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
197       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
198                     .arg(m_cwd)
199                     .arg(newdir);
200       Pmsg0(000, msg.toUtf8().data());
201    }
202
203    if (!m_slashTrap) {
204       /* add unix '/' directory first */
205       if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullpath,0) == -1)) {
206          m_slashTrap = true;
207          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
208          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
209          QString text("/");
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());
214          }
215          m_dirPaths.insert(text, item);
216       }
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");
226          }
227          m_dirPaths.insert(m_cwd, item);
228       }
229    }
230  
231    /* is it already existent ?? */
232    if (!m_dirPaths.contains(fullpath)) {
233       QTreeWidgetItem *item = NULL;
234       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
235       if (parent) {
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));
240       } else {
241          ok = false;
242          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
243             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
244                  .arg(m_cwd)
245                  .arg(newdir);
246             Pmsg0(000, msg.toUtf8().data());
247          }
248       }
249       /* insert into hash */
250       if (ok) {
251          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
252             Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
253          }
254          m_dirPaths.insert(fullpath, item);
255          added = true;
256       }
257    }
258    return added;
259 }
260
261 /*
262  * Virtual function which is called when this page is visible on the stack
263  */
264 void restoreTree::currentStackItem()
265 {
266    if(!m_populated) {
267       if (!m_console->preventInUseConnect())
268          return;
269       setupPage();
270       m_populated=true;
271    }
272 }
273
274 /*
275  * Populate the tree when refresh button pushed.
276  */
277 void restoreTree::refreshButtonPushed()
278 {
279    populateDirectoryTree();
280 }
281
282 /*
283  * Set the values of non-job combo boxes to the job defaults
284  */
285 void restoreTree::jobComboChanged(int)
286 {
287    job_defaults job_defs;
288
289    (void)index;
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));
294    }
295 }
296
297 /*
298  * Function to populate the file list table
299  */
300 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
301 {
302    if (item == NULL)
303       return;
304    QBrush blackBrush(Qt::black);
305    QString directory = item->data(0,Qt::UserRole).toString();
306    directoryLabel->setText("Present Working Directory : " + directory);
307    QString cmd =
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;
315
316    QStringList headerlist = (QStringList() << "File Name");
317    fileTable->clear();
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);
324
325    if (mainWin->m_sqlDebug) {
326       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
327    }
328    QStringList results;
329    if (m_console->sql_cmd(cmd, results)) {
330       m_resultCount = results.count();
331    
332       QTableWidgetItem* tableItem;
333       QString field;
334       QStringList fieldlist;
335       fileTable->setRowCount(results.size());
336
337       int row = 0;
338       /* Iterate through the record returned from the query */
339       foreach (QString resultline, results) {
340          /* Iterate through fields in the record */
341          int column = 0;
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);
350             column++;
351          }
352          row++;
353       }
354       fileTable->setRowCount(row);
355    }
356    fileTable->resizeColumnsToContents();
357    fileTable->resizeRowsToContents();
358    fileTable->verticalHeader()->hide();
359 }
360
361 /*
362  * Function to populate the version table
363  */
364 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
365 {
366    if (fileTableItem == NULL)
367       return;
368    QString file = fileTableItem->text();
369    QString directory = fileTableItem->data(Qt::UserRole).toString();
370
371    QBrush blackBrush(Qt::black);
372    QString cmd = 
373       "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
374       " FROM File"
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;
381
382    QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
383    versionTable->clear();
384    versionTable->setColumnCount(headerlist.size());
385    versionTable->setHorizontalHeaderLabels(headerlist);
386
387    if (mainWin->m_sqlDebug) {
388       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
389    }
390    QStringList results;
391    if (m_console->sql_cmd(cmd, results)) {
392       m_resultCount = results.count();
393    
394       QTableWidgetItem* tableItem;
395       QString field;
396       QStringList fieldlist;
397       versionTable->setRowCount(results.size());
398
399       int row = 0;
400       /* Iterate through the record returned from the query */
401       foreach (QString resultline, results) {
402          fieldlist = resultline.split("\t");
403          int column = 0;
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);
414                column++;
415             }
416             row++;
417          }
418       }
419    }
420    versionTable->resizeColumnsToContents();
421    versionTable->resizeRowsToContents();
422    versionTable->verticalHeader()->hide();
423 }
424
425 /*
426  * Save user settings associated with this page
427  */
428 void restoreTree::writeSettings()
429 {
430    QSettings settings(m_console->m_dir->name(), "bat");
431    settings.beginGroup("RestoreTree");
432    settings.setValue("splitterSizes", splitter->saveState());
433    settings.endGroup();
434 }
435
436 /*
437  * Read and restore user settings associated with this page
438  */
439 void restoreTree::readSettings()
440 {
441    QSettings settings(m_console->m_dir->name(), "bat");
442    settings.beginGroup("RestoreTree");
443    splitter->restoreState(settings.value("splitterSizes").toByteArray());
444    settings.endGroup();
445 }
446
447 /*
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.
450  */
451 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
452 {
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")));
457    }
458 }