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