]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
This is an example of where I would like to go with offering the user the ability
[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 #include "restoretreerun.h"
42
43 restoreTree::restoreTree()
44 {
45    setupUi(this);
46    m_name = "Version Browser";
47    pgInitialize();
48    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
49    thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
50
51    m_closeable = true;
52    m_populated = false;
53
54    readSettings();
55    dockPage();
56    m_winRegExpDrive.setPattern("^[a-z]:/$");
57    m_winRegExpPath.setPattern("^[a-z]:/");
58    m_slashregex.setPattern("/");
59    m_debugCnt = 0;
60    m_debugTrap = true;
61 }
62
63 restoreTree::~restoreTree()
64 {
65    writeSettings();
66 }
67
68 /*
69  * Called from the constructor to set up the page widgets and connections.
70  */
71 void restoreTree::setupPage()
72 {
73    connect(refreshButton, SIGNAL(pressed()), this, SLOT(refreshButtonPushed()));
74    connect(testButton, SIGNAL(pressed()), this, SLOT(testButtonPushed()));
75    connect(jobCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(jobComboChanged(int)));
76    connect(directoryTree, SIGNAL(currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
77            this, SLOT(directoryCurrentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
78    connect(directoryTree, SIGNAL(itemExpanded(QTreeWidgetItem *)),
79            this, SLOT(directoryItemExpanded(QTreeWidgetItem *)));
80    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
81            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
82    connect(fileTable, SIGNAL(currentItemChanged(QTableWidgetItem *, QTableWidgetItem *)),
83            this, SLOT(fileCurrentItemChanged(QTableWidgetItem *, QTableWidgetItem *)));
84
85    QStringList titles = QStringList() << "Directories";
86    directoryTree->setHeaderLabels(titles);
87    clientCombo->addItems(m_console->client_list);
88    fileSetCombo->addItem("Any");
89    fileSetCombo->addItems(m_console->fileset_list);
90    jobCombo->addItems(m_console->job_list);
91
92    directoryTree->setContextMenuPolicy(Qt::ActionsContextMenu);
93 }
94
95 /*
96  * When refresh button is pushed, perform a query getting the directories and
97  * use parseDirectory and addDirectory to populate the directory tree with items.
98  */
99 void restoreTree::populateDirectoryTree()
100 {
101    m_slashTrap = false;
102    m_dirPaths.clear();
103    directoryTree->clear();
104    fileTable->clear();
105    fileTable->setRowCount(0);
106    fileTable->setColumnCount(0);
107    versionTable->clear();
108    versionTable->setRowCount(0);
109    versionTable->setColumnCount(0);
110    m_fileExceptionHash.clear();
111    m_fileExceptionMulti.clear();
112    m_versionExceptionHash.clear();
113    m_directoryIconStateHash.clear();
114
115    
116    int clientIndex = clientCombo->currentIndex();
117    int fileSetIndex = fileSetCombo->currentIndex();
118    QString jobComboText = jobCombo->itemText(jobCombo->currentIndex());
119    QString clientComboText = clientCombo->itemText(clientIndex);
120    QString fileSetComboText = fileSetCombo->itemText(fileSetIndex);
121    if ((m_prevJobCombo != jobComboText) || (m_prevClientCombo != clientComboText) || (m_prevFileSetCombo != fileSetComboText)) {
122       m_prevJobCombo =  jobComboText;
123       m_prevClientCombo = clientComboText;
124       m_prevFileSetCombo = fileSetComboText;
125       if (mainWin->m_rtPopDirDebug) Pmsg0(000, "Repopulating the Job Table\n");
126
127       m_condition = " Job.name = '" + jobCombo->itemText(jobCombo->currentIndex()) + "'";
128       if ((clientIndex >= 0) && (clientCombo->itemText(clientIndex) != "Any")) {
129          m_condition.append(" AND Client.Name='" + clientCombo->itemText(clientIndex) + "'");
130       }
131       if ((fileSetIndex >= 0) && (fileSetCombo->itemText(fileSetIndex) != "Any")) {
132          m_condition.append(" AND FileSet.FileSet='" + fileSetCombo->itemText(fileSetIndex) + "'");
133       }
134       m_jobQueryPart =
135          " LEFT OUTER JOIN Client ON (Job.ClientId=Client.ClientId)"
136          " LEFT OUTER JOIN FileSet ON (Job.FileSetId=FileSet.FileSetId)"
137          " WHERE" + m_condition +
138          " AND Job.purgedfiles=0";
139       m_jobQuery =
140          "SELECT Job.Jobid"
141          " From Job" + m_jobQueryPart;
142       if (mainWin->m_sqlDebug) {
143          Pmsg1(000, "Query cmd : %s\n", m_jobQuery.toUtf8().data());
144       }
145       populateJobTable();
146       setJobsCheckedList();
147    } else {
148       setJobsCheckedList();
149    }
150
151    QString cmd =
152       "SELECT DISTINCT Path.Path AS Path"
153       " FROM Path"
154       " LEFT OUTER JOIN File ON (File.PathId=Path.PathId)"
155       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
156       " WHERE Job.Jobid IN (" + m_jobQuery + ")"
157       " ORDER BY Path";
158    if (mainWin->m_sqlDebug) {
159       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
160    }
161    QStringList directories;
162    if (m_console->sql_cmd(cmd, directories)) {
163       if (mainWin->m_miscDebug) {
164          Pmsg1(000, "Done with query %i directories\n", directories.count());
165       }
166       foreach(QString directory, directories) {
167          m_debugCnt += 1;
168          parseDirectory(directory);
169       }
170    }
171 }
172
173 /*
174  *  Function to set m_jobQuery from the jobs that are checked in the table
175  *  of jobs
176  */     
177 void restoreTree::setJobsCheckedList()
178 {
179    m_JobsCheckedList = "";
180    bool first = true;
181    /* Update the items in the version table */
182    int cnt = jobTable->rowCount();
183    for (int row=0; row<cnt; row++) {
184       QTableWidgetItem* jobItem = jobTable->item(row, 0);
185       if (jobItem->checkState() == Qt::Checked) {
186          if (!first)
187             m_JobsCheckedList += ",";
188          m_JobsCheckedList += jobItem->text();
189          first = false;
190          jobItem->setBackground(Qt::green);
191       } else
192          jobItem->setBackground(Qt::gray);
193    }
194    m_jobQuery = m_JobsCheckedList;
195 }
196
197 /*
198  * Function to parse a directory into all possible subdirectories, then add to
199  * The tree.
200  */
201 void restoreTree::parseDirectory(QString &dir_in)
202 {
203    /* m_debugTrap is to only print debugs for a few occurances of calling parseDirectory
204     * instead of printing out what could potentially a whole bunch */
205    if (m_debugCnt > 2)
206       m_debugTrap = false;
207    /* Clean up the directory string remove some funny char after last '/' */
208    QRegExp rgx("[^/]$");
209    int lastslash = rgx.indexIn(dir_in);
210    dir_in.replace(lastslash, dir_in.length()-lastslash, "");
211    if ((mainWin->m_miscDebug) && (m_debugTrap))
212       Pmsg1(000, "parsing %s\n", dir_in.toUtf8().data());
213
214    /* split and add if not in yet */
215    QString direct, path;
216    int index;
217    bool done = false;
218    QStringList pathAfter, dirAfter;
219    /* start from the end, turn /etc/somedir/subdir/ into /etc/somedir and subdir/ 
220     * if not added into tree, then try /etc/ and somedir/ if not added, then try
221     * / and etc/ .  That should succeed, then add the ones that failed in reverse */
222    while (((index = m_slashregex.lastIndexIn(dir_in, -2)) != -1) && (!done)) {
223       direct = path = dir_in;
224       path.replace(index+1, dir_in.length()-index-1,"");
225       direct.replace(0, index+1, "");
226       if ((mainWin->m_miscDebug) && (m_debugTrap)) {
227          QString msg = QString("length = \"%1\" index = \"%2\" Adding \"%3\" \"%4\"\n")
228                     .arg(dir_in.length()).arg(index).arg(path).arg(direct);
229          Pmsg0(000, msg.toUtf8().data());
230       }
231       if (addDirectory(path, direct)) done = true;
232       else {
233          if ((mainWin->m_miscDebug) && (m_debugTrap))
234             Pmsg0(000, "Saving for later\n");
235          pathAfter.prepend(path);
236          dirAfter.prepend(direct);
237       }
238       dir_in = path;
239    }
240
241    for (int k=0; k<pathAfter.count(); k++) {
242       if (addDirectory(pathAfter[k], dirAfter[k]))
243          if ((mainWin->m_miscDebug) && (m_debugTrap))
244             Pmsg2(000, "Adding After %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
245       else
246          if ((mainWin->m_miscDebug) && (m_debugTrap))
247             Pmsg2(000, "Error Adding %s %s\n", pathAfter[k].toUtf8().data(), dirAfter[k].toUtf8().data());
248    }
249 }
250
251 /*
252  * Function called from fill directory when a directory is found to see if this
253  * directory exists in the directory pane and then add it to the directory pane
254  */
255 bool restoreTree::addDirectory(QString &m_cwd, QString &newdirr)
256 {
257    QString newdir = newdirr;
258    QString fullPath = m_cwd + newdirr;
259    bool ok = true, added = false;
260
261    if ((mainWin->m_miscDebug) && (m_debugTrap)) {
262       QString msg = QString("In addDirectory cwd \"%1\" newdir \"%2\"\n")
263                     .arg(m_cwd)
264                     .arg(newdir);
265       Pmsg0(000, msg.toUtf8().data());
266    }
267
268    if (!m_slashTrap) {
269       /* add unix '/' directory first */
270       if (m_dirPaths.empty() && (m_winRegExpPath.indexIn(fullPath, 0) == -1)) {
271          m_slashTrap = true;
272          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
273          QString text("/");
274          item->setText(0, text.toUtf8().data());
275          item->setData(0, Qt::UserRole, QVariant(text));
276          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
277          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
278          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
279             Pmsg1(000, "Pre Inserting %s\n", text.toUtf8().data());
280          }
281          m_dirPaths.insert(text, item);
282       }
283       /* no need to check for windows drive if unix */
284       if (m_winRegExpDrive.indexIn(m_cwd, 0) == 0) {
285          /* this is a windows drive add the base widget */
286          QTreeWidgetItem *item = new QTreeWidgetItem(directoryTree);
287          item->setText(0, m_cwd);
288          item->setData(0, Qt::UserRole, QVariant(fullPath));
289          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
290          item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
291          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
292             Pmsg0(000, "Added Base \"letter\":/\n");
293          }
294          m_dirPaths.insert(m_cwd, item);
295       }
296    }
297  
298    /* is it already existent ?? */
299    if (!m_dirPaths.contains(fullPath)) {
300       QTreeWidgetItem *item = NULL;
301       QTreeWidgetItem *parent = m_dirPaths.value(m_cwd);
302       if (parent) {
303          /* new directories to add */
304          item = new QTreeWidgetItem(parent);
305          item->setText(0, newdir.toUtf8().data());
306          item->setData(0, Qt::UserRole, QVariant(fullPath));
307          item->setCheckState(0, Qt::Unchecked);
308          /* Store the current state of the check status in column 1, which at
309           * this point has no text*/
310          item->setData(1, Qt::UserRole, QVariant(Qt::Unchecked));
311       } else {
312          ok = false;
313          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
314             QString msg = QString("In else of if parent cwd \"%1\" newdir \"%2\"\n")
315                  .arg(m_cwd)
316                  .arg(newdir);
317             Pmsg0(000, msg.toUtf8().data());
318          }
319       }
320       /* insert into hash */
321       if (ok) {
322          if ((mainWin->m_miscDebug) && (m_debugTrap)) {
323             Pmsg1(000, "Inserting %s\n", fullPath.toUtf8().data());
324          }
325          m_dirPaths.insert(fullPath, item);
326          added = true;
327       }
328    }
329    return added;
330 }
331
332 /*
333  * Virtual function which is called when this page is visible on the stack
334  */
335 void restoreTree::currentStackItem()
336 {
337    if(!m_populated) {
338       if (!m_console->preventInUseConnect())
339          return;
340       setupPage();
341       m_populated=true;
342    }
343 }
344
345 /*
346  * Populate the tree when refresh button pushed.
347  */
348 void restoreTree::refreshButtonPushed()
349 {
350    populateDirectoryTree();
351 }
352
353 /*
354  * Set the values of non-job combo boxes to the job defaults
355  */
356 void restoreTree::jobComboChanged(int)
357 {
358    job_defaults job_defs;
359
360    (void)index;
361    job_defs.job_name = jobCombo->currentText();
362    if (m_console->get_job_defaults(job_defs)) {
363       fileSetCombo->setCurrentIndex(fileSetCombo->findText(job_defs.fileset_name, Qt::MatchExactly));
364       clientCombo->setCurrentIndex(clientCombo->findText(job_defs.client_name, Qt::MatchExactly));
365    }
366 }
367
368 /*
369  * Function to populate the file list table
370  */
371 void restoreTree::directoryCurrentItemChanged(QTreeWidgetItem *item, QTreeWidgetItem *)
372 {
373    if (item == NULL)
374       return;
375
376    m_fileCheckStateList.clear();
377    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
378            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
379    QBrush blackBrush(Qt::black);
380    QString directory = item->data(0, Qt::UserRole).toString();
381    directoryLabel->setText("Present Working Directory : " + directory);
382    QString cmd =
383       "SELECT DISTINCT Filename.Name"
384       " FROM File "
385       " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
386       " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
387       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
388       " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
389       " AND Job.Jobid IN (" + m_jobQuery + ")";
390  
391    QStringList headerlist = (QStringList() << "File Name");
392    fileTable->clear();
393    /* Also clear the version table here */
394    versionTable->clear();
395    versionFileLabel->setText("");
396    versionTable->setRowCount(0);
397    versionTable->setColumnCount(0);
398    fileTable->setColumnCount(headerlist.size());
399    fileTable->setHorizontalHeaderLabels(headerlist);
400
401    if (mainWin->m_sqlDebug) {
402       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
403    }
404    QStringList results;
405    if (m_console->sql_cmd(cmd, results)) {
406    
407       QTableWidgetItem* tableItem;
408       QString field;
409       QStringList fieldlist;
410       fileTable->setRowCount(results.size());
411
412       int row = 0;
413       /* Iterate through the record returned from the query */
414       foreach (QString resultline, results) {
415          /* Iterate through fields in the record */
416          int column = 0;
417          fieldlist = resultline.split("\t");
418          foreach (field, fieldlist) {
419             field = field.trimmed();  /* strip leading & trailing spaces */
420             tableItem = new QTableWidgetItem(field, 1);
421             /* Possible flags are Qt::ItemFlags flag = Qt::ItemIsSelectable | Qt::ItemIsEditablex
422              *  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable 
423              *  | Qt::ItemIsEnabled | Qt::ItemIsTristate; */
424             tableItem->setForeground(blackBrush);
425             /* Just in case a column ever gets added */
426             if (column == 0) {
427                Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
428                tableItem->setFlags(flag);
429                tableItem->setData(Qt::UserRole, QVariant(directory));
430                fileTable->setItem(row, column, tableItem);
431                m_fileCheckStateList.append(Qt::Unchecked);
432                tableItem->setCheckState(Qt::Unchecked);
433             }
434             column++;
435          }
436          row++;
437       }
438       fileTable->setRowCount(row);
439    }
440    fileTable->resizeColumnsToContents();
441    fileTable->resizeRowsToContents();
442    fileTable->verticalHeader()->hide();
443    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
444            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
445    if (mainWin->m_rtDirCurICDebug) Pmsg0(000, "will update file table checks\n");
446    updateFileTableChecks();
447 }
448
449 /*
450  * Function to populate the version table
451  */
452 void restoreTree::fileCurrentItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
453 {
454    if (fileTableItem == NULL)
455       return;
456
457    m_versionCheckStateList.clear();
458    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
459            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
460
461    QString file = fileTableItem->text();
462    versionFileLabel->setText(file);
463    QString directory = fileTableItem->data(Qt::UserRole).toString();
464
465    QBrush blackBrush(Qt::black);
466    QString cmd = 
467       "SELECT Job.JobId AS JobId, Job.Level AS Type, Job.EndTime AS EndTime, File.Md5 AS MD5, File.FileId AS FileId"
468       " FROM File"
469       " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
470       " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
471       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
472       " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "'"
473       " AND Job.Jobid IN (" + m_jobQuery + ")"
474       " ORDER BY Job.EndTime DESC";
475
476    QStringList headerlist = (QStringList() << "Job Id" << "Type" << "End Time" << "Md5" << "FileId");
477    versionTable->clear();
478    versionTable->setColumnCount(headerlist.size());
479    versionTable->setHorizontalHeaderLabels(headerlist);
480
481    if (mainWin->m_sqlDebug) {
482       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
483    }
484    QStringList results;
485    if (m_console->sql_cmd(cmd, results)) {
486    
487       QTableWidgetItem* tableItem;
488       QString field;
489       QStringList fieldlist;
490       versionTable->setRowCount(results.size());
491
492       int row = 0;
493       /* Iterate through the record returned from the query */
494       foreach (QString resultline, results) {
495          fieldlist = resultline.split("\t");
496          int column = 0;
497          /* remove directory */
498          if (fieldlist[0].trimmed() != "") {
499             /* Iterate through fields in the record */
500             foreach (field, fieldlist) {
501                field = field.trimmed();  /* strip leading & trailing spaces */
502                tableItem = new QTableWidgetItem(field, 1);
503                tableItem->setFlags(0);
504                tableItem->setForeground(blackBrush);
505                tableItem->setData(Qt::UserRole, QVariant(directory));
506                versionTable->setItem(row, column, tableItem);
507
508                if (column == 0) {
509                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
510                   tableItem->setFlags(flag);
511                   m_versionCheckStateList.append(Qt::Unchecked);
512                   tableItem->setCheckState(Qt::Unchecked);
513                }
514                column++;
515             }
516             row++;
517          }
518       }
519    }
520    versionTable->resizeColumnsToContents();
521    versionTable->resizeRowsToContents();
522    versionTable->verticalHeader()->hide();
523    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
524            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
525    updateVersionTableChecks();
526 }
527
528 /*
529  * Save user settings associated with this page
530  */
531 void restoreTree::writeSettings()
532 {
533    QSettings settings(m_console->m_dir->name(), "bat");
534    settings.beginGroup("RestoreTree");
535    settings.setValue("splitterSizes", splitter->saveState());
536    settings.endGroup();
537 }
538
539 /*
540  * Read and restore user settings associated with this page
541  */
542 void restoreTree::readSettings()
543 {
544    QSettings settings(m_console->m_dir->name(), "bat");
545    settings.beginGroup("RestoreTree");
546    splitter->restoreState(settings.value("splitterSizes").toByteArray());
547    settings.endGroup();
548 }
549
550 /*
551  * This is a funcion to accomplish the one thing I struggled to figure out what
552  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
553  * work fast after changing from svg to png file for graphic.
554  */
555 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
556 {
557    int childCount = item->childCount();
558    for (int i=0; i<childCount; i++) {
559       QTreeWidgetItem *child = item->child(i);
560       if (child->icon(0).isNull())
561          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
562    }
563 }
564
565 /*
566  * I wanted a table to show what jobs meet the criterion and are being used to
567  * populate the directory tree and file and version tables.
568  */
569 void restoreTree::populateJobTable()
570 {
571    QBrush blackBrush(Qt::black);
572    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Type");
573    jobTable->clear();
574    jobTable->setColumnCount(headerlist.size());
575    jobTable->setHorizontalHeaderLabels(headerlist);
576    QString jobQuery =
577       "SELECT Job.Jobid AS Id, Job.EndTime AS EndTime, Job.Level AS Level"
578       " FROM Job" + m_jobQueryPart +
579       " ORDER BY Job.EndTime DESC";
580    if (mainWin->m_sqlDebug) {
581       Pmsg1(000, "Query cmd : %s\n", jobQuery.toUtf8().data());
582    }
583
584    QStringList results;
585    if (m_console->sql_cmd(jobQuery, results)) {
586    
587       QTableWidgetItem* tableItem;
588       QString field;
589       QStringList fieldlist;
590       jobTable->setRowCount(results.size());
591
592       int row = 0;
593       /* Iterate through the record returned from the query */
594       foreach (QString resultline, results) {
595          fieldlist = resultline.split("\t");
596          int column = 0;
597          /* remove directory */
598          if (fieldlist[0].trimmed() != "") {
599             /* Iterate through fields in the record */
600             foreach (field, fieldlist) {
601                field = field.trimmed();  /* strip leading & trailing spaces */
602                tableItem = new QTableWidgetItem(field, 1);
603                tableItem->setFlags(0);
604                tableItem->setForeground(blackBrush);
605                jobTable->setItem(row, column, tableItem);
606                if (column == 0) {
607                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
608                   tableItem->setFlags(flag);
609                   tableItem->setCheckState(Qt::Checked);
610                   tableItem->setBackground(Qt::green);
611                }
612                column++;
613             }
614             row++;
615          }
616       }
617    }
618    jobTable->resizeColumnsToContents();
619    jobTable->resizeRowsToContents();
620    jobTable->verticalHeader()->hide();
621 }
622
623 /*
624  * When a directory item is "changed" check the state of the checkable item
625  * to see if it is different than what it was which is stored in Qt::UserRole
626  * of the 2nd column, column 1, of the tree widget.
627  */
628 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, int /*column*/)
629 {
630    Qt::CheckState prevState = (Qt::CheckState)item->data(1, Qt::UserRole).toInt();
631    Qt::CheckState curState = item->checkState(0);
632    QTreeWidgetItem* parent = item->parent();
633    Qt::CheckState parState;
634    if (parent) parState = parent->checkState(0);
635    else parState = (Qt::CheckState)3;
636    if (mainWin->m_rtDirICDebug) {
637       QString msg = QString("directory item OBJECT has changed prev=%1 cur=%2 par=%3 dir=%4\n")
638          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
639       Pmsg1(000, "%s", msg.toUtf8().data()); }
640    /* I only care when the check state changes */
641    if (prevState == curState) {
642       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning Early\n");
643       return;
644    }
645
646    if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (parState != Qt::Unchecked)) {
647       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::PartiallyChecked\n");
648       directoryTreeDisconnectedSet(item, Qt::PartiallyChecked);
649       curState = Qt::PartiallyChecked;
650    }
651    if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked)) {
652       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::Unchecked\n");
653       directoryTreeDisconnectedSet(item, Qt::Unchecked);
654       curState = Qt::Unchecked;
655    }
656    if (mainWin->m_rtDirICDebug) {
657       QString msg = QString("directory item CHECKSTATE has changed prev=%1 cur=%2 par=%3 dir=%4\n")
658          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
659       Pmsg1(000, "%s", msg.toUtf8().data()); }
660
661    item->setData(1, Qt::UserRole, QVariant(curState));
662    Qt::CheckState childState = curState;
663    if (childState == Qt::Checked)
664       childState = Qt::PartiallyChecked;
665    setCheckofChildren(item, childState);
666
667    /* Remove items from the exception lists.  The multi exception list is my index
668     * of what exceptions can be removed when the directory is known*/
669    QString directory = item->data(0, Qt::UserRole).toString();
670    QStringList fullPathList = m_fileExceptionMulti.values(directory);
671    int fullPathListCount = fullPathList.count();
672    if ((mainWin->m_rtDirICDebug) && fullPathListCount) Pmsg2(000, "Will attempt to remove file exceptions for %s count %i\n", directory.toUtf8().data(), fullPathListCount);
673    foreach (QString fullPath, fullPathList) {
674       /* If there is no value in the hash for the key fullPath a value of 3 will be returned
675        * which will match no Qt::xxx values */
676       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
677       if (mainWin->m_rtDirICDebug) Pmsg2(000, "hashState=%i childState=%i\n", hashState, childState);
678       if (hashState == Qt::Unchecked) {
679          fileExceptionRemove(fullPath, directory);
680          m_versionExceptionHash.remove(fullPath);
681          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal A\n");
682       }
683       if (hashState == Qt::Checked) {
684          fileExceptionRemove(fullPath, directory);
685          m_versionExceptionHash.remove(fullPath);
686          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal B\n");
687       }
688    }
689
690    if (item == directoryTree->currentItem()) {
691       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Will attempt to update File Table Checks\n");
692       updateFileTableChecks();
693       versionTable->clear();
694       versionTable->setRowCount(0);
695       versionTable->setColumnCount(0);
696    }
697    if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning At End\n");
698 }
699
700 /*
701  * When a directory item check state is changed, this function iterates through
702  * all subdirectories and sets all to the passed state, which is either partially
703  * checked or unchecked.
704  */
705 void restoreTree::setCheckofChildren(QTreeWidgetItem *item, Qt::CheckState state)
706 {
707    int childCount;
708    childCount = item->childCount();
709    for (int i=0; i<childCount; i++) {
710       QTreeWidgetItem *child = item->child(i);
711       child->setData(1, Qt::UserRole, QVariant(state));
712       child->setCheckState(0, state);
713       setCheckofChildren(child, state);
714    }
715 }
716
717 /*
718  * When a File Table Item is "changed" check to see if the state of the checkable
719  * item has changed which is stored in m_fileCheckStateList
720  * If changed store in a hash m_fileExceptionHash that whether this file should be
721  * restored or not.
722  * Called as a slot, connected after populated (after directory current changed called)
723  */
724 void restoreTree::fileTableItemChanged(QTableWidgetItem *item)
725 {
726    /* get the previous and current check states */
727    int row = fileTable->row(item);
728    Qt::CheckState prevState;
729    /* prevent a segfault */
730    prevState = m_fileCheckStateList[row];
731    Qt::CheckState curState = item->checkState();
732
733    /* deterimine the default state from the state of the directory */
734    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
735    Qt::CheckState dirState = (Qt::CheckState)dirTreeItem->data(1, Qt::UserRole).toInt();
736    Qt::CheckState defState = Qt::PartiallyChecked;
737    if (dirState == Qt::Unchecked) defState = Qt::Unchecked;
738
739    /* determine if it is already in the m_fileExceptionHash */
740    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
741    QString file = item->text();
742    QString fullPath = directory + file;
743    Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
744    int verJobNum = m_versionExceptionHash.value(fullPath, 0);
745
746    if (mainWin->m_rtFileTabICDebug) {
747       QString msg = QString("filerow=%1 prev=%2 cur=%3 def=%4 hash=%5 dir=%6 verJobNum=%7\n")
748          .arg(row).arg(prevState).arg(curState).arg(defState).arg(hashState).arg(dirState).arg(verJobNum);
749       Pmsg1(000, "%s", msg.toUtf8().data()); }
750
751    /* Remove the hash if currently checked previously unchecked and directory is checked or partial */
752    if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (dirState == Qt::Unchecked)) {
753       /* it can behave as defaulted so current of unchecked is fine */
754       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
755       fileExceptionRemove(fullPath, directory);
756       m_versionExceptionHash.remove(fullPath);
757    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
758       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionInsert here\n");
759       fileExceptionInsert(fullPath, directory, Qt::Unchecked);
760    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0) && (defState == Qt::PartiallyChecked)) {
761       /* filerow=2 prev=0 cur=2 def=1 hash=0 dir=2 verJobNum=0 */
762       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove here\n");
763       fileExceptionRemove(fullPath, directory);
764    } else if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (defState == Qt::PartiallyChecked) && (verJobNum != 0) && (hashState == Qt::Checked)) {
765       /* Check dir, check version, attempt uncheck in file
766        * filerow=4 prev=2 cur=0 def=1 hash=2 dir=2 verJobNum=53 */
767       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
768       fileExceptionRemove(fullPath, directory);
769       m_versionExceptionHash.remove(fullPath);
770    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
771       /* filerow=0 prev=0 cur=2 def=1 hash=0 dirState=2 verJobNum */
772       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will Not remove here\n");
773    } else if (prevState != curState) {
774       if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  THE STATE OF THE Check has changed, Setting StateList[%i] to %i\n", row, curState);
775       /* A user did not set the check state to Partially checked, ignore if so */
776       if (curState != Qt::PartiallyChecked) {
777          if ((defState == Qt::Unchecked) && (prevState == Qt::PartiallyChecked) && (curState == Qt::Unchecked)) {
778             if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "  got here\n");
779          } else {
780             if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
781             fileExceptionInsert(fullPath, directory, curState);
782          }
783       } else {
784          if (mainWin->m_rtFileTabICDebug) Pmsg1(000, "Removing version hash for %s\n", fullPath.toUtf8().data());
785          /* programattically been changed back to a default state of Qt::PartiallyChecked remove the version hash here */
786          m_versionExceptionHash.remove(fullPath);
787       }
788    }
789
790    updateFileTableChecks();
791    updateVersionTableChecks();
792 }
793
794 /*
795  * function to insert keys and values to both m_fileExceptionHash and m_fileExceptionMulti
796  */
797 void restoreTree::fileExceptionInsert(QString &fullPath, QString &direcotry, Qt::CheckState state)
798 {
799    m_fileExceptionHash.insert(fullPath, state);
800    m_fileExceptionMulti.insert(direcotry, fullPath);
801    directoryIconStateInsert(fullPath, state);
802 }
803
804 /*
805  * function to remove keys from both m_fileExceptionHash and m_fileExceptionMulti
806  */
807 void restoreTree::fileExceptionRemove(QString &fullPath, QString &directory)
808 {
809    m_fileExceptionHash.remove(fullPath);
810    /* pull the list of values in the multi */
811    QStringList fullPathList = m_fileExceptionMulti.values(directory);
812    /* get the index of the fullpath to remove */
813    int index = fullPathList.indexOf(fullPath);
814    if (index != -1) {
815       /* remove the desired item in the list */
816       fullPathList.removeAt(index);
817       /* remove the entire list from the multi */
818       m_fileExceptionMulti.remove(directory);
819       /* readd the remaining */
820       foreach (QString fp, fullPathList) {
821          m_fileExceptionMulti.insert(directory, fp);
822       }
823    }
824    directoryIconStateRemove();
825 }
826
827 /*
828  * Overloaded function to be called from the slot and from other places to set the state
829  * of the check marks in the version table
830  */
831 void restoreTree::versionTableItemChanged(QTableWidgetItem *item)
832 {
833    /* get the previous and current check states */
834    int row = versionTable->row(item);
835    QTableWidgetItem *colZeroItem = versionTable->item(row, 0);
836    Qt::CheckState prevState = m_versionCheckStateList[row];
837    Qt::CheckState curState = (Qt::CheckState)colZeroItem->checkState();
838    m_versionCheckStateList[row] = curState;
839
840    /* deterimine the default state from the state of the file */
841    QTableWidgetItem *fileTableItem = fileTable->currentItem();
842    Qt::CheckState fileState = (Qt::CheckState)fileTableItem->checkState();
843
844    /* determine the default state */
845    Qt::CheckState defState;
846    if (row == 0) {
847       defState = Qt::PartiallyChecked;
848       if (fileState == Qt::Unchecked)
849          defState = Qt::Unchecked;
850    }
851    else
852       defState = Qt::Unchecked;
853
854    /* determine if it is already in the versionExceptionHash */
855    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
856    Qt::CheckState dirState = directoryTree->currentItem()->checkState(0);
857    QString file = fileTableItem->text();
858    QString fullPath = directory + file;
859    int thisJobNum = colZeroItem->text().toInt();
860    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
861
862    if (mainWin->m_rtVerTabICDebug) {
863       QString msg = QString("versrow=%1 prev=%2 cur=%3 def=%4 dir=%5 hashJobNum=%6 thisJobNum=%7 filestate=%8 fec=%9 vec=%10\n")
864          .arg(row).arg(prevState).arg(curState).arg(defState).arg(dirState).arg(hashJobNum).arg(thisJobNum).arg(fileState)
865          .arg(m_fileExceptionHash.count()).arg(m_versionExceptionHash.count());
866       Pmsg1(000, "%s", msg.toUtf8().data()); }
867    /* if changed from partially checked to checked, make it unchecked */
868    if ((curState == Qt::Checked) && (row == 0) && (fileState == Qt::Unchecked)) {
869       if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "Setting to Qt::Checked\n");
870       fileTableItem->setCheckState(Qt::Checked);
871    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (row == 0) && (fileState == Qt::Checked) && (dirState == Qt::Unchecked)) {
872       //versrow=0 prev=1 cur=2 def=1 dir=0 hashJobNum=0 thisJobNum=64 filestate=2 fec=1 vec=0
873       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "fileExceptionRemove %s, %i\n", fullPath.toUtf8().data());
874       fileExceptionRemove(fullPath, directory);
875    } else if ((curState == Qt::Checked) && (row == 0) && (hashJobNum != 0) && (dirState != Qt::Unchecked)) {
876       //versrow=0 prev=0 cur=2 def=1 dir=2 hashJobNum=53 thisJobNum=64 filestate=2 fec=1 vec=1
877       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
878       m_versionExceptionHash.remove(fullPath);
879       fileExceptionRemove(fullPath, directory);
880    } else if ((curState == Qt::Checked) && (row == 0)) {
881       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
882       m_versionExceptionHash.remove(fullPath);
883    } else if (prevState != curState) {
884       if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "  THE STATE OF THE version Check has changed, Setting StateList[%i] to %i\n", row, curState);
885       if ((curState == Qt::Checked) || (curState == Qt::PartiallyChecked) && (row != 0)) {
886          if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_versionExceptionHash %s, %i\n", fullPath.toUtf8().data(), thisJobNum);
887          m_versionExceptionHash.insert(fullPath, thisJobNum);
888          if (fileState != Qt::Checked) {
889             if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
890             fileExceptionInsert(fullPath, directory, curState);
891          }
892       } else {
893          if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "got here\n");
894       }
895    } else {
896      if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "no conditions met\n");
897    }
898
899    updateFileTableChecks();
900    updateVersionTableChecks();
901 }
902
903 /*
904  * Simple function to set the check state in the file table by disconnecting the
905  * signal/slot the setting then reconnecting the signal/slot
906  */
907 void restoreTree::fileTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state, bool color)
908 {
909    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
910            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
911    item->setCheckState(state);
912    if (color) item->setBackground(Qt::yellow);
913    else item->setBackground(Qt::white);
914    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
915            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
916 }
917
918 /*
919  * Simple function to set the check state in the version table by disconnecting the
920  * signal/slot the setting then reconnecting the signal/slot
921  */
922 void restoreTree::versionTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state)
923 {
924    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
925            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
926    item->setCheckState(state);
927    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
928            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
929 }
930
931 /*
932  * Simple function to set the check state in the directory tree by disconnecting the
933  * signal/slot the setting then reconnecting the signal/slot
934  */
935 void restoreTree::directoryTreeDisconnectedSet(QTreeWidgetItem *item, Qt::CheckState state)
936 {
937    disconnect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
938            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
939    item->setCheckState(0, state);
940    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
941            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
942 }
943
944 /*
945  * Simplify the updating of the check state in the File table by iterating through
946  * each item in the file table to determine it's appropriate state.
947  * !! Will probably want to concoct a way to do this without iterating for the possibility
948  * of the very large directories.
949  */
950 void restoreTree::updateFileTableChecks()
951 {
952    /* deterimine the default state from the state of the directory */
953    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
954    Qt::CheckState dirState = dirTreeItem->checkState(0);
955
956    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
957
958    /* Update the items in the version table */
959    int rcnt = fileTable->rowCount();
960    for (int row=0; row<rcnt; row++) {
961       QTableWidgetItem* item = fileTable->item(row, 0);
962
963       Qt::CheckState curState = item->checkState();
964       Qt::CheckState newState = Qt::PartiallyChecked;
965       if (dirState == Qt::Unchecked) newState = Qt::Unchecked;
966
967       /* determine if it is already in the m_fileExceptionHash */
968       QString file = item->text();
969       QString fullPath = dirName + file;
970       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
971       int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
972
973       if (hashState != 3) newState = hashState;
974
975       if (mainWin->m_rtUpdateFTDebug) {
976          QString msg = QString("file row=%1 cur=%2 hash=%3 new=%4 dirState=%5\n")
977             .arg(row).arg(curState).arg(hashState).arg(newState).arg(dirState);
978          Pmsg1(000, "%s", msg.toUtf8().data());
979       }
980
981       bool docolor = false;
982       if (hashJobNum != 0) docolor = true;
983       bool isyellow = item->background().color() == QColor(Qt::yellow);
984       if ((newState != curState) || (hashState == 3) || ((isyellow && !docolor) || (!isyellow && docolor)))
985          fileTableDisconnectedSet(item, newState, docolor);
986       m_fileCheckStateList[row] = newState;
987    }
988 }
989
990 /*
991  * Simplify the updating of the check state in the Version table by iterating through
992  * each item in the file table to determine it's appropriate state.
993  */
994 void restoreTree::updateVersionTableChecks()
995 {
996    /* deterimine the default state from the state of the directory */
997    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
998    Qt::CheckState dirState = dirTreeItem->checkState(0);
999    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1000
1001    /* deterimine the default state from the state of the file */
1002    QTableWidgetItem *fileTableItem = fileTable->item(fileTable->currentRow(), 0);
1003    Qt::CheckState fileState = fileTableItem->checkState();
1004    QString file = fileTableItem->text();
1005    QString fullPath = dirName + file;
1006    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1007
1008    /* Update the items in the version table */
1009    int cnt = versionTable->rowCount();
1010    for (int row=0; row<cnt; row++) {
1011       QTableWidgetItem* item = versionTable->item(row, 0);
1012
1013       Qt::CheckState curState = item->checkState();
1014       Qt::CheckState newState = Qt::Unchecked;
1015
1016       if ((row == 0) && (fileState != Qt::Unchecked) && (hashJobNum == 0))
1017          newState = Qt::PartiallyChecked;
1018       /* determine if it is already in the versionExceptionHash */
1019       if (hashJobNum) {
1020          int thisJobNum = item->text().toInt();
1021          if (thisJobNum == hashJobNum)
1022             newState = Qt::Checked;
1023       }
1024       if (mainWin->m_rtChecksDebug) {
1025          QString msg = QString("ver row=%1 cur=%2 hashJobNum=%3 new=%4 dirState=%5\n")
1026             .arg(row).arg(curState).arg(hashJobNum).arg(newState).arg(dirState);
1027          Pmsg1(000, "%s", msg.toUtf8().data());
1028       }
1029       if (newState != curState)
1030          versionTableDisconnectedSet(item, newState);
1031       m_versionCheckStateList[row] = newState;
1032    }
1033 }
1034
1035 /*
1036  * Quick subroutine to "return via subPaths" a list of subpaths when passed a fullPath
1037  */
1038 void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in)
1039 {
1040    int index;
1041    bool done = false;
1042    QString fullPath = fullPath_in;
1043    QString direct, path;
1044    while (((index = m_slashregex.lastIndexIn(fullPath, -2)) != -1) && (!done)) {
1045       direct = path = fullPath;
1046       path.replace(index+1, fullPath.length()-index-1, "");
1047       direct.replace(0, index+1, "");
1048       if (false) {
1049          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1050                     .arg(fullPath.length()).arg(index).arg(path).arg(direct);
1051          Pmsg0(000, msg.toUtf8().data());
1052       }
1053       fullPath = path;
1054       subPaths.append(fullPath);
1055    }
1056 }
1057
1058 /*
1059  * A Function to set the icon state and insert a record into
1060  * m_directoryIconStateHash when an exception is added by the user
1061  */
1062 void restoreTree::directoryIconStateInsert(QString &fullPath, Qt::CheckState excpState)
1063 {
1064    QStringList paths;
1065    fullPathtoSubPaths(paths, fullPath);
1066    /* an exception that causes the item in the file table to be "Checked" has occured */
1067    if (excpState == Qt::Checked) {
1068       bool foundAsUnChecked = false;
1069       QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1070       if (firstItem) {
1071          if (firstItem->checkState(0) == Qt::Unchecked)
1072             foundAsUnChecked = true;
1073       }
1074       if (foundAsUnChecked) {
1075           /* as long as directory item is Unchecked, set icon state to "green check" */
1076          bool done = false;
1077          QListIterator<QString> siter(paths);
1078          while (siter.hasNext() && !done) {
1079             QString path = siter.next();
1080             QTreeWidgetItem *item = m_dirPaths.value(path);
1081             if (item) {
1082                if (item->checkState(0) != Qt::Unchecked)
1083                   done = true;
1084                else {
1085                   directorySetIcon(1, FolderGreenChecked, path, item);
1086                   if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1087                }
1088             }
1089          }
1090       } else {
1091          /* if it is partially checked or fully checked insert green Check until a unchecked is found in the path */
1092          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert Aqua %s\n", paths[0].toUtf8().data());
1093          bool done = false;
1094          QListIterator<QString> siter(paths);
1095          while (siter.hasNext() && !done) {
1096             QString path = siter.next();
1097             QTreeWidgetItem *item = m_dirPaths.value(path);
1098             if (item) {  /* if the directory item is checked, set icon state to unchecked "green check" */
1099                if (item->checkState(0) == Qt::Checked)
1100                   done = true;
1101                directorySetIcon(1, FolderGreenChecked, path, item);
1102                if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1103             }
1104          }
1105       }
1106    }
1107    /* an exception that causes the item in the file table to be "Unchecked" has occured */
1108    if (excpState == Qt::Unchecked) {
1109       bool done = false;
1110       QListIterator<QString> siter(paths);
1111       while (siter.hasNext() && !done) {
1112          QString path = siter.next();
1113          QTreeWidgetItem *item = m_dirPaths.value(path);
1114          if (item) {  /* if the directory item is checked, set icon state to unchecked "white check" */
1115             if (item->checkState(0) == Qt::Checked)
1116                done = true;
1117             directorySetIcon(1, FolderWhiteChecked, path, item);
1118             if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1119          }
1120       }
1121    }
1122 }
1123
1124 /*
1125  * A function to set the icon state back to "folder" and to remove a record from
1126  * m_directoryIconStateHash when an exception is removed by a user.
1127  */
1128 void restoreTree::directoryIconStateRemove()
1129 {
1130    QHash<QString, int> shouldBeIconStateHash;
1131    /* First determine all paths with icons that should be checked with m_fileExceptionHash */
1132    /* Use iterator tera to iterate through m_fileExceptionHash */
1133    QHashIterator<QString, Qt::CheckState> tera(m_fileExceptionHash);
1134    while (tera.hasNext()) {
1135       tera.next();
1136       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Alpha Key %s value %i\n", tera.key().toUtf8().data(), tera.value());
1137
1138       QString keyPath = tera.key();
1139       Qt::CheckState state = tera.value();
1140
1141       QStringList paths;
1142       fullPathtoSubPaths(paths, keyPath);
1143       /* if the state of the item in m_fileExceptionHash is checked 
1144        * each of the subpaths should be "Checked Green" */
1145       if (state == Qt::Checked) {
1146
1147          bool foundAsUnChecked = false;
1148          QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1149          if (firstItem) {
1150             if (firstItem->checkState(0) == Qt::Unchecked)
1151                foundAsUnChecked = true;
1152          }
1153          if (foundAsUnChecked) {
1154             /* The right most directory is Unchecked, iterate leftwards
1155              * as long as directory item is Unchecked, set icon state to "green check" */
1156             bool done = false;
1157             QListIterator<QString> siter(paths);
1158             while (siter.hasNext() && !done) {
1159                QString path = siter.next();
1160                QTreeWidgetItem *item = m_dirPaths.value(path);
1161                if (item) {
1162                   if (item->checkState(0) != Qt::Unchecked)
1163                      done = true;
1164                   else {
1165                      shouldBeIconStateHash.insert(path, FolderGreenChecked);
1166                      if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1167                   }
1168                }
1169             }
1170          }
1171          else {
1172             /* The right most directory is Unchecked, iterate leftwards
1173              * until directory item is Checked, set icon state to "green check" */
1174             bool done = false;
1175             QListIterator<QString> siter(paths);
1176             while (siter.hasNext() && !done) {
1177                QString path = siter.next();
1178                QTreeWidgetItem *item = m_dirPaths.value(path);
1179                if (item) {
1180                   if (item->checkState(0) == Qt::Checked)
1181                      done = true;
1182                   shouldBeIconStateHash.insert(path, FolderGreenChecked);
1183                }
1184             }
1185          }
1186       }
1187       /* if the state of the item in m_fileExceptionHash is UNChecked
1188        * each of the subpaths should be "Checked white" until the tree item
1189        * which represents that path is Qt::Checked */
1190       if (state == Qt::Unchecked) {
1191          bool done = false;
1192          QListIterator<QString> siter(paths);
1193          while (siter.hasNext() && !done) {
1194             QString path = siter.next();
1195             QTreeWidgetItem *item = m_dirPaths.value(path);
1196             if (item) {
1197                if (item->checkState(0) == Qt::Checked)
1198                   done = true;
1199                shouldBeIconStateHash.insert(path, FolderWhiteChecked);
1200             }
1201          }
1202       }
1203    }
1204    /* now iterate through m_directoryIconStateHash which are the items that are checked
1205     * and remove all of those that are not in shouldBeIconStateHash */
1206    QHashIterator<QString, int> iter(m_directoryIconStateHash);
1207    while (iter.hasNext()) {
1208       iter.next();
1209       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Beta Key %s value %i\n", iter.key().toUtf8().data(), iter.value());
1210
1211       QString keyPath = iter.key();
1212       if (shouldBeIconStateHash.value(keyPath)) {
1213          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "WAS found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1214          //newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1215          int newval = shouldBeIconStateHash.value(keyPath);
1216          newval = ~newval;
1217          newval = newval & FolderBothChecked;
1218          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1219          if (item)
1220             directorySetIcon(0, newval, keyPath, item);
1221       } else {
1222          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "NOT found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1223          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1224          if (item)
1225             directorySetIcon(0, FolderBothChecked, keyPath, item);
1226             //item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
1227             //m_directoryIconStateHash.remove(keyPath);
1228       }
1229    }
1230 }
1231
1232 void restoreTree::directorySetIcon(int operation, int change, QString &path, QTreeWidgetItem* item) {
1233    int newval;
1234    /* we are adding a check type white or green */
1235    if (operation > 0) {
1236       /* get the old val and "bitwise OR" with the change */
1237       newval = m_directoryIconStateHash.value(path, FolderUnchecked) | change;
1238       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1239       m_directoryIconStateHash.insert(path, newval);
1240    } else {
1241    /* we are removing a check type white or green */
1242       newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1243       if (newval == 0) {
1244          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Removing from m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1245          m_directoryIconStateHash.remove(path);
1246       }
1247       else {
1248          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1249          m_directoryIconStateHash.insert(path, newval);
1250       }
1251    }
1252    if (newval == FolderUnchecked)
1253       item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
1254    else if (newval == FolderGreenChecked)
1255       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderchecked.png")));
1256    else if (newval == FolderWhiteChecked)
1257       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderunchecked.png")));
1258    else if (newval == FolderBothChecked)
1259       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderbothchecked.png")));
1260 }
1261
1262 /*
1263  * Test Button
1264  */
1265 void restoreTree::testButtonPushed()
1266 {
1267    QMultiHash<int, QString> versionFilesMulti;
1268    QHash <QString, bool> fullPathDone;
1269    QHash <QString, int> fileIndexHash;
1270    if ((mainWin->m_rtRestore1Debug) || (mainWin->m_rtRestore2Debug) || (mainWin->m_rtRestore3Debug))
1271       Pmsg0(000, "In restoreTree::testButtonPushed\n");
1272    /* Use a tree widget item iterator filtering for Checked Items */
1273    QTreeWidgetItemIterator diter(directoryTree, QTreeWidgetItemIterator::Checked);
1274    while (*diter) {
1275       QString directory = (*diter)->data(0, Qt::UserRole).toString();
1276       if (mainWin->m_rtRestore1Debug)
1277       Pmsg1(000, "Directory Checked=\"%s\"\n", directory.toUtf8().data());
1278       /* With a checked directory, query for the files in the directory */
1279
1280       QString cmd =
1281          "SELECT t1.Filename AS Filename, t1.JobId AS JobId, File.FileIndex AS FileIndex"
1282          " FROM"
1283          " ( SELECT Filename.Name AS Filename, MAX(Job.JobId) AS JobId"
1284            " FROM File"
1285              " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1286              " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1287              " LEFT OUTER JOIN Job ON (Job.JobId=File.JobId)"
1288            " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1289            "  AND Job.Jobid IN (" + m_jobQuery + ")"
1290            " GROUP BY Filename.Name"
1291          ") t1, File "
1292            " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1293            " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1294            " LEFT OUTER JOIN Job ON (Job.JobId=File.JobId)"
1295          " WHERE"
1296            " Path.Path='" + directory + "'"
1297            " AND Filename.Name=t1.Filename"
1298            " AND Job.Jobid=t1.JobId"
1299          " ORDER BY Filename";
1300
1301       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1302       QStringList results;
1303       if (m_console->sql_cmd(cmd, results)) {
1304       
1305          QStringList fieldlist;
1306    
1307          int row = 0;
1308          /* Iterate through the record returned from the query */
1309          foreach (QString resultline, results) {
1310             /* Iterate through fields in the record */
1311             int column = 0;
1312             QString fullPath = "";
1313             Qt::CheckState fileExcpState = (Qt::CheckState)4;
1314             fieldlist = resultline.split("\t");
1315             int version = 0;
1316             int fileIndex = 0;
1317             foreach (QString field, fieldlist) {
1318                if (column == 0) {
1319                   fullPath = directory + field;
1320                }
1321                if (column == 1) {
1322                   version = field.toInt();
1323                }
1324                if (column == 2) {
1325                   fileIndex = field.toInt();
1326                }
1327                column++;
1328             }
1329             fileExcpState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1330             
1331             int excpVersion = m_versionExceptionHash.value(fullPath, 0);
1332             if (fileExcpState != Qt::Unchecked) {
1333                QString debugtext;
1334                if (excpVersion != 0) {
1335                   debugtext = QString("*E* version=%1").arg(excpVersion);
1336                   version = excpVersion;
1337                   fileIndex = queryFileIndex(fullPath, excpVersion);
1338                } else
1339                   debugtext = QString("___ version=%1").arg(version);
1340                if (mainWin->m_rtRestore1Debug)
1341                   Pmsg2(000, "Restoring %s File %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1342                fullPathDone.insert(fullPath, 1);
1343                fileIndexHash.insert(fullPath, fileIndex);
1344                versionFilesMulti.insert(version, fullPath);
1345             }
1346             row++;
1347          }
1348       }
1349       ++diter;
1350    } /* while (*diter) */
1351
1352    /* There may be some exceptions not accounted for yet with fullPathDone */
1353    QHashIterator<QString, Qt::CheckState> ftera(m_fileExceptionHash);
1354    while (ftera.hasNext()) {
1355       ftera.next();
1356       QString fullPath = ftera.key();
1357       Qt::CheckState state = ftera.value();
1358       if (state != 0) {
1359          /* now we don't want the ones already done */
1360          if (fullPathDone.value(fullPath, 0) == 0) {
1361             int version = m_versionExceptionHash.value(fullPath, 0);
1362             int fileIndex = 0;
1363             QString debugtext = "";
1364             if (version != 0) {
1365                fileIndex = queryFileIndex(fullPath, version);
1366                debugtext = QString("E1* version=%1 fileid=%2").arg(version).arg(fileIndex);
1367             } else {
1368                version = mostRecentVersionfromFullPath(fullPath);
1369                if (version) {
1370                   fileIndex = queryFileIndex(fullPath, version);
1371                   debugtext = QString("E2* version=%1 fileid=%2").arg(version).arg(fileIndex);
1372                } else
1373                   debugtext = QString("Error det vers").arg(version);
1374             }
1375             if (mainWin->m_rtRestore1Debug)
1376                Pmsg2(000, "Restoring %s file %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1377             versionFilesMulti.insert(version, fullPath);
1378             fileIndexHash.insert(fullPath, fileIndex);
1379          } /* if fullPathDone.value(fullPath, 0) == 0 */
1380       } /* if state != 0 */
1381    } /* while ftera.hasNext */
1382
1383    /* now for the final spit out of the versions and lists of files for each version */
1384    QHash<int, int> doneKeys;
1385    QHashIterator<int, QString> vFMiter(versionFilesMulti);
1386    QString tempTable = "";
1387    QList<int> jobList;
1388    while (vFMiter.hasNext()) {
1389       vFMiter.next();
1390       int fversion = vFMiter.key();
1391       /* did not succeed in getting an iterator to work as expected on versionFilesMulti so use doneKeys */
1392       if (doneKeys.value(fversion, 0) == 0) {
1393          if (tempTable == "") {
1394             QSettings settings("www.bacula.org", "bat");
1395             settings.beginGroup("Restore");
1396             int counter = settings.value("Counter", 1).toInt();
1397             settings.setValue("Counter", counter+1);
1398             settings.endGroup();
1399             tempTable = "restore_" + QString("%1").arg(qrand()) + "_" + QString("%1").arg(counter);
1400             QString sqlcmd = "CREATE TEMPORARY TABLE " + tempTable + " (JobId INTEGER, FileIndex INTEGER)";
1401             if (mainWin->m_sqlDebug)
1402                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1403             QStringList results;
1404             if (!m_console->sql_cmd(sqlcmd, results))
1405                Pmsg1(000, "CREATE TABLE FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1406          }
1407
1408          if (mainWin->m_rtRestore2Debug) Pmsg1(000, "Version->%i\n", fversion);
1409          QStringList fullPathList = versionFilesMulti.values(fversion);
1410          /* create the command to perform the restore */
1411          foreach(QString ffullPath, fullPathList) {
1412             int fileIndex = fileIndexHash.value(ffullPath);
1413             if (mainWin->m_rtRestore2Debug) Pmsg2(000, "  file->%s id %i\n", ffullPath.toUtf8().data(), fileIndex);
1414             QString sqlcmd = "INSERT INTO " + tempTable + " (JobId, FileIndex) VALUES (" + QString("%1").arg(fversion) + ", " + QString("%1").arg(fileIndex) + ")";
1415             if (mainWin->m_sqlDebug)
1416                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1417             QStringList results;
1418             if (!m_console->sql_cmd(sqlcmd, results))
1419                Pmsg1(000, "INSERT INTO FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1420          } /* foreach fullPathList */
1421          doneKeys.insert(fversion,1);
1422          jobList.append(fversion);
1423       } /*  if (doneKeys.value(fversion, 0) == 0) */
1424    } /* while (vFMiter.hasNext()) */
1425    if (tempTable != "") {
1426       QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
1427       new restoreTreeRunPage(tempTable, m_prevClientCombo, jobList, pageSelectorTreeWidgetItem);
1428    }
1429 }
1430
1431 int restoreTree::mostRecentVersionfromFullPath(QString &fullPath)
1432 {
1433    int qversion = 0;
1434    QString directory, fileName;
1435    int index = m_slashregex.lastIndexIn(fullPath, -2);
1436    if (index != -1) {
1437       directory = fileName = fullPath;
1438       directory.replace(index+1, fullPath.length()-index-1, "");
1439       fileName.replace(0, index+1, "");
1440       if (false) {
1441          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1442                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1443          Pmsg0(000, msg.toUtf8().data());
1444       }
1445       /* so now we need the latest version from the database */
1446       QString cmd =
1447          "SELECT MAX(Job.JobId)"
1448          " FROM File "
1449          " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1450          " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1451          " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1452          " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1453          " AND Job.Jobid IN (" + m_jobQuery + ")"
1454          " AND Filename.Name='" + fileName + "'"
1455          " GROUP BY Filename.Name";
1456  
1457       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1458       QStringList results;
1459       if (m_console->sql_cmd(cmd, results)) {
1460          QStringList fieldlist;
1461          int row = 0;
1462          /* Iterate through the record returned from the query */
1463          foreach (QString resultline, results) {
1464             /* Iterate through fields in the record */
1465             int column = 0;
1466             fieldlist = resultline.split("\t");
1467             foreach (QString field, fieldlist) {
1468                if (column == 0) {
1469                   qversion = field.toInt();
1470                }
1471                column++;
1472             }
1473             row++;
1474          }
1475       }
1476    } /* if (index != -1) */
1477    return qversion;
1478 }
1479
1480
1481 int restoreTree::queryFileIndex(QString &fullPath, int jobId)
1482 {
1483    int qfileIndex = 0;
1484    QString directory, fileName;
1485    int index = m_slashregex.lastIndexIn(fullPath, -2);
1486    if (index != -1) {
1487       directory = fileName = fullPath;
1488       directory.replace(index+1, fullPath.length()-index-1, "");
1489       fileName.replace(0, index+1, "");
1490       if (false) {
1491          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1492                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1493          Pmsg0(000, msg.toUtf8().data());
1494       }
1495       /* so now we need the latest version from the database */
1496       QString cmd =
1497          "SELECT"
1498           " File.FileIndex"
1499          " FROM File"
1500           " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1501           " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1502           " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1503          " WHERE"
1504           " Path.Path='" + directory + "'"
1505           " AND Filename.Name='" + fileName + "'"
1506           " AND Job.Jobid='" + QString("%1").arg(jobId) + "'"
1507          " GROUP BY File.FileIndex";
1508  
1509       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1510       QStringList results;
1511       if (m_console->sql_cmd(cmd, results)) {
1512          QStringList fieldlist;
1513          int row = 0;
1514          /* Iterate through the record returned from the query */
1515          foreach (QString resultline, results) {
1516             /* Iterate through fields in the record */
1517             int column = 0;
1518             fieldlist = resultline.split("\t");
1519             foreach (QString field, fieldlist) {
1520                if (column == 0) {
1521                   qfileIndex = field.toInt();
1522                }
1523                column++;
1524             }
1525             row++;
1526          }
1527       }
1528    } /* if (index != -1) */
1529    return qfileIndex;
1530 }