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