]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Clear list before populating lists in console.
[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 and included
11    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    fileTable->setRowCount(0);
101    fileTable->setColumnCount(0);
102    versionTable->clear();
103    versionTable->setRowCount(0);
104    versionTable->setColumnCount(0);
105    jobTable->clear();
106    jobTable->setRowCount(0);
107    jobTable->setColumnCount(0);
108
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) + "'");
113    }
114    int fileSetIndex = fileSetCombo->currentIndex();
115    if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
116       m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
117    }
118    m_jobQueryPart =
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";
123    m_jobQuery =
124       "SELECT Job.Jobid"
125       " From Job" + m_jobQueryPart;
126    if (mainWin->m_sqlDebug) {
127       Pmsg1(000, "Query cmd : %s\n",m_jobQuery.toUtf8().data());
128    }
129    populateJobTable();
130
131    QString cmd =
132       "SELECT DISTINCT Path.Path"
133       " FROM 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());
139    }
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());
144       }
145       foreach(QString directory, directories) {
146          m_debugCnt += 1;
147          parseDirectory(directory);
148       }
149    }
150 }
151
152 /*
153  * Function to parse a directory into all possible subdirectories, then add to
154  * The tree.
155  */
156 void restoreTree::parseDirectory(QString &dir_in)
157 {
158    if (m_debugCnt > 2)
159       m_debugTrap = false;
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());
166
167    /* split and add if not in yet */
168    QString direct, path;
169    int index;
170    bool done = false;
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())
182                     .arg(index)
183                     .arg(path)
184                     .arg(direct);
185          Pmsg0(000, msg.toUtf8().data());
186       }
187       if (addDirectory(path, direct)) done = true;
188       else {
189          if ((mainWin->m_miscDebug) && (m_debugTrap))
190             Pmsg0(000, "Saving for later\n");
191          pathAfter.prepend(path);
192          dirAfter.prepend(direct);
193       }
194       dir_in = path;
195    }
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());
200       else
201          if ((mainWin->m_miscDebug) && (m_debugTrap))
202             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
203    }
204 }
205
206 /*
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
209  */
210 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
211 {
212    QString newdir = newdirr;
213    QString fullpath = m_cwd + newdirr;
214    bool ok = true, added = false;
215
216    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
217       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
218                     .arg(m_cwd)
219                     .arg(newdir);
220       Pmsg0(000, msg.toUtf8().data());
221    }
222
223    if (!m_slashTrap) {
224       /* add unix '/' directory first */
225       if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullpath,0) == -1)) {
226          m_slashTrap = true;
227          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
228          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
229          QString text("/");
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());
234          }
235          m_dirPaths.insert(text, item);
236       }
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");
246          }
247          m_dirPaths.insert(m_cwd, item);
248       }
249    }
250  
251    /* is it already existent ?? */
252    if (!m_dirPaths.contains(fullpath)) {
253       QTreeWidgetItem *item = NULL;
254       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
255       if (parent) {
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));
260       } else {
261          ok = false;
262          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
263             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
264                  .arg(m_cwd)
265                  .arg(newdir);
266             Pmsg0(000, msg.toUtf8().data());
267          }
268       }
269       /* insert into hash */
270       if (ok) {
271          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
272             Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
273          }
274          m_dirPaths.insert(fullpath, item);
275          added = true;
276       }
277    }
278    return added;
279 }
280
281 /*
282  * Virtual function which is called when this page is visible on the stack
283  */
284 void restoreTree::currentStackItem()
285 {
286    if(!m_populated) {
287       if (!m_console->preventInUseConnect())
288          return;
289       setupPage();
290       m_populated=true;
291    }
292 }
293
294 /*
295  * Populate the tree when refresh button pushed.
296  */
297 void restoreTree::refreshButtonPushed()
298 {
299    populateDirectoryTree();
300 }
301
302 /*
303  * Set the values of non-job combo boxes to the job defaults
304  */
305 void restoreTree::jobComboChanged(int)
306 {
307    job_defaults job_defs;
308
309    (void)index;
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));
314    }
315 }
316
317 /*
318  * Function to populate the file list table
319  */
320 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
321 {
322    if (item == NULL)
323       return;
324    QBrush blackBrush(Qt::black);
325    QString directory = item->data(0,Qt::UserRole).toString();
326    directoryLabel->setText("Present Working Directory : " + directory);
327    QString cmd =
328       "SELECT DISTINCT Filename.Name"
329       " FROM File "
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 + ")";
335  
336
337    QStringList headerlist = (QStringList() << "File Name");
338    fileTable->clear();
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);
345
346    if (mainWin->m_sqlDebug) {
347       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
348    }
349    QStringList results;
350    if (m_console->sql_cmd(cmd, results)) {
351    
352       QTableWidgetItem* tableItem;
353       QString field;
354       QStringList fieldlist;
355       fileTable->setRowCount(results.size());
356
357       int row = 0;
358       /* Iterate through the record returned from the query */
359       foreach (QString resultline, results) {
360          /* Iterate through fields in the record */
361          int column = 0;
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);
370             column++;
371          }
372          row++;
373       }
374       fileTable->setRowCount(row);
375    }
376    fileTable->resizeColumnsToContents();
377    fileTable->resizeRowsToContents();
378    fileTable->verticalHeader()->hide();
379 }
380
381 /*
382  * Function to populate the version table
383  */
384 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
385 {
386    if (fileTableItem == NULL)
387       return;
388    QString file = fileTableItem->text();
389    QString directory = fileTableItem->data(Qt::UserRole).toString();
390
391    QBrush blackBrush(Qt::black);
392    QString cmd = 
393       "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
394       " FROM File"
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 + ")";
400
401    QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
402    versionTable->clear();
403    versionTable->setColumnCount(headerlist.size());
404    versionTable->setHorizontalHeaderLabels(headerlist);
405
406    if (mainWin->m_sqlDebug) {
407       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
408    }
409    QStringList results;
410    if (m_console->sql_cmd(cmd, results)) {
411    
412       QTableWidgetItem* tableItem;
413       QString field;
414       QStringList fieldlist;
415       versionTable->setRowCount(results.size());
416
417       int row = 0;
418       /* Iterate through the record returned from the query */
419       foreach (QString resultline, results) {
420          fieldlist = resultline.split("\t");
421          int column = 0;
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);
432                column++;
433             }
434             row++;
435          }
436       }
437    }
438    versionTable->resizeColumnsToContents();
439    versionTable->resizeRowsToContents();
440    versionTable->verticalHeader()->hide();
441 }
442
443 /*
444  * Save user settings associated with this page
445  */
446 void restoreTree::writeSettings()
447 {
448    QSettings settings(m_console->m_dir->name(), "bat");
449    settings.beginGroup("RestoreTree");
450    settings.setValue("splitterSizes", splitter->saveState());
451    settings.endGroup();
452 }
453
454 /*
455  * Read and restore user settings associated with this page
456  */
457 void restoreTree::readSettings()
458 {
459    QSettings settings(m_console->m_dir->name(), "bat");
460    settings.beginGroup("RestoreTree");
461    splitter->restoreState(settings.value("splitterSizes").toByteArray());
462    settings.endGroup();
463 }
464
465 /*
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.
468  */
469 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
470 {
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")));
475    }
476 }
477
478 void restoreTree::populateJobTable()
479 {
480    QBrush blackBrush(Qt::black);
481    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Type");
482    jobTable->clear();
483    jobTable->setColumnCount(headerlist.size());
484    jobTable->setHorizontalHeaderLabels(headerlist);
485    QString jobQuery =
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());
491    }
492
493    QStringList results;
494    if (m_console->sql_cmd(jobQuery, results)) {
495    
496       QTableWidgetItem* tableItem;
497       QString field;
498       QStringList fieldlist;
499       jobTable->setRowCount(results.size());
500
501       int row = 0;
502       /* Iterate through the record returned from the query */
503       foreach (QString resultline, results) {
504          fieldlist = resultline.split("\t");
505          int column = 0;
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);
515                column++;
516             }
517             row++;
518          }
519       }
520    }
521    jobTable->resizeColumnsToContents();
522    jobTable->resizeRowsToContents();
523    jobTable->verticalHeader()->hide();
524 }