1 /*
2    Bacula(R) - The Network Backup Solution
3 
4    Copyright (C) 2000-2020 Kern Sibbald
5 
6    The original author of Bacula is Kern Sibbald, with contributions
7    from many others, a complete list can be found in the file AUTHORS.
8 
9    You may use this file and others of this release according to the
10    license defined in the LICENSE file, which includes the Affero General
11    Public License, v3.0 ("AGPLv3") and some additional permissions and
12    terms pursuant to its AGPLv3 Section 7.
13 
14    This notice must be preserved when any source code is
15    conveyed and/or propagated.
16 
17    Bacula(R) is a registered trademark of Kern Sibbald.
18 */
19 /*
20  *   Dirk Bartley, March 2007
21  */
22 
23 #include "bat.h"
24 #include <QAbstractEventDispatcher>
25 #include <QTableWidgetItem>
26 #include "joblist.h"
27 #include "restore.h"
28 #include "job/job.h"
29 #include "joblog/joblog.h"
30 #ifdef HAVE_QWT
31 #include "jobgraphs/jobplot.h"
32 #endif
33 #include "util/fmtwidgetitem.h"
34 #include "util/comboutil.h"
35 
36 /*
37  * Constructor for the class
38  */
JobList(const QString & mediaName,const QString & clientName,const QString & jobName,const QString & filesetName,QTreeWidgetItem * parentTreeWidgetItem)39 JobList::JobList(const QString &mediaName, const QString &clientName,
40           const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
41    : Pages()
42 {
43    setupUi(this);
44    m_name = "Jobs Run"; /* treeWidgetName has a virtual override in this class */
45    m_mediaName = mediaName;
46    m_clientName = clientName;
47    m_jobName = jobName;
48    m_filesetName = filesetName;
49    pgInitialize("", parentTreeWidgetItem);
50    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
51    thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.png")));
52 
53    m_resultCount = 0;
54    m_populated = false;
55    m_closeable = false;
56    if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != "")) {
57       m_closeable=true;
58    }
59    m_checkCurrentWidget = true;
60 
61    /* Set Defaults for check and spin for limits */
62    limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
63    limitSpinBox->setValue(mainWin->m_recordLimitVal);
64    daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
65    daysSpinBox->setValue(mainWin->m_daysLimitVal);
66 
67    QGridLayout *gridLayout = new QGridLayout(this);
68    gridLayout->setSpacing(6);
69    gridLayout->setMargin(9);
70    gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
71 
72    m_splitter = new QSplitter(Qt::Vertical, this);
73    QScrollArea *area = new QScrollArea();
74    area->setObjectName(QString::fromUtf8("area"));
75    area->setWidget(frame);
76    area->setWidgetResizable(true);
77    m_splitter->addWidget(area);
78    m_splitter->addWidget(mp_tableWidget);
79 
80    gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
81    createConnections();
82    readSettings();
83    if (m_closeable) { dockPage(); }
84 }
85 
86 /*
87  * Write the m_splitter settings in the destructor
88  */
~JobList()89 JobList::~JobList()
90 {
91    writeSettings();
92 }
93 
94 /*
95  * The Meat of the class.
96  * This function will populate the QTableWidget, mp_tablewidget, with
97  * QTableWidgetItems representing the results of a query for what jobs exist on
98  * the media name passed from the constructor stored in m_mediaName.
99  */
populateTable()100 void JobList::populateTable()
101 {
102    /* Can't do this in constructor because not neccesarily conected in constructor */
103    prepareFilterWidgets();
104    m_populated = true;
105 
106    Freeze frz(*mp_tableWidget); /* disable updating*/
107 
108    /* Set up query */
109    QString query;
110    fillQueryString(query);
111 
112    /* Set up the Header for the table */
113    QStringList headerlist = (QStringList()
114       << tr("Job Id") << tr("Job Name") << tr("Client") << tr("Job Starttime")
115       << tr("Job Type") << tr("Job Level") << tr("Job Files")
116       << tr("Job Bytes") << tr("Job Status")  << tr("Purged") << tr("File Set")
117       << tr("Pool Name") << tr("First Volume") << tr("VolCount"));
118 
119    m_jobIdIndex = headerlist.indexOf(tr("Job Id"));
120    m_purgedIndex = headerlist.indexOf(tr("Purged"));
121    m_typeIndex = headerlist.indexOf(tr("Job Type"));
122    m_statusIndex = headerlist.indexOf(tr("Job Status"));
123    m_startIndex = headerlist.indexOf(tr("Job Starttime"));
124    m_filesIndex = headerlist.indexOf(tr("Job Files"));
125    m_bytesIndex = headerlist.indexOf(tr("Job Bytes"));
126    m_levelIndex = headerlist.indexOf(tr("Job Level"));
127    m_nameIndex = headerlist.indexOf(tr("Job Name"));
128    m_filesetIndex = headerlist.indexOf(tr("File Set"));
129    m_clientIndex = headerlist.indexOf(tr("Client"));
130 
131    /* Initialize the QTableWidget */
132    m_checkCurrentWidget = false;
133    mp_tableWidget->clear();
134    m_checkCurrentWidget = true;
135    mp_tableWidget->setColumnCount(headerlist.size());
136    mp_tableWidget->setHorizontalHeaderLabels(headerlist);
137    mp_tableWidget->horizontalHeader()->setHighlightSections(false);
138    mp_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
139    mp_tableWidget->setSortingEnabled(false); /* rows move on insert if sorting enabled */
140 
141    if (mainWin->m_sqlDebug) {
142       Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
143    }
144 
145    QStringList results;
146    if (m_console->sql_cmd(query, results)) {
147       m_resultCount = results.count();
148 
149       QStringList fieldlist;
150       mp_tableWidget->setRowCount(results.size());
151 
152       int row = 0;
153       /* Iterate through the record returned from the query */
154       QString resultline;
155       foreach (resultline, results) {
156          fieldlist = resultline.split("\t");
157          if (fieldlist.size() < 13)
158             continue; /* some fields missing, ignore row */
159 
160          TableItemFormatter jobitem(*mp_tableWidget, row);
161 
162          /* Iterate through fields in the record */
163          QStringListIterator fld(fieldlist);
164          int col = 0;
165 
166          /* job id */
167          jobitem.setNumericFld(col++, fld.next());
168 
169          /* job name */
170          jobitem.setTextFld(col++, fld.next());
171 
172          /* client */
173          jobitem.setTextFld(col++, fld.next());
174 
175          /* job starttime */
176          jobitem.setTextFld(col++, fld.next(), true);
177 
178          /* job type */
179          jobitem.setJobTypeFld(col++, fld.next());
180 
181          /* job level */
182          jobitem.setJobLevelFld(col++, fld.next());
183 
184          /* job files */
185          jobitem.setNumericFld(col++, fld.next());
186 
187          /* job bytes */
188          jobitem.setBytesFld(col++, fld.next());
189 
190          /* job status */
191          jobitem.setJobStatusFld(col++, fld.next());
192 
193          /* purged */
194          jobitem.setBoolFld(col++, fld.next());
195 
196          /* fileset */
197          jobitem.setTextFld(col++, fld.next());
198 
199          /* pool name */
200          jobitem.setTextFld(col++, fld.next());
201 
202          /* First Media */
203          jobitem.setTextFld(col++, fld.next());
204 
205          /* Medias count */
206          jobitem.setNumericFld(col++, fld.next());
207          row++;
208       }
209    }
210    /* set default sorting */
211    mp_tableWidget->sortByColumn(m_jobIdIndex, Qt::DescendingOrder);
212    mp_tableWidget->setSortingEnabled(true);
213 
214    /* Resize the columns */
215    mp_tableWidget->resizeColumnsToContents();
216    mp_tableWidget->resizeRowsToContents();
217    mp_tableWidget->verticalHeader()->hide();
218    if ((m_mediaName != tr("Any")) && (m_resultCount == 0)){
219       /* for context sensitive searches, let the user know if there were no
220        * results */
221       QMessageBox::warning(this, "Bat",
222           tr("The Jobs query returned no results.\n"
223          "Press OK to continue?"), QMessageBox::Ok );
224    }
225 
226    /* make read only */
227    mp_tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers);
228 }
229 
prepareFilterWidgets()230 void JobList::prepareFilterWidgets()
231 {
232    if (!m_populated) {
233       clientComboBox->addItem(tr("Any"));
234       clientComboBox->addItems(m_console->client_list);
235       comboSel(clientComboBox, m_clientName);
236 
237       QStringList volumeList;
238       getVolumeList(volumeList);
239       volumeComboBox->addItem(tr("Any"));
240       volumeComboBox->addItems(volumeList);
241       comboSel(volumeComboBox, m_mediaName);
242 
243       jobComboBox->addItem(tr("Any"));
244       jobComboBox->addItems(m_console->job_list);
245       comboSel(jobComboBox, m_jobName);
246 
247       levelComboFill(levelComboBox);
248 
249       boolComboFill(purgedComboBox);
250 
251       fileSetComboBox->addItem(tr("Any"));
252       fileSetComboBox->addItems(m_console->fileset_list);
253       comboSel(fileSetComboBox, m_filesetName);
254 
255       poolComboBox->addItem(tr("Any"));
256       poolComboBox->addItems(m_console->pool_list);
257 
258       jobStatusComboFill(statusComboBox);
259    }
260 }
261 
fillQueryString(QString & query)262 void JobList::fillQueryString(QString &query)
263 {
264    query = "";
265    int volumeIndex = volumeComboBox->currentIndex();
266    if (volumeIndex != -1)
267       m_mediaName = volumeComboBox->itemText(volumeIndex);
268    QString distinct = "";
269    if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
270    query += "SELECT " + distinct + "Job.JobId AS JobId, Job.Name AS JobName, "
271             " Client.Name AS Client,"
272             " Job.Starttime AS JobStart, Job.Type AS JobType,"
273             " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
274             " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
275             " Job.PurgedFiles AS Purged, FileSet.FileSet,"
276             " Pool.Name AS Pool,"
277             " (SELECT Media.VolumeName FROM JobMedia JOIN Media ON JobMedia.MediaId=Media.MediaId WHERE JobMedia.JobId=Job.JobId ORDER BY JobMediaId LIMIT 1) AS FirstVolume,"
278             " (SELECT count(DISTINCT MediaId) FROM JobMedia WHERE JobMedia.JobId=Job.JobId) AS Volumes"
279             " FROM Job"
280             " JOIN Client ON (Client.ClientId=Job.ClientId)"
281             " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) "
282             " LEFT OUTER JOIN Pool ON Job.PoolId = Pool.PoolId ";
283    QStringList conditions;
284    if (m_mediaName != tr("Any")) {
285       query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
286                " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
287       conditions.append("Media.VolumeName='" + m_mediaName + "'");
288    }
289 
290    comboCond(conditions, clientComboBox, "Client.Name");
291    comboCond(conditions, jobComboBox, "Job.Name");
292    levelComboCond(conditions, levelComboBox, "Job.Level");
293    jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
294    boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
295    comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
296    comboCond(conditions, poolComboBox, "Pool.Name");
297 
298    /* If Limit check box For limit by days is checked  */
299    if (daysCheckBox->checkState() == Qt::Checked) {
300       QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
301       QString since = stamp.toString(Qt::ISODate);
302       conditions.append("Job.Starttime > '" + since + "'");
303    }
304    if (filterCopyCheckBox->checkState() == Qt::Checked) {
305       conditions.append("Job.Type != 'c'" );
306    }
307    if (filterMigrationCheckBox->checkState() == Qt::Checked) {
308       conditions.append("Job.Type != 'g'" );
309    }
310    bool first = true;
311    foreach (QString condition, conditions) {
312       if (first) {
313          query += " WHERE " + condition;
314          first = false;
315       } else {
316          query += " AND " + condition;
317       }
318    }
319    /* Descending */
320    query += " ORDER BY Job.JobId DESC";
321    /* If Limit check box for limit records returned is checked  */
322    if (limitCheckBox->checkState() == Qt::Checked) {
323       QString limit;
324       limit.setNum(limitSpinBox->value());
325       query += " LIMIT " + limit;
326    }
327 }
328 
329 /*
330  * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
331  * The tree has been populated.
332  */
PgSeltreeWidgetClicked()333 void JobList::PgSeltreeWidgetClicked()
334 {
335    if (!m_populated) {
336       populateTable();
337       /* Lets make sure the splitter is not all the way to size index 0 == 0 */
338       QList<int> sizes = m_splitter->sizes();
339       if (sizes[0] == 0) {
340          int frameMax = frame->maximumHeight();
341          int sizeSum = 0;
342          foreach(int size, sizes) { sizeSum += size; }
343          int tabHeight = mainWin->tabWidget->geometry().height();
344          sizes[0] = frameMax;
345          sizes[1] = tabHeight - frameMax;
346          m_splitter->setSizes(sizes);
347       }
348    }
349    if (!isOnceDocked()) {
350       dockPage();
351    }
352 }
353 
354 /*
355  *  Virtual function override of pages function which is called when this page
356  *  is visible on the stack
357  */
currentStackItem()358 void JobList::currentStackItem()
359 {
360 /*   if (!m_populated) populate every time user comes back to this object */
361       populateTable();
362 }
363 
364 /*
365  * Virtual Function to return the name for the medialist tree widget
366  */
treeWidgetName(QString & desc)367 void JobList::treeWidgetName(QString &desc)
368 {
369    if (m_mediaName != "" ) {
370      desc = tr("Jobs Run on Volume %1").arg(m_mediaName);
371    } else if (m_clientName != "" ) {
372      desc = tr("Jobs Run from Client %1").arg(m_clientName);
373    } else if (m_jobName != "" ) {
374      desc = tr("Jobs Run of Job %1").arg(m_jobName);
375    } else if (m_filesetName != "" ) {
376      desc = tr("Jobs Run with fileset %1").arg(m_filesetName);
377    } else {
378      desc = tr("Jobs Run");
379    }
380 }
381 
382 /*
383  * Function to create connections for context sensitive menu for this and
384  * the page selector
385  */
createConnections()386 void JobList::createConnections()
387 {
388    /* connect to the action specific to this pages class that shows up in the
389     * page selector tree */
390    connect(actionRefreshJobList, SIGNAL(triggered()), this, SLOT(populateTable()));
391    connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
392 #ifdef HAVE_QWT
393    connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
394 #else
395    graphButton->setEnabled(false);
396    graphButton->setVisible(false);
397 #endif
398    /* for the selectionChanged to maintain m_currentJob and a delete selection */
399    connect(mp_tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
400    connect(mp_tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), this, SLOT(showInfoForJob()));
401 
402    /* Do what is required for the local context sensitive menu */
403 
404 
405    /* setContextMenuPolicy is required */
406    mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
407 
408    connect(actionListFilesOnJob, SIGNAL(triggered()), this, SLOT(consoleListFilesOnJob()));
409    connect(actionListJobMedia, SIGNAL(triggered()), this, SLOT(consoleListJobMedia()));
410    connect(actionDeleteJob, SIGNAL(triggered()), this, SLOT(consoleDeleteJob()));
411    connect(actionRestartJob, SIGNAL(triggered()), this, SLOT(consoleRestartJob()));
412    connect(actionPurgeFiles, SIGNAL(triggered()), this, SLOT(consolePurgeFiles()));
413    connect(actionRestoreFromJob, SIGNAL(triggered()), this, SLOT(preRestoreFromJob()));
414    connect(actionRestoreFromTime, SIGNAL(triggered()), this, SLOT(preRestoreFromTime()));
415    connect(actionShowLogForJob, SIGNAL(triggered()), this, SLOT(showLogForJob()));
416    connect(actionShowInfoForJob, SIGNAL(triggered()), this, SLOT(showInfoForJob()));
417    connect(actionCancelJob, SIGNAL(triggered()), this, SLOT(consoleCancelJob()));
418    connect(actionListJobTotals, SIGNAL(triggered()), this, SLOT(consoleListJobTotals()));
419    connect(m_splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(splitterMoved(int, int)));
420 
421    m_contextActions.append(actionRefreshJobList);
422    m_contextActions.append(actionListJobTotals);
423 }
424 
425 /*
426  * Functions to respond to local context sensitive menu sending console commands
427  * If I could figure out how to make these one function passing a string, Yaaaaaa
428  */
consoleListFilesOnJob()429 void JobList::consoleListFilesOnJob()
430 {
431    QString cmd("list files jobid=");
432    cmd += m_currentJob;
433    if (mainWin->m_longList) { cmd.prepend("l"); }
434    consoleCommand(cmd);
435 }
consoleListJobMedia()436 void JobList::consoleListJobMedia()
437 {
438    QString cmd("list jobmedia jobid=");
439    cmd += m_currentJob;
440    if (mainWin->m_longList) { cmd.prepend("l"); }
441    consoleCommand(cmd);
442 }
443 
consoleListJobTotals()444 void JobList::consoleListJobTotals()
445 {
446    QString cmd("list jobtotals");
447    if (mainWin->m_longList) { cmd.prepend("l"); }
448    consoleCommand(cmd);
449 }
450 
consoleDeleteJob()451 void JobList::consoleDeleteJob()
452 {
453    if (QMessageBox::warning(this, "Bat",
454       tr("Are you sure you want to delete??  !!!.\n"
455 "This delete command is used to delete a Job record and all associated catalog"
456 " records that were created. This command operates only on the Catalog"
457 " database and has no effect on the actual data written to a Volume. This"
458 " command can be dangerous and we strongly recommend that you do not use"
459 " it unless you know what you are doing.  The Job and all its associated"
460 " records (File and JobMedia) will be deleted from the catalog."
461       "Press OK to proceed with delete operation.?"),
462       QMessageBox::Ok | QMessageBox::Cancel)
463       == QMessageBox::Cancel) { return; }
464 
465    QString cmd("delete job jobid=");
466    cmd += m_selectedJobs;
467    consoleCommand(cmd, false);
468    populateTable();
469 }
470 
consoleRestartJob()471 void JobList::consoleRestartJob()
472 {
473    QString cmd;
474 
475    cmd = tr("run job=\"%1\" client=\"%2\" level=%3").arg(m_jobName).arg(m_clientName).arg(m_levelName);
476    if (m_filesetName != "" && m_filesetName != "*None*") {
477       cmd += tr(" fileset=\"%1\"").arg(m_filesetName);
478    }
479 
480    if (mainWin->m_commandDebug) Pmsg1(000, "Run cmd : %s\n",cmd.toUtf8().data());
481    consoleCommand(cmd, false);
482    populateTable();
483 }
484 
485 
486 
consolePurgeFiles()487 void JobList::consolePurgeFiles()
488 {
489    if (QMessageBox::warning(this, "Bat",
490       tr("Are you sure you want to purge ??  !!!.\n"
491 "The Purge command will delete associated Catalog database records from Jobs and"
492 " Volumes without considering the retention period. Purge  works only on the"
493 " Catalog database and does not affect data written to Volumes. This command can"
494 " be dangerous because you can delete catalog records associated with current"
495 " backups of files, and we recommend that you do not use it unless you know what"
496 " you are doing.\n"
497       "Press OK to proceed with the purge operation?"),
498       QMessageBox::Ok | QMessageBox::Cancel)
499       == QMessageBox::Cancel) { return; }
500 
501    m_console->m_warningPrevent = true;
502    foreach(QString job, m_selectedJobsList) {
503       QString cmd("purge files jobid=");
504       cmd += job;
505       consoleCommand(cmd, false);
506    }
507    m_console->m_warningPrevent = false;
508    populateTable();
509 }
510 
511 /*
512  * Subroutine to call preRestore to restore from a select job
513  */
preRestoreFromJob()514 void JobList::preRestoreFromJob()
515 {
516    new prerestorePage(m_currentJob, R_JOBIDLIST);
517 }
518 
519 /*
520  * Subroutine to call preRestore to restore from a select job
521  */
preRestoreFromTime()522 void JobList::preRestoreFromTime()
523 {
524    new prerestorePage(m_currentJob, R_JOBDATETIME);
525 }
526 
527 /*
528  * Subroutine to call class to show the log in the database from that job
529  */
showLogForJob()530 void JobList::showLogForJob()
531 {
532    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
533    new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
534 }
535 
536 /*
537  * Subroutine to call class to show the log in the database from that job
538  */
showInfoForJob(QTableWidgetItem *)539 void JobList::showInfoForJob(QTableWidgetItem * /*item*/)
540 {
541    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
542    new Job(m_currentJob, pageSelectorTreeWidgetItem);
543 }
544 
545 /*
546  * Cancel a running job
547  */
consoleCancelJob()548 void JobList::consoleCancelJob()
549 {
550    QString cmd("cancel jobid=");
551    cmd += m_currentJob;
552    consoleCommand(cmd);
553 }
554 
555 /*
556  * Graph this table
557  */
graphTable()558 void JobList::graphTable()
559 {
560 #ifdef HAVE_QWT
561    JobPlotPass pass;
562    pass.recordLimitCheck = limitCheckBox->checkState();
563    pass.daysLimitCheck = daysCheckBox->checkState();
564    pass.recordLimitSpin = limitSpinBox->value();
565    pass.daysLimitSpin = daysSpinBox->value();
566    pass.jobCombo = jobComboBox->currentText();
567    pass.clientCombo = clientComboBox->currentText();
568    pass.volumeCombo = volumeComboBox->currentText();
569    pass.fileSetCombo = fileSetComboBox->currentText();
570    pass.purgedCombo = purgedComboBox->currentText();
571    pass.levelCombo = levelComboBox->currentText();
572    pass.statusCombo = statusComboBox->currentText();
573    pass.use = true;
574    QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
575    new JobPlot(pageSelectorTreeWidgetItem, pass);
576 #endif
577 }
578 
579 /*
580  * Save user settings associated with this page
581  */
writeSettings()582 void JobList::writeSettings()
583 {
584    QSettings settings(m_console->m_dir->name(), "bat");
585    settings.beginGroup(m_groupText);
586    settings.setValue(m_splitText, m_splitter->saveState());
587    settings.setValue("FilterCopyCheckState", filterCopyCheckBox->checkState());
588    settings.setValue("FilterMigrationCheckState", filterMigrationCheckBox->checkState());
589    settings.endGroup();
590 }
591 
592 /*
593  * Read and restore user settings associated with this page
594  */
readSettings()595 void JobList::readSettings()
596 {
597    m_groupText = "JobListPage";
598    m_splitText = "splitterSizes_2";
599    QSettings settings(m_console->m_dir->name(), "bat");
600    settings.beginGroup(m_groupText);
601    if (settings.contains(m_splitText)) {
602       m_splitter->restoreState(settings.value(m_splitText).toByteArray());
603    }
604    filterCopyCheckBox->setCheckState((Qt::CheckState)settings.value("FilterCopyCheckState").toInt());
605    filterMigrationCheckBox->setCheckState((Qt::CheckState)settings.value("FilterMigrationCheckState").toInt());
606    settings.endGroup();
607 }
608 
609 /*
610  * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
611  */
selectionChanged()612 void JobList::selectionChanged()
613 {
614    QList<int> rowList;
615    QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
616    foreach (QTableWidgetItem *sitem, sitems) {
617       int row = sitem->row();
618       if (!rowList.contains(row)) {
619          rowList.append(row);
620       }
621    }
622 
623    m_selectedJobs = "";
624    m_selectedJobsList.clear();
625    bool first = true;
626    foreach(int row, rowList) {
627       QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
628       if (!first) m_selectedJobs.append(",");
629       else first = false;
630       m_selectedJobs.append(sitem->text());
631       m_selectedJobsList.append(sitem->text());
632    }
633    m_selectedJobsCount = rowList.count();
634    if (m_selectedJobsCount > 1) {
635       QString text = QString( tr("Delete list of %1 Jobs")).arg(m_selectedJobsCount);
636       actionDeleteJob->setText(text);
637       text = QString( tr("Purge Files from list of %1 Jobs")).arg(m_selectedJobsCount);
638       actionPurgeFiles->setText(text);
639    } else {
640       actionDeleteJob->setText(tr("Delete Single Job"));
641       actionPurgeFiles->setText(tr("Purge Files from single job"));
642    }
643 
644    /* remove all actions */
645    foreach(QAction* mediaAction, mp_tableWidget->actions()) {
646       mp_tableWidget->removeAction(mediaAction);
647    }
648 
649    /* Add Actions */
650    mp_tableWidget->addAction(actionRefreshJobList);
651    if (m_selectedJobsCount == 1) {
652       mp_tableWidget->addAction(actionListFilesOnJob);
653       mp_tableWidget->addAction(actionListJobMedia);
654       mp_tableWidget->addAction(actionRestartJob);
655       mp_tableWidget->addAction(actionRestoreFromJob);
656       mp_tableWidget->addAction(actionRestoreFromTime);
657       mp_tableWidget->addAction(actionShowLogForJob);
658       mp_tableWidget->addAction(actionShowInfoForJob);
659    }
660    if (m_selectedJobsCount >= 1) {
661       mp_tableWidget->addAction(actionDeleteJob);
662       mp_tableWidget->addAction(actionPurgeFiles);
663    }
664 
665    /* Make Connections */
666    if (m_checkCurrentWidget) {
667       int row = mp_tableWidget->currentRow();
668       QTableWidgetItem* jobitem = mp_tableWidget->item(row, 0);
669       m_currentJob = jobitem->text();    /* get JobId */
670       jobitem = mp_tableWidget->item(row, m_clientIndex);
671       m_clientName = jobitem->text();    /* get Client Name */
672       jobitem = mp_tableWidget->item(row, m_nameIndex);
673       m_jobName = jobitem->text();    /* get Job Name */
674       jobitem = mp_tableWidget->item(row, m_levelIndex);
675       m_levelName = jobitem->text();    /* get level */
676       jobitem = mp_tableWidget->item(row, m_filesetIndex);
677       if (jobitem) {
678          m_filesetName = jobitem->text();    /* get FileSet Name */
679       } else {
680          m_filesetName = "";
681       }
682 
683       /* include purged action or not */
684       jobitem = mp_tableWidget->item(row, m_purgedIndex);
685       QString purged = jobitem->text();
686 /*      mp_tableWidget->removeAction(actionPurgeFiles);
687       if (purged == tr("No") ) {
688          mp_tableWidget->addAction(actionPurgeFiles);
689       }*/
690 
691       /* include restore from time and job action or not */
692       jobitem = mp_tableWidget->item(row, m_typeIndex);
693       QString type = jobitem->text();
694       if (m_selectedJobsCount == 1) {
695          mp_tableWidget->removeAction(actionRestoreFromJob);
696          mp_tableWidget->removeAction(actionRestoreFromTime);
697          if (type == tr("Backup")) {
698             mp_tableWidget->addAction(actionRestoreFromJob);
699             mp_tableWidget->addAction(actionRestoreFromTime);
700          }
701       }
702 
703       /* include cancel action or not */
704       jobitem = mp_tableWidget->item(row, m_statusIndex);
705       QString status = jobitem->text();
706       mp_tableWidget->removeAction(actionCancelJob);
707       if (status == tr("Running") || status == tr("Created, not yet running")) {
708          mp_tableWidget->addAction(actionCancelJob);
709       }
710    }
711 }
712 
713 /*
714  *  Function to prevent the splitter from making index 0 of the size larger than it
715  *  needs to be
716  */
splitterMoved(int,int)717 void JobList::splitterMoved(int /*pos*/, int /*index*/)
718 {
719    int frameMax = frame->maximumHeight();
720    QList<int> sizes = m_splitter->sizes();
721    int sizeSum = 0;
722    foreach(int size, sizes) { sizeSum += size; }
723    if (sizes[0] > frameMax) {
724       sizes[0] = frameMax;
725       sizes[1] = sizeSum - frameMax;
726       m_splitter->setSizes(sizes);
727    }
728 }
729