]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
5adf3f94a0464799fae3914e95d13901a4ab7f88
[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_winregex.setPattern("^/[a-z]:/$");
56    m_slashregex.setPattern("/");
57 }
58
59 restoreTree::~restoreTree()
60 {
61    writeSettings();
62 }
63
64 /*
65  * Called from the constructor to set up the page widgets and connections.
66  */
67 void restoreTree::setupPage()
68 {
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 *)));
77
78    QStringList titles;
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);
85 }
86
87 /*
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.
90  */
91 void restoreTree::populateDirectoryTree()
92 {
93    m_slashTrap = false;
94    m_winTrap = false;
95    m_isWin = false;
96    m_dirPaths.clear();
97    directoryTree->clear();
98    fileTable->clear();
99    versionTable->clear();
100    QString cmd =
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) + "'");
110    }
111    int fileSetIndex = fileSetCombo->currentIndex();
112    if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
113       m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
114    }
115    cmd += m_condition;
116    if (mainWin->m_sqlDebug) {
117       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
118    }
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());
123       }
124       foreach(QString directory, directories) {
125          parseDirectory(directory);
126       }
127    }
128 }
129
130 /*
131  * Function to parse a directory into all possible subdirectories, then add to
132  * The tree.
133  */
134 void restoreTree::parseDirectory(QString &dir_in)
135 {
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());
141
142    /* split and add if not in yet */
143    QString direct, path;
144    int index;
145    bool done = false;
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;
159       else {
160          if (mainWin->m_miscDebug) Pmsg0(000, "Saving for later\n");
161          pathAfter.prepend(path);
162          dirAfter.prepend(direct);
163       }
164       dir_in = path;
165    }
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());
170       else
171          if (mainWin->m_miscDebug)
172             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
173    }
174 }
175
176 /*
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
179  */
180 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
181 {
182    QString newdir = newdirr;
183    QString fullpath = m_cwd + newdirr;
184    bool ok = true, added = false;
185    bool windrive = false;
186
187    if (mainWin->m_miscDebug) {
188       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
189                     .arg(m_cwd)
190                     .arg(newdir);
191       Pmsg0(000, msg.toUtf8().data());
192    }
193
194    if (!m_slashTrap) {
195       /* add unix '/' directory first */
196       if (m_dirPaths.empty() && (m_winregex.indexIn(fullpath,0) == -1)) {
197          m_slashTrap = true;
198          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
199          item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
200          
201          QString text("/");
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());
206          }
207          m_dirPaths.insert(text, item);
208       }
209    }
210
211    if (!m_winTrap) {
212       m_winTrap = true;
213       if (m_winregex.indexIn(fullpath,0) == 0) {
214          m_isWin = true;
215          /* this is a windows drive */
216          if (mainWin->m_miscDebug) {
217             Pmsg0(000, "Need to do windows \"letter\":/\n");
218          }
219       }
220    }
221    if (m_isWin) {
222       fullpath.replace(0,1,"");
223       windrive = true;
224    }
225  
226    /* is it already existent ?? */
227    if (!m_dirPaths.contains(fullpath)) {
228       QTreeWidgetItem *item = NULL;
229       if (windrive) {
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));
234       } else {
235          QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
236          if (parent) {
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));
241          } else {
242             ok = false;
243             if (mainWin->m_miscDebug) {
244                QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
245                     .arg(m_cwd)
246                     .arg(newdir);
247                Pmsg0(000, msg.toUtf8().data());
248             }
249          }
250       }
251       /* insert into hash */
252       if (ok) {
253          if (mainWin->m_miscDebug) {
254             Pmsg1(000, "Inserting %s\n",fullpath.toUtf8().data());
255          }
256          m_dirPaths.insert(fullpath, item);
257          added = true;
258       }
259    }
260    return added;
261 }
262
263 /*
264  * Virtual function which is called when this page is visible on the stack
265  */
266 void restoreTree::currentStackItem()
267 {
268    if(!m_populated) {
269       if (!m_console->preventInUseConnect())
270          return;
271       setupPage();
272       m_populated=true;
273    }
274 }
275
276 /*
277  * Populate the tree when refresh button pushed.
278  */
279 void restoreTree::refreshButtonPushed()
280 {
281    populateDirectoryTree();
282 }
283
284 /*
285  * Set the values of non-job combo boxes to the job defaults
286  */
287 void restoreTree::jobComboChanged(int)
288 {
289    job_defaults job_defs;
290
291    (void)index;
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));
296    }
297 }
298
299 /*
300  * Function to populate the file list table
301  */
302 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
303 {
304    if (item == NULL)
305       return;
306    QBrush blackBrush(Qt::black);
307    QString directory = item->data(0,Qt::UserRole).toString();
308    directoryLabel->setText("Present Working Directory : " + directory);
309    QString cmd =
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;
317
318    QStringList headerlist = (QStringList() << "File Name");
319    fileTable->clear();
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);
326
327    if (mainWin->m_sqlDebug) {
328       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
329    }
330    QStringList results;
331    if (m_console->sql_cmd(cmd, results)) {
332       m_resultCount = results.count();
333    
334       QTableWidgetItem* tableItem;
335       QString field;
336       QStringList fieldlist;
337       fileTable->setRowCount(results.size());
338
339       int row = 0;
340       /* Iterate through the record returned from the query */
341       foreach (QString resultline, results) {
342          /* Iterate through fields in the record */
343          int column = 0;
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);
352             column++;
353          }
354          row++;
355       }
356       fileTable->setRowCount(row);
357    }
358    fileTable->resizeColumnsToContents();
359    fileTable->resizeRowsToContents();
360    fileTable->verticalHeader()->hide();
361 }
362
363 /*
364  * Function to populate the version table
365  */
366 void restoreTree::fileItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
367 {
368    if (fileTableItem == NULL)
369       return;
370    QString file = fileTableItem->text();
371    QString directory = fileTableItem->data(Qt::UserRole).toString();
372
373    QBrush blackBrush(Qt::black);
374    QString cmd = 
375       "SELECT File.FileId, Job.JobId, Job.EndTime, File.Md5 "
376       " FROM File"
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;
383
384    QStringList headerlist = (QStringList() << "File Id" << "Job Id" << "End Time" << "Md5");
385    versionTable->clear();
386    versionTable->setColumnCount(headerlist.size());
387    versionTable->setHorizontalHeaderLabels(headerlist);
388
389    if (mainWin->m_sqlDebug) {
390       Pmsg1(000, "Query cmd : %s\n",cmd.toUtf8().data());
391    }
392    QStringList results;
393    if (m_console->sql_cmd(cmd, results)) {
394       m_resultCount = results.count();
395    
396       QTableWidgetItem* tableItem;
397       QString field;
398       QStringList fieldlist;
399       versionTable->setRowCount(results.size());
400
401       int row = 0;
402       /* Iterate through the record returned from the query */
403       foreach (QString resultline, results) {
404          fieldlist = resultline.split("\t");
405          int column = 0;
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);
416                column++;
417             }
418             row++;
419          }
420       }
421    }
422    versionTable->resizeColumnsToContents();
423    versionTable->resizeRowsToContents();
424    versionTable->verticalHeader()->hide();
425 }
426
427 /*
428  * Save user settings associated with this page
429  */
430 void restoreTree::writeSettings()
431 {
432    QSettings settings(m_console->m_dir->name(), "bat");
433    settings.beginGroup("RestoreTree");
434    settings.setValue("splitterSizes", splitter->saveState());
435    settings.endGroup();
436 }
437
438 /*
439  * Read and restore user settings associated with this page
440  */
441 void restoreTree::readSettings()
442 {
443    QSettings settings(m_console->m_dir->name(), "bat");
444    settings.beginGroup("RestoreTree");
445    splitter->restoreState(settings.value("splitterSizes").toByteArray());
446    settings.endGroup();
447 }
448
449 /*
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.
452  */
453 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
454 {
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")));
459    }
460 }