]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/restoretree.cpp
Set keyword replacement on files
[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$
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(restoreButton, SIGNAL(pressed()), this, SLOT(restoreButtonPushed()));
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 AS FileName"
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       " ORDER BY FileName";
391  
392    QStringList headerlist = (QStringList() << "File Name");
393    fileTable->clear();
394    /* Also clear the version table here */
395    versionTable->clear();
396    versionFileLabel->setText("");
397    versionTable->setRowCount(0);
398    versionTable->setColumnCount(0);
399    fileTable->setColumnCount(headerlist.size());
400    fileTable->setHorizontalHeaderLabels(headerlist);
401
402    if (mainWin->m_sqlDebug) {
403       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
404    }
405    QStringList results;
406    if (m_console->sql_cmd(cmd, results)) {
407    
408       QTableWidgetItem* tableItem;
409       QString field;
410       QStringList fieldlist;
411       fileTable->setRowCount(results.size());
412
413       int row = 0;
414       /* Iterate through the record returned from the query */
415       foreach (QString resultline, results) {
416          /* Iterate through fields in the record */
417          int column = 0;
418          fieldlist = resultline.split("\t");
419          foreach (field, fieldlist) {
420             field = field.trimmed();  /* strip leading & trailing spaces */
421             tableItem = new QTableWidgetItem(field, 1);
422             /* Possible flags are Qt::ItemFlags flag = Qt::ItemIsSelectable | Qt::ItemIsEditablex
423              *  | Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable 
424              *  | Qt::ItemIsEnabled | Qt::ItemIsTristate; */
425             tableItem->setForeground(blackBrush);
426             /* Just in case a column ever gets added */
427             if (column == 0) {
428                Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
429                tableItem->setFlags(flag);
430                tableItem->setData(Qt::UserRole, QVariant(directory));
431                fileTable->setItem(row, column, tableItem);
432                m_fileCheckStateList.append(Qt::Unchecked);
433                tableItem->setCheckState(Qt::Unchecked);
434             }
435             column++;
436          }
437          row++;
438       }
439       fileTable->setRowCount(row);
440    }
441    fileTable->resizeColumnsToContents();
442    fileTable->resizeRowsToContents();
443    fileTable->verticalHeader()->hide();
444    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
445            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
446    if (mainWin->m_rtDirCurICDebug) Pmsg0(000, "will update file table checks\n");
447    updateFileTableChecks();
448 }
449
450 /*
451  * Function to populate the version table
452  */
453 void restoreTree::fileCurrentItemChanged(QTableWidgetItem *fileTableItem, QTableWidgetItem *)
454 {
455    if (fileTableItem == NULL)
456       return;
457
458    m_versionCheckStateList.clear();
459    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
460            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
461
462    QString file = fileTableItem->text();
463    versionFileLabel->setText(file);
464    QString directory = fileTableItem->data(Qt::UserRole).toString();
465
466    QBrush blackBrush(Qt::black);
467    QString cmd = 
468       "SELECT Job.JobId AS JobId, Job.Level AS Type, Job.EndTime AS EndTime, File.Md5 AS MD5, File.FileId AS FileId"
469       " FROM File"
470       " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
471       " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
472       " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
473       " WHERE Filename.Name='" + file + "' AND Path.Path='" + directory + "'"
474       " AND Job.Jobid IN (" + m_jobQuery + ")"
475       " ORDER BY Job.EndTime DESC";
476
477    QStringList headerlist = (QStringList() << "Job Id" << "Type" << "End Time" << "Md5" << "FileId");
478    versionTable->clear();
479    versionTable->setColumnCount(headerlist.size());
480    versionTable->setHorizontalHeaderLabels(headerlist);
481
482    if (mainWin->m_sqlDebug) {
483       Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
484    }
485    QStringList results;
486    if (m_console->sql_cmd(cmd, results)) {
487    
488       QTableWidgetItem* tableItem;
489       QString field;
490       QStringList fieldlist;
491       versionTable->setRowCount(results.size());
492
493       int row = 0;
494       /* Iterate through the record returned from the query */
495       foreach (QString resultline, results) {
496          fieldlist = resultline.split("\t");
497          int column = 0;
498          /* remove directory */
499          if (fieldlist[0].trimmed() != "") {
500             /* Iterate through fields in the record */
501             foreach (field, fieldlist) {
502                field = field.trimmed();  /* strip leading & trailing spaces */
503                tableItem = new QTableWidgetItem(field, 1);
504                tableItem->setFlags(0);
505                tableItem->setForeground(blackBrush);
506                tableItem->setData(Qt::UserRole, QVariant(directory));
507                versionTable->setItem(row, column, tableItem);
508
509                if (column == 0) {
510                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
511                   tableItem->setFlags(flag);
512                   m_versionCheckStateList.append(Qt::Unchecked);
513                   tableItem->setCheckState(Qt::Unchecked);
514                }
515                column++;
516             }
517             row++;
518          }
519       }
520    }
521    versionTable->resizeColumnsToContents();
522    versionTable->resizeRowsToContents();
523    versionTable->verticalHeader()->hide();
524    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
525            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
526    updateVersionTableChecks();
527 }
528
529 /*
530  * Save user settings associated with this page
531  */
532 void restoreTree::writeSettings()
533 {
534    QSettings settings(m_console->m_dir->name(), "bat");
535    settings.beginGroup("RestoreTree");
536    settings.setValue("splitterSizes", splitter->saveState());
537    settings.endGroup();
538 }
539
540 /*
541  * Read and restore user settings associated with this page
542  */
543 void restoreTree::readSettings()
544 {
545    QSettings settings(m_console->m_dir->name(), "bat");
546    settings.beginGroup("RestoreTree");
547    splitter->restoreState(settings.value("splitterSizes").toByteArray());
548    settings.endGroup();
549 }
550
551 /*
552  * This is a funcion to accomplish the one thing I struggled to figure out what
553  * was taking so long.  It add the icons, but after the tree is made.  Seemed to
554  * work fast after changing from svg to png file for graphic.
555  */
556 void restoreTree::directoryItemExpanded(QTreeWidgetItem *item)
557 {
558    int childCount = item->childCount();
559    for (int i=0; i<childCount; i++) {
560       QTreeWidgetItem *child = item->child(i);
561       if (child->icon(0).isNull())
562          child->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
563    }
564 }
565
566 /*
567  * I wanted a table to show what jobs meet the criterion and are being used to
568  * populate the directory tree and file and version tables.
569  */
570 void restoreTree::populateJobTable()
571 {
572    QBrush blackBrush(Qt::black);
573    QStringList headerlist = (QStringList() << "Job Id" << "End Time" << "Type");
574    jobTable->clear();
575    jobTable->setColumnCount(headerlist.size());
576    jobTable->setHorizontalHeaderLabels(headerlist);
577    QString jobQuery =
578       "SELECT Job.Jobid AS Id, Job.EndTime AS EndTime, Job.Level AS Level"
579       " FROM Job" + m_jobQueryPart +
580       " ORDER BY Job.EndTime DESC";
581    if (mainWin->m_sqlDebug) {
582       Pmsg1(000, "Query cmd : %s\n", jobQuery.toUtf8().data());
583    }
584
585    QStringList results;
586    if (m_console->sql_cmd(jobQuery, results)) {
587    
588       QTableWidgetItem* tableItem;
589       QString field;
590       QStringList fieldlist;
591       jobTable->setRowCount(results.size());
592
593       int row = 0;
594       /* Iterate through the record returned from the query */
595       foreach (QString resultline, results) {
596          fieldlist = resultline.split("\t");
597          int column = 0;
598          /* remove directory */
599          if (fieldlist[0].trimmed() != "") {
600             /* Iterate through fields in the record */
601             foreach (field, fieldlist) {
602                field = field.trimmed();  /* strip leading & trailing spaces */
603                tableItem = new QTableWidgetItem(field, 1);
604                tableItem->setFlags(0);
605                tableItem->setForeground(blackBrush);
606                jobTable->setItem(row, column, tableItem);
607                if (column == 0) {
608                   Qt::ItemFlags flag = Qt::ItemIsUserCheckable | Qt::ItemIsEnabled | Qt::ItemIsTristate;
609                   tableItem->setFlags(flag);
610                   tableItem->setCheckState(Qt::Checked);
611                   tableItem->setBackground(Qt::green);
612                }
613                column++;
614             }
615             row++;
616          }
617       }
618    }
619    jobTable->resizeColumnsToContents();
620    jobTable->resizeRowsToContents();
621    jobTable->verticalHeader()->hide();
622 }
623
624 /*
625  * When a directory item is "changed" check the state of the checkable item
626  * to see if it is different than what it was which is stored in Qt::UserRole
627  * of the 2nd column, column 1, of the tree widget.
628  */
629 void restoreTree::directoryItemChanged(QTreeWidgetItem *item, int /*column*/)
630 {
631    Qt::CheckState prevState = (Qt::CheckState)item->data(1, Qt::UserRole).toInt();
632    Qt::CheckState curState = item->checkState(0);
633    QTreeWidgetItem* parent = item->parent();
634    Qt::CheckState parState;
635    if (parent) parState = parent->checkState(0);
636    else parState = (Qt::CheckState)3;
637    if (mainWin->m_rtDirICDebug) {
638       QString msg = QString("directory item OBJECT has changed prev=%1 cur=%2 par=%3 dir=%4\n")
639          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
640       Pmsg1(000, "%s", msg.toUtf8().data()); }
641    /* I only care when the check state changes */
642    if (prevState == curState) {
643       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning Early\n");
644       return;
645    }
646
647    if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (parState != Qt::Unchecked)) {
648       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::PartiallyChecked\n");
649       directoryTreeDisconnectedSet(item, Qt::PartiallyChecked);
650       curState = Qt::PartiallyChecked;
651    }
652    if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked)) {
653       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Disconnected Setting to Qt::Unchecked\n");
654       directoryTreeDisconnectedSet(item, Qt::Unchecked);
655       curState = Qt::Unchecked;
656    }
657    if (mainWin->m_rtDirICDebug) {
658       QString msg = QString("directory item CHECKSTATE has changed prev=%1 cur=%2 par=%3 dir=%4\n")
659          .arg(prevState).arg(curState).arg(parState).arg(item->text(0));
660       Pmsg1(000, "%s", msg.toUtf8().data()); }
661
662    item->setData(1, Qt::UserRole, QVariant(curState));
663    Qt::CheckState childState = curState;
664    if (childState == Qt::Checked)
665       childState = Qt::PartiallyChecked;
666    setCheckofChildren(item, childState);
667
668    /* Remove items from the exception lists.  The multi exception list is my index
669     * of what exceptions can be removed when the directory is known*/
670    QString directory = item->data(0, Qt::UserRole).toString();
671    QStringList fullPathList = m_fileExceptionMulti.values(directory);
672    int fullPathListCount = fullPathList.count();
673    if ((mainWin->m_rtDirICDebug) && fullPathListCount) Pmsg2(000, "Will attempt to remove file exceptions for %s count %i\n", directory.toUtf8().data(), fullPathListCount);
674    foreach (QString fullPath, fullPathList) {
675       /* If there is no value in the hash for the key fullPath a value of 3 will be returned
676        * which will match no Qt::xxx values */
677       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
678       if (mainWin->m_rtDirICDebug) Pmsg2(000, "hashState=%i childState=%i\n", hashState, childState);
679       if (hashState == Qt::Unchecked) {
680          fileExceptionRemove(fullPath, directory);
681          m_versionExceptionHash.remove(fullPath);
682          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal A\n");
683       }
684       if (hashState == Qt::Checked) {
685          fileExceptionRemove(fullPath, directory);
686          m_versionExceptionHash.remove(fullPath);
687          if (mainWin->m_rtDirICDebug) Pmsg0(000, "Attempted Removal B\n");
688       }
689    }
690
691    if (item == directoryTree->currentItem()) {
692       if (mainWin->m_rtDirICDebug) Pmsg0(000, "Will attempt to update File Table Checks\n");
693       updateFileTableChecks();
694       versionTable->clear();
695       versionTable->setRowCount(0);
696       versionTable->setColumnCount(0);
697    }
698    if (mainWin->m_rtDirICDebug) Pmsg0(000, "Returning At End\n");
699 }
700
701 /*
702  * When a directory item check state is changed, this function iterates through
703  * all subdirectories and sets all to the passed state, which is either partially
704  * checked or unchecked.
705  */
706 void restoreTree::setCheckofChildren(QTreeWidgetItem *item, Qt::CheckState state)
707 {
708    int childCount;
709    childCount = item->childCount();
710    for (int i=0; i<childCount; i++) {
711       QTreeWidgetItem *child = item->child(i);
712       child->setData(1, Qt::UserRole, QVariant(state));
713       child->setCheckState(0, state);
714       setCheckofChildren(child, state);
715    }
716 }
717
718 /*
719  * When a File Table Item is "changed" check to see if the state of the checkable
720  * item has changed which is stored in m_fileCheckStateList
721  * If changed store in a hash m_fileExceptionHash that whether this file should be
722  * restored or not.
723  * Called as a slot, connected after populated (after directory current changed called)
724  */
725 void restoreTree::fileTableItemChanged(QTableWidgetItem *item)
726 {
727    /* get the previous and current check states */
728    int row = fileTable->row(item);
729    Qt::CheckState prevState;
730    /* prevent a segfault */
731    prevState = m_fileCheckStateList[row];
732    Qt::CheckState curState = item->checkState();
733
734    /* deterimine the default state from the state of the directory */
735    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
736    Qt::CheckState dirState = (Qt::CheckState)dirTreeItem->data(1, Qt::UserRole).toInt();
737    Qt::CheckState defState = Qt::PartiallyChecked;
738    if (dirState == Qt::Unchecked) defState = Qt::Unchecked;
739
740    /* determine if it is already in the m_fileExceptionHash */
741    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
742    QString file = item->text();
743    QString fullPath = directory + file;
744    Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
745    int verJobNum = m_versionExceptionHash.value(fullPath, 0);
746
747    if (mainWin->m_rtFileTabICDebug) {
748       QString msg = QString("filerow=%1 prev=%2 cur=%3 def=%4 hash=%5 dir=%6 verJobNum=%7\n")
749          .arg(row).arg(prevState).arg(curState).arg(defState).arg(hashState).arg(dirState).arg(verJobNum);
750       Pmsg1(000, "%s", msg.toUtf8().data()); }
751
752    /* Remove the hash if currently checked previously unchecked and directory is checked or partial */
753    if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (dirState == Qt::Unchecked)) {
754       /* it can behave as defaulted so current of unchecked is fine */
755       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
756       fileExceptionRemove(fullPath, directory);
757       m_versionExceptionHash.remove(fullPath);
758    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
759       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionInsert here\n");
760       fileExceptionInsert(fullPath, directory, Qt::Unchecked);
761    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0) && (defState == Qt::PartiallyChecked)) {
762       /* filerow=2 prev=0 cur=2 def=1 hash=0 dir=2 verJobNum=0 */
763       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove here\n");
764       fileExceptionRemove(fullPath, directory);
765    } else if ((prevState == Qt::Checked) && (curState == Qt::Unchecked) && (defState == Qt::PartiallyChecked) && (verJobNum != 0) && (hashState == Qt::Checked)) {
766       /* Check dir, check version, attempt uncheck in file
767        * filerow=4 prev=2 cur=0 def=1 hash=2 dir=2 verJobNum=53 */
768       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will fileExceptionRemove and m_versionExceptionHash.remove here\n");
769       fileExceptionRemove(fullPath, directory);
770       m_versionExceptionHash.remove(fullPath);
771    } else if ((prevState == Qt::Unchecked) && (curState == Qt::Checked) && (dirState != Qt::Unchecked) && (verJobNum == 0)) {
772       /* filerow=0 prev=0 cur=2 def=1 hash=0 dirState=2 verJobNum */
773       if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "Will Not remove here\n");
774    } else if (prevState != curState) {
775       if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  THE STATE OF THE Check has changed, Setting StateList[%i] to %i\n", row, curState);
776       /* A user did not set the check state to Partially checked, ignore if so */
777       if (curState != Qt::PartiallyChecked) {
778          if ((defState == Qt::Unchecked) && (prevState == Qt::PartiallyChecked) && (curState == Qt::Unchecked)) {
779             if (mainWin->m_rtFileTabICDebug) Pmsg0(000, "  got here\n");
780          } else {
781             if (mainWin->m_rtFileTabICDebug) Pmsg2(000, "  Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
782             fileExceptionInsert(fullPath, directory, curState);
783          }
784       } else {
785          if (mainWin->m_rtFileTabICDebug) Pmsg1(000, "Removing version hash for %s\n", fullPath.toUtf8().data());
786          /* programattically been changed back to a default state of Qt::PartiallyChecked remove the version hash here */
787          m_versionExceptionHash.remove(fullPath);
788       }
789    }
790
791    updateFileTableChecks();
792    updateVersionTableChecks();
793 }
794
795 /*
796  * function to insert keys and values to both m_fileExceptionHash and m_fileExceptionMulti
797  */
798 void restoreTree::fileExceptionInsert(QString &fullPath, QString &direcotry, Qt::CheckState state)
799 {
800    m_fileExceptionHash.insert(fullPath, state);
801    m_fileExceptionMulti.insert(direcotry, fullPath);
802    directoryIconStateInsert(fullPath, state);
803 }
804
805 /*
806  * function to remove keys from both m_fileExceptionHash and m_fileExceptionMulti
807  */
808 void restoreTree::fileExceptionRemove(QString &fullPath, QString &directory)
809 {
810    m_fileExceptionHash.remove(fullPath);
811    /* pull the list of values in the multi */
812    QStringList fullPathList = m_fileExceptionMulti.values(directory);
813    /* get the index of the fullpath to remove */
814    int index = fullPathList.indexOf(fullPath);
815    if (index != -1) {
816       /* remove the desired item in the list */
817       fullPathList.removeAt(index);
818       /* remove the entire list from the multi */
819       m_fileExceptionMulti.remove(directory);
820       /* readd the remaining */
821       foreach (QString fp, fullPathList) {
822          m_fileExceptionMulti.insert(directory, fp);
823       }
824    }
825    directoryIconStateRemove();
826 }
827
828 /*
829  * Overloaded function to be called from the slot and from other places to set the state
830  * of the check marks in the version table
831  */
832 void restoreTree::versionTableItemChanged(QTableWidgetItem *item)
833 {
834    /* get the previous and current check states */
835    int row = versionTable->row(item);
836    QTableWidgetItem *colZeroItem = versionTable->item(row, 0);
837    Qt::CheckState prevState = m_versionCheckStateList[row];
838    Qt::CheckState curState = (Qt::CheckState)colZeroItem->checkState();
839    m_versionCheckStateList[row] = curState;
840
841    /* deterimine the default state from the state of the file */
842    QTableWidgetItem *fileTableItem = fileTable->currentItem();
843    Qt::CheckState fileState = (Qt::CheckState)fileTableItem->checkState();
844
845    /* determine the default state */
846    Qt::CheckState defState;
847    if (row == 0) {
848       defState = Qt::PartiallyChecked;
849       if (fileState == Qt::Unchecked)
850          defState = Qt::Unchecked;
851    }
852    else
853       defState = Qt::Unchecked;
854
855    /* determine if it is already in the versionExceptionHash */
856    QString directory = directoryTree->currentItem()->data(0, Qt::UserRole).toString();
857    Qt::CheckState dirState = directoryTree->currentItem()->checkState(0);
858    QString file = fileTableItem->text();
859    QString fullPath = directory + file;
860    int thisJobNum = colZeroItem->text().toInt();
861    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
862
863    if (mainWin->m_rtVerTabICDebug) {
864       QString msg = QString("versrow=%1 prev=%2 cur=%3 def=%4 dir=%5 hashJobNum=%6 thisJobNum=%7 filestate=%8 fec=%9 vec=%10\n")
865          .arg(row).arg(prevState).arg(curState).arg(defState).arg(dirState).arg(hashJobNum).arg(thisJobNum).arg(fileState)
866          .arg(m_fileExceptionHash.count()).arg(m_versionExceptionHash.count());
867       Pmsg1(000, "%s", msg.toUtf8().data()); }
868    /* if changed from partially checked to checked, make it unchecked */
869    if ((curState == Qt::Checked) && (row == 0) && (fileState == Qt::Unchecked)) {
870       if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "Setting to Qt::Checked\n");
871       fileTableItem->setCheckState(Qt::Checked);
872    } else if ((prevState == Qt::PartiallyChecked) && (curState == Qt::Checked) && (row == 0) && (fileState == Qt::Checked) && (dirState == Qt::Unchecked)) {
873       //versrow=0 prev=1 cur=2 def=1 dir=0 hashJobNum=0 thisJobNum=64 filestate=2 fec=1 vec=0
874       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "fileExceptionRemove %s, %i\n", fullPath.toUtf8().data());
875       fileExceptionRemove(fullPath, directory);
876    } else if ((curState == Qt::Checked) && (row == 0) && (hashJobNum != 0) && (dirState != Qt::Unchecked)) {
877       //versrow=0 prev=0 cur=2 def=1 dir=2 hashJobNum=53 thisJobNum=64 filestate=2 fec=1 vec=1
878       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
879       m_versionExceptionHash.remove(fullPath);
880       fileExceptionRemove(fullPath, directory);
881    } else if ((curState == Qt::Checked) && (row == 0)) {
882       if (mainWin->m_rtVerTabICDebug) Pmsg1(000, "m_versionExceptionHash.remove %s\n", fullPath.toUtf8().data());
883       m_versionExceptionHash.remove(fullPath);
884    } else if (prevState != curState) {
885       if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "  THE STATE OF THE version Check has changed, Setting StateList[%i] to %i\n", row, curState);
886       if ((curState == Qt::Checked) || (curState == Qt::PartiallyChecked) && (row != 0)) {
887          if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_versionExceptionHash %s, %i\n", fullPath.toUtf8().data(), thisJobNum);
888          m_versionExceptionHash.insert(fullPath, thisJobNum);
889          if (fileState != Qt::Checked) {
890             if (mainWin->m_rtVerTabICDebug) Pmsg2(000, "Inserting into m_fileExceptionHash %s, %i\n", fullPath.toUtf8().data(), curState);
891             fileExceptionInsert(fullPath, directory, curState);
892          }
893       } else {
894          if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "got here\n");
895       }
896    } else {
897      if (mainWin->m_rtVerTabICDebug) Pmsg0(000, "no conditions met\n");
898    }
899
900    updateFileTableChecks();
901    updateVersionTableChecks();
902 }
903
904 /*
905  * Simple function to set the check state in the file table by disconnecting the
906  * signal/slot the setting then reconnecting the signal/slot
907  */
908 void restoreTree::fileTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state, bool color)
909 {
910    disconnect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
911            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
912    item->setCheckState(state);
913    if (color) item->setBackground(Qt::yellow);
914    else item->setBackground(Qt::white);
915    connect(fileTable, SIGNAL(itemChanged(QTableWidgetItem *)),
916            this, SLOT(fileTableItemChanged(QTableWidgetItem *)));
917 }
918
919 /*
920  * Simple function to set the check state in the version table by disconnecting the
921  * signal/slot the setting then reconnecting the signal/slot
922  */
923 void restoreTree::versionTableDisconnectedSet(QTableWidgetItem *item, Qt::CheckState state)
924 {
925    disconnect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
926            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
927    item->setCheckState(state);
928    connect(versionTable, SIGNAL(itemChanged(QTableWidgetItem *)),
929            this, SLOT(versionTableItemChanged(QTableWidgetItem *)));
930 }
931
932 /*
933  * Simple function to set the check state in the directory tree by disconnecting the
934  * signal/slot the setting then reconnecting the signal/slot
935  */
936 void restoreTree::directoryTreeDisconnectedSet(QTreeWidgetItem *item, Qt::CheckState state)
937 {
938    disconnect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
939            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
940    item->setCheckState(0, state);
941    connect(directoryTree, SIGNAL(itemChanged(QTreeWidgetItem *, int)),
942            this, SLOT(directoryItemChanged(QTreeWidgetItem *, int)));
943 }
944
945 /*
946  * Simplify the updating of the check state in the File table by iterating through
947  * each item in the file table to determine it's appropriate state.
948  * !! Will probably want to concoct a way to do this without iterating for the possibility
949  * of the very large directories.
950  */
951 void restoreTree::updateFileTableChecks()
952 {
953    /* deterimine the default state from the state of the directory */
954    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
955    Qt::CheckState dirState = dirTreeItem->checkState(0);
956
957    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
958
959    /* Update the items in the version table */
960    int rcnt = fileTable->rowCount();
961    for (int row=0; row<rcnt; row++) {
962       QTableWidgetItem* item = fileTable->item(row, 0);
963
964       Qt::CheckState curState = item->checkState();
965       Qt::CheckState newState = Qt::PartiallyChecked;
966       if (dirState == Qt::Unchecked) newState = Qt::Unchecked;
967
968       /* determine if it is already in the m_fileExceptionHash */
969       QString file = item->text();
970       QString fullPath = dirName + file;
971       Qt::CheckState hashState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
972       int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
973
974       if (hashState != 3) newState = hashState;
975
976       if (mainWin->m_rtUpdateFTDebug) {
977          QString msg = QString("file row=%1 cur=%2 hash=%3 new=%4 dirState=%5\n")
978             .arg(row).arg(curState).arg(hashState).arg(newState).arg(dirState);
979          Pmsg1(000, "%s", msg.toUtf8().data());
980       }
981
982       bool docolor = false;
983       if (hashJobNum != 0) docolor = true;
984       bool isyellow = item->background().color() == QColor(Qt::yellow);
985       if ((newState != curState) || (hashState == 3) || ((isyellow && !docolor) || (!isyellow && docolor)))
986          fileTableDisconnectedSet(item, newState, docolor);
987       m_fileCheckStateList[row] = newState;
988    }
989 }
990
991 /*
992  * Simplify the updating of the check state in the Version table by iterating through
993  * each item in the file table to determine it's appropriate state.
994  */
995 void restoreTree::updateVersionTableChecks()
996 {
997    /* deterimine the default state from the state of the directory */
998    QTreeWidgetItem *dirTreeItem = directoryTree->currentItem();
999    Qt::CheckState dirState = dirTreeItem->checkState(0);
1000    QString dirName = dirTreeItem->data(0, Qt::UserRole).toString();
1001
1002    /* deterimine the default state from the state of the file */
1003    QTableWidgetItem *fileTableItem = fileTable->item(fileTable->currentRow(), 0);
1004    Qt::CheckState fileState = fileTableItem->checkState();
1005    QString file = fileTableItem->text();
1006    QString fullPath = dirName + file;
1007    int hashJobNum = m_versionExceptionHash.value(fullPath, 0);
1008
1009    /* Update the items in the version table */
1010    int cnt = versionTable->rowCount();
1011    for (int row=0; row<cnt; row++) {
1012       QTableWidgetItem* item = versionTable->item(row, 0);
1013
1014       Qt::CheckState curState = item->checkState();
1015       Qt::CheckState newState = Qt::Unchecked;
1016
1017       if ((row == 0) && (fileState != Qt::Unchecked) && (hashJobNum == 0))
1018          newState = Qt::PartiallyChecked;
1019       /* determine if it is already in the versionExceptionHash */
1020       if (hashJobNum) {
1021          int thisJobNum = item->text().toInt();
1022          if (thisJobNum == hashJobNum)
1023             newState = Qt::Checked;
1024       }
1025       if (mainWin->m_rtChecksDebug) {
1026          QString msg = QString("ver row=%1 cur=%2 hashJobNum=%3 new=%4 dirState=%5\n")
1027             .arg(row).arg(curState).arg(hashJobNum).arg(newState).arg(dirState);
1028          Pmsg1(000, "%s", msg.toUtf8().data());
1029       }
1030       if (newState != curState)
1031          versionTableDisconnectedSet(item, newState);
1032       m_versionCheckStateList[row] = newState;
1033    }
1034 }
1035
1036 /*
1037  * Quick subroutine to "return via subPaths" a list of subpaths when passed a fullPath
1038  */
1039 void restoreTree::fullPathtoSubPaths(QStringList &subPaths, QString &fullPath_in)
1040 {
1041    int index;
1042    bool done = false;
1043    QString fullPath = fullPath_in;
1044    QString direct, path;
1045    while (((index = m_slashregex.lastIndexIn(fullPath, -2)) != -1) && (!done)) {
1046       direct = path = fullPath;
1047       path.replace(index+1, fullPath.length()-index-1, "");
1048       direct.replace(0, index+1, "");
1049       if (false) {
1050          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1051                     .arg(fullPath.length()).arg(index).arg(path).arg(direct);
1052          Pmsg0(000, msg.toUtf8().data());
1053       }
1054       fullPath = path;
1055       subPaths.append(fullPath);
1056    }
1057 }
1058
1059 /*
1060  * A Function to set the icon state and insert a record into
1061  * m_directoryIconStateHash when an exception is added by the user
1062  */
1063 void restoreTree::directoryIconStateInsert(QString &fullPath, Qt::CheckState excpState)
1064 {
1065    QStringList paths;
1066    fullPathtoSubPaths(paths, fullPath);
1067    /* an exception that causes the item in the file table to be "Checked" has occured */
1068    if (excpState == Qt::Checked) {
1069       bool foundAsUnChecked = false;
1070       QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1071       if (firstItem) {
1072          if (firstItem->checkState(0) == Qt::Unchecked)
1073             foundAsUnChecked = true;
1074       }
1075       if (foundAsUnChecked) {
1076           /* as long as directory item is Unchecked, set icon state to "green check" */
1077          bool done = false;
1078          QListIterator<QString> siter(paths);
1079          while (siter.hasNext() && !done) {
1080             QString path = siter.next();
1081             QTreeWidgetItem *item = m_dirPaths.value(path);
1082             if (item) {
1083                if (item->checkState(0) != Qt::Unchecked)
1084                   done = true;
1085                else {
1086                   directorySetIcon(1, FolderGreenChecked, path, item);
1087                   if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1088                }
1089             }
1090          }
1091       } else {
1092          /* if it is partially checked or fully checked insert green Check until a unchecked is found in the path */
1093          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert Aqua %s\n", paths[0].toUtf8().data());
1094          bool done = false;
1095          QListIterator<QString> siter(paths);
1096          while (siter.hasNext() && !done) {
1097             QString path = siter.next();
1098             QTreeWidgetItem *item = m_dirPaths.value(path);
1099             if (item) {  /* if the directory item is checked, set icon state to unchecked "green check" */
1100                if (item->checkState(0) == Qt::Checked)
1101                   done = true;
1102                directorySetIcon(1, FolderGreenChecked, path, item);
1103                if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1104             }
1105          }
1106       }
1107    }
1108    /* an exception that causes the item in the file table to be "Unchecked" has occured */
1109    if (excpState == Qt::Unchecked) {
1110       bool done = false;
1111       QListIterator<QString> siter(paths);
1112       while (siter.hasNext() && !done) {
1113          QString path = siter.next();
1114          QTreeWidgetItem *item = m_dirPaths.value(path);
1115          if (item) {  /* if the directory item is checked, set icon state to unchecked "white check" */
1116             if (item->checkState(0) == Qt::Checked)
1117                done = true;
1118             directorySetIcon(1, FolderWhiteChecked, path, item);
1119             if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert boogie %s\n", path.toUtf8().data());
1120          }
1121       }
1122    }
1123 }
1124
1125 /*
1126  * A function to set the icon state back to "folder" and to remove a record from
1127  * m_directoryIconStateHash when an exception is removed by a user.
1128  */
1129 void restoreTree::directoryIconStateRemove()
1130 {
1131    QHash<QString, int> shouldBeIconStateHash;
1132    /* First determine all paths with icons that should be checked with m_fileExceptionHash */
1133    /* Use iterator tera to iterate through m_fileExceptionHash */
1134    QHashIterator<QString, Qt::CheckState> tera(m_fileExceptionHash);
1135    while (tera.hasNext()) {
1136       tera.next();
1137       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Alpha Key %s value %i\n", tera.key().toUtf8().data(), tera.value());
1138
1139       QString keyPath = tera.key();
1140       Qt::CheckState state = tera.value();
1141
1142       QStringList paths;
1143       fullPathtoSubPaths(paths, keyPath);
1144       /* if the state of the item in m_fileExceptionHash is checked 
1145        * each of the subpaths should be "Checked Green" */
1146       if (state == Qt::Checked) {
1147
1148          bool foundAsUnChecked = false;
1149          QTreeWidgetItem *firstItem = m_dirPaths.value(paths[0]);
1150          if (firstItem) {
1151             if (firstItem->checkState(0) == Qt::Unchecked)
1152                foundAsUnChecked = true;
1153          }
1154          if (foundAsUnChecked) {
1155             /* The right most directory is Unchecked, iterate leftwards
1156              * as long as directory item is Unchecked, set icon state to "green check" */
1157             bool done = false;
1158             QListIterator<QString> siter(paths);
1159             while (siter.hasNext() && !done) {
1160                QString path = siter.next();
1161                QTreeWidgetItem *item = m_dirPaths.value(path);
1162                if (item) {
1163                   if (item->checkState(0) != Qt::Unchecked)
1164                      done = true;
1165                   else {
1166                      shouldBeIconStateHash.insert(path, FolderGreenChecked);
1167                      if (mainWin->m_rtIconStateDebug) Pmsg1(000, "In restoreTree::directoryIconStateInsert inserting %s\n", path.toUtf8().data());
1168                   }
1169                }
1170             }
1171          }
1172          else {
1173             /* The right most directory is Unchecked, iterate leftwards
1174              * until directory item is Checked, set icon state to "green check" */
1175             bool done = false;
1176             QListIterator<QString> siter(paths);
1177             while (siter.hasNext() && !done) {
1178                QString path = siter.next();
1179                QTreeWidgetItem *item = m_dirPaths.value(path);
1180                if (item) {
1181                   if (item->checkState(0) == Qt::Checked)
1182                      done = true;
1183                   shouldBeIconStateHash.insert(path, FolderGreenChecked);
1184                }
1185             }
1186          }
1187       }
1188       /* if the state of the item in m_fileExceptionHash is UNChecked
1189        * each of the subpaths should be "Checked white" until the tree item
1190        * which represents that path is Qt::Checked */
1191       if (state == Qt::Unchecked) {
1192          bool done = false;
1193          QListIterator<QString> siter(paths);
1194          while (siter.hasNext() && !done) {
1195             QString path = siter.next();
1196             QTreeWidgetItem *item = m_dirPaths.value(path);
1197             if (item) {
1198                if (item->checkState(0) == Qt::Checked)
1199                   done = true;
1200                shouldBeIconStateHash.insert(path, FolderWhiteChecked);
1201             }
1202          }
1203       }
1204    }
1205    /* now iterate through m_directoryIconStateHash which are the items that are checked
1206     * and remove all of those that are not in shouldBeIconStateHash */
1207    QHashIterator<QString, int> iter(m_directoryIconStateHash);
1208    while (iter.hasNext()) {
1209       iter.next();
1210       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Beta Key %s value %i\n", iter.key().toUtf8().data(), iter.value());
1211
1212       QString keyPath = iter.key();
1213       if (shouldBeIconStateHash.value(keyPath)) {
1214          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "WAS found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1215          //newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1216          int newval = shouldBeIconStateHash.value(keyPath);
1217          newval = ~newval;
1218          newval = newval & FolderBothChecked;
1219          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1220          if (item)
1221             directorySetIcon(0, newval, keyPath, item);
1222       } else {
1223          if (mainWin->m_rtIconStateDebug) Pmsg1(000, "NOT found in shouldBeStateHash %s\n", keyPath.toUtf8().data());
1224          QTreeWidgetItem *item = m_dirPaths.value(keyPath);
1225          if (item)
1226             directorySetIcon(0, FolderBothChecked, keyPath, item);
1227             //item->setIcon(0,QIcon(QString::fromUtf8(":images/folder.png")));
1228             //m_directoryIconStateHash.remove(keyPath);
1229       }
1230    }
1231 }
1232
1233 void restoreTree::directorySetIcon(int operation, int change, QString &path, QTreeWidgetItem* item) {
1234    int newval;
1235    /* we are adding a check type white or green */
1236    if (operation > 0) {
1237       /* get the old val and "bitwise OR" with the change */
1238       newval = m_directoryIconStateHash.value(path, FolderUnchecked) | change;
1239       if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1240       m_directoryIconStateHash.insert(path, newval);
1241    } else {
1242    /* we are removing a check type white or green */
1243       newval = m_directoryIconStateHash.value(path, FolderUnchecked) & (~change);
1244       if (newval == 0) {
1245          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Removing from m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1246          m_directoryIconStateHash.remove(path);
1247       }
1248       else {
1249          if (mainWin->m_rtIconStateDebug) Pmsg2(000, "Inserting into m_directoryIconStateHash path=%s newval=%i\n", path.toUtf8().data(), newval);
1250          m_directoryIconStateHash.insert(path, newval);
1251       }
1252    }
1253    if (newval == FolderUnchecked)
1254       item->setIcon(0, QIcon(QString::fromUtf8(":images/folder.png")));
1255    else if (newval == FolderGreenChecked)
1256       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderchecked.png")));
1257    else if (newval == FolderWhiteChecked)
1258       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderunchecked.png")));
1259    else if (newval == FolderBothChecked)
1260       item->setIcon(0, QIcon(QString::fromUtf8(":images/folderbothchecked.png")));
1261 }
1262
1263 /*
1264  * Test Button
1265  */
1266 void restoreTree::restoreButtonPushed()
1267 {
1268    QMultiHash<int, QString> versionFilesMulti;
1269    QHash <QString, bool> fullPathDone;
1270    QHash <QString, int> fileIndexHash;
1271    if ((mainWin->m_rtRestore1Debug) || (mainWin->m_rtRestore2Debug) || (mainWin->m_rtRestore3Debug))
1272       Pmsg0(000, "In restoreTree::restoreButtonPushed\n");
1273    /* Use a tree widget item iterator filtering for Checked Items */
1274    QTreeWidgetItemIterator diter(directoryTree, QTreeWidgetItemIterator::Checked);
1275    while (*diter) {
1276       QString directory = (*diter)->data(0, Qt::UserRole).toString();
1277       if (mainWin->m_rtRestore1Debug)
1278       Pmsg1(000, "Directory Checked=\"%s\"\n", directory.toUtf8().data());
1279       /* With a checked directory, query for the files in the directory */
1280
1281       QString cmd =
1282          "SELECT t1.Filename AS Filename, t1.JobId AS JobId, File.FileIndex AS FileIndex"
1283          " FROM"
1284          " ( SELECT Filename.Name AS Filename, MAX(Job.JobId) AS JobId"
1285            " FROM File"
1286              " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1287              " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1288              " LEFT OUTER JOIN Job ON (Job.JobId=File.JobId)"
1289            " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1290            "  AND Job.Jobid IN (" + m_jobQuery + ")"
1291            " GROUP BY Filename.Name"
1292          ") t1, File "
1293            " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1294            " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1295            " LEFT OUTER JOIN Job ON (Job.JobId=File.JobId)"
1296          " WHERE"
1297            " Path.Path='" + directory + "'"
1298            " AND Filename.Name=t1.Filename"
1299            " AND Job.Jobid=t1.JobId"
1300          " ORDER BY Filename";
1301
1302       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1303       QStringList results;
1304       if (m_console->sql_cmd(cmd, results)) {
1305       
1306          QStringList fieldlist;
1307    
1308          int row = 0;
1309          /* Iterate through the record returned from the query */
1310          foreach (QString resultline, results) {
1311             /* Iterate through fields in the record */
1312             int column = 0;
1313             QString fullPath = "";
1314             Qt::CheckState fileExcpState = (Qt::CheckState)4;
1315             fieldlist = resultline.split("\t");
1316             int version = 0;
1317             int fileIndex = 0;
1318             foreach (QString field, fieldlist) {
1319                if (column == 0) {
1320                   fullPath = directory + field;
1321                }
1322                if (column == 1) {
1323                   version = field.toInt();
1324                }
1325                if (column == 2) {
1326                   fileIndex = field.toInt();
1327                }
1328                column++;
1329             }
1330             fileExcpState = m_fileExceptionHash.value(fullPath, (Qt::CheckState)3);
1331             
1332             int excpVersion = m_versionExceptionHash.value(fullPath, 0);
1333             if (fileExcpState != Qt::Unchecked) {
1334                QString debugtext;
1335                if (excpVersion != 0) {
1336                   debugtext = QString("*E* version=%1").arg(excpVersion);
1337                   version = excpVersion;
1338                   fileIndex = queryFileIndex(fullPath, excpVersion);
1339                } else
1340                   debugtext = QString("___ version=%1").arg(version);
1341                if (mainWin->m_rtRestore1Debug)
1342                   Pmsg2(000, "Restoring %s File %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1343                fullPathDone.insert(fullPath, 1);
1344                fileIndexHash.insert(fullPath, fileIndex);
1345                versionFilesMulti.insert(version, fullPath);
1346             }
1347             row++;
1348          }
1349       }
1350       ++diter;
1351    } /* while (*diter) */
1352
1353    /* There may be some exceptions not accounted for yet with fullPathDone */
1354    QHashIterator<QString, Qt::CheckState> ftera(m_fileExceptionHash);
1355    while (ftera.hasNext()) {
1356       ftera.next();
1357       QString fullPath = ftera.key();
1358       Qt::CheckState state = ftera.value();
1359       if (state != 0) {
1360          /* now we don't want the ones already done */
1361          if (fullPathDone.value(fullPath, 0) == 0) {
1362             int version = m_versionExceptionHash.value(fullPath, 0);
1363             int fileIndex = 0;
1364             QString debugtext = "";
1365             if (version != 0) {
1366                fileIndex = queryFileIndex(fullPath, version);
1367                debugtext = QString("E1* version=%1 fileid=%2").arg(version).arg(fileIndex);
1368             } else {
1369                version = mostRecentVersionfromFullPath(fullPath);
1370                if (version) {
1371                   fileIndex = queryFileIndex(fullPath, version);
1372                   debugtext = QString("E2* version=%1 fileid=%2").arg(version).arg(fileIndex);
1373                } else
1374                   debugtext = QString("Error det vers").arg(version);
1375             }
1376             if (mainWin->m_rtRestore1Debug)
1377                Pmsg2(000, "Restoring %s file %s\n", debugtext.toUtf8().data(), fullPath.toUtf8().data());
1378             versionFilesMulti.insert(version, fullPath);
1379             fileIndexHash.insert(fullPath, fileIndex);
1380          } /* if fullPathDone.value(fullPath, 0) == 0 */
1381       } /* if state != 0 */
1382    } /* while ftera.hasNext */
1383
1384    /* now for the final spit out of the versions and lists of files for each version */
1385    QHash<int, int> doneKeys;
1386    QHashIterator<int, QString> vFMiter(versionFilesMulti);
1387    QString tempTable = "";
1388    QList<int> jobList;
1389    while (vFMiter.hasNext()) {
1390       vFMiter.next();
1391       int fversion = vFMiter.key();
1392       /* did not succeed in getting an iterator to work as expected on versionFilesMulti so use doneKeys */
1393       if (doneKeys.value(fversion, 0) == 0) {
1394          if (tempTable == "") {
1395             QSettings settings("www.bacula.org", "bat");
1396             settings.beginGroup("Restore");
1397             int counter = settings.value("Counter", 1).toInt();
1398             settings.setValue("Counter", counter+1);
1399             settings.endGroup();
1400             tempTable = "restore_" + QString("%1").arg(qrand()) + "_" + QString("%1").arg(counter);
1401             QString sqlcmd = "CREATE TEMPORARY TABLE " + tempTable + " (JobId INTEGER, FileIndex INTEGER)";
1402             if (mainWin->m_sqlDebug)
1403                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1404             QStringList results;
1405             if (!m_console->sql_cmd(sqlcmd, results))
1406                Pmsg1(000, "CREATE TABLE FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1407          }
1408
1409          if (mainWin->m_rtRestore2Debug) Pmsg1(000, "Version->%i\n", fversion);
1410          QStringList fullPathList = versionFilesMulti.values(fversion);
1411          /* create the command to perform the restore */
1412          foreach(QString ffullPath, fullPathList) {
1413             int fileIndex = fileIndexHash.value(ffullPath);
1414             if (mainWin->m_rtRestore2Debug) Pmsg2(000, "  file->%s id %i\n", ffullPath.toUtf8().data(), fileIndex);
1415             QString sqlcmd = "INSERT INTO " + tempTable + " (JobId, FileIndex) VALUES (" + QString("%1").arg(fversion) + ", " + QString("%1").arg(fileIndex) + ")";
1416             if (mainWin->m_sqlDebug)
1417                Pmsg1(000, "Query cmd : %s ;\n", sqlcmd.toUtf8().data());
1418             QStringList results;
1419             if (!m_console->sql_cmd(sqlcmd, results))
1420                Pmsg1(000, "INSERT INTO FAILED!!!! %s\n", sqlcmd.toUtf8().data());
1421          } /* foreach fullPathList */
1422          doneKeys.insert(fversion,1);
1423          jobList.append(fversion);
1424       } /*  if (doneKeys.value(fversion, 0) == 0) */
1425    } /* while (vFMiter.hasNext()) */
1426    if (tempTable != "") {
1427       QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
1428       new restoreTreeRunPage(tempTable, m_prevClientCombo, jobList, pageSelectorTreeWidgetItem);
1429    }
1430 }
1431
1432 int restoreTree::mostRecentVersionfromFullPath(QString &fullPath)
1433 {
1434    int qversion = 0;
1435    QString directory, fileName;
1436    int index = m_slashregex.lastIndexIn(fullPath, -2);
1437    if (index != -1) {
1438       directory = fileName = fullPath;
1439       directory.replace(index+1, fullPath.length()-index-1, "");
1440       fileName.replace(0, index+1, "");
1441       if (false) {
1442          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1443                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1444          Pmsg0(000, msg.toUtf8().data());
1445       }
1446       /* so now we need the latest version from the database */
1447       QString cmd =
1448          "SELECT MAX(Job.JobId)"
1449          " FROM File "
1450          " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1451          " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1452          " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1453          " WHERE Path.Path='" + directory + "' AND Filename.Name!=''"
1454          " AND Job.Jobid IN (" + m_jobQuery + ")"
1455          " AND Filename.Name='" + fileName + "'"
1456          " GROUP BY Filename.Name";
1457  
1458       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1459       QStringList results;
1460       if (m_console->sql_cmd(cmd, results)) {
1461          QStringList fieldlist;
1462          int row = 0;
1463          /* Iterate through the record returned from the query */
1464          foreach (QString resultline, results) {
1465             /* Iterate through fields in the record */
1466             int column = 0;
1467             fieldlist = resultline.split("\t");
1468             foreach (QString field, fieldlist) {
1469                if (column == 0) {
1470                   qversion = field.toInt();
1471                }
1472                column++;
1473             }
1474             row++;
1475          }
1476       }
1477    } /* if (index != -1) */
1478    return qversion;
1479 }
1480
1481
1482 int restoreTree::queryFileIndex(QString &fullPath, int jobId)
1483 {
1484    int qfileIndex = 0;
1485    QString directory, fileName;
1486    int index = m_slashregex.lastIndexIn(fullPath, -2);
1487    if (index != -1) {
1488       directory = fileName = fullPath;
1489       directory.replace(index+1, fullPath.length()-index-1, "");
1490       fileName.replace(0, index+1, "");
1491       if (false) {
1492          QString msg = QString("length = \"%1\" index = \"%2\" Considering \"%3\" \"%4\"\n")
1493                     .arg(fullPath.length()).arg(index).arg(fileName).arg(directory);
1494          Pmsg0(000, msg.toUtf8().data());
1495       }
1496       /* so now we need the latest version from the database */
1497       QString cmd =
1498          "SELECT"
1499           " File.FileIndex"
1500          " FROM File"
1501           " LEFT OUTER JOIN Filename on (Filename.FilenameId=File.FilenameId)"
1502           " LEFT OUTER JOIN Path ON (Path.PathId=File.PathId)"
1503           " LEFT OUTER JOIN Job ON (File.JobId=Job.JobId)"
1504          " WHERE"
1505           " Path.Path='" + directory + "'"
1506           " AND Filename.Name='" + fileName + "'"
1507           " AND Job.Jobid='" + QString("%1").arg(jobId) + "'"
1508          " GROUP BY File.FileIndex";
1509  
1510       if (mainWin->m_sqlDebug) Pmsg1(000, "Query cmd : %s\n", cmd.toUtf8().data());
1511       QStringList results;
1512       if (m_console->sql_cmd(cmd, results)) {
1513          QStringList fieldlist;
1514          int row = 0;
1515          /* Iterate through the record returned from the query */
1516          foreach (QString resultline, results) {
1517             /* Iterate through fields in the record */
1518             int column = 0;
1519             fieldlist = resultline.split("\t");
1520             foreach (QString field, fieldlist) {
1521                if (column == 0) {
1522                   qfileIndex = field.toInt();
1523                }
1524                column++;
1525             }
1526             row++;
1527          }
1528       }
1529    } /* if (index != -1) */
1530    return qfileIndex;
1531 }