2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2009 Free Software Foundation Europe e.V.
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
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.
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
23 Bacula® is a registered trademark of Kern Sibbald.
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.
31 * Dirk Bartley, March 2007
35 #include <QAbstractEventDispatcher>
36 #include <QTableWidgetItem>
40 #include "joblog/joblog.h"
42 #include "jobgraphs/jobplot.h"
44 #include "util/fmtwidgetitem.h"
45 #include "util/comboutil.h"
48 * Constructor for the class
50 JobList::JobList(const QString &mediaName, const QString &clientName,
51 const QString &jobName, const QString &filesetName, QTreeWidgetItem *parentTreeWidgetItem)
54 m_name = "Jobs Run"; /* treeWidgetName has a virtual override in this class */
55 m_mediaName = mediaName;
56 m_clientName = clientName;
58 m_filesetName = filesetName;
59 m_filesetName = filesetName;
60 pgInitialize("", parentTreeWidgetItem);
61 QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
62 thisitem->setIcon(0,QIcon(QString::fromUtf8(":images/emblem-system.png")));
67 if ((m_mediaName != "") || (m_clientName != "") || (m_jobName != "") || (m_filesetName != ""))
69 m_checkCurrentWidget = true;
71 /* Set Defaults for check and spin for limits */
72 limitCheckBox->setCheckState(mainWin->m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
73 limitSpinBox->setValue(mainWin->m_recordLimitVal);
74 daysCheckBox->setCheckState(mainWin->m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
75 daysSpinBox->setValue(mainWin->m_daysLimitVal);
77 QGridLayout *gridLayout = new QGridLayout(this);
78 gridLayout->setSpacing(6);
79 gridLayout->setMargin(9);
80 gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
82 m_splitter = new QSplitter(Qt::Vertical, this);
83 QScrollArea *area = new QScrollArea();
84 area->setObjectName(QString::fromUtf8("area"));
85 area->setWidget(frame);
86 area->setWidgetResizable(true);
87 m_splitter->addWidget(area);
88 m_splitter->addWidget(mp_tableWidget);
90 gridLayout->addWidget(m_splitter, 0, 0, 1, 1);
96 * Write the m_splitter settings in the destructor
104 * The Meat of the class.
105 * This function will populate the QTableWidget, mp_tablewidget, with
106 * QTableWidgetItems representing the results of a query for what jobs exist on
107 * the media name passed from the constructor stored in m_mediaName.
109 void JobList::populateTable()
111 /* Can't do this in constructor because not neccesarily conected in constructor */
112 prepareFilterWidgets();
115 Freeze frz(*mp_tableWidget); /* disable updating*/
119 fillQueryString(query);
121 /* Set up the Header for the table */
122 QStringList headerlist = (QStringList()
123 << tr("Job Id") << tr("Job Name") << tr("Client") << tr("Job Starttime")
124 << tr("Job Type") << tr("Job Level") << tr("Job Files")
125 << tr("Job Bytes") << tr("Job Status") << tr("Purged") << tr("File Set")
126 << tr("Pool Name") << tr("First Volume") << tr("VolCount"));
128 m_jobIdIndex = headerlist.indexOf(tr("Job Id"));
129 m_purgedIndex = headerlist.indexOf(tr("Purged"));
130 m_typeIndex = headerlist.indexOf(tr("Job Type"));
131 m_statusIndex = headerlist.indexOf(tr("Job Status"));
132 m_startIndex = headerlist.indexOf(tr("Job Starttime"));
133 m_filesIndex = headerlist.indexOf(tr("Job Files"));
134 m_bytesIndex = headerlist.indexOf(tr("Job Bytes"));
136 /* Initialize the QTableWidget */
137 m_checkCurrentWidget = false;
138 mp_tableWidget->clear();
139 m_checkCurrentWidget = true;
140 mp_tableWidget->setColumnCount(headerlist.size());
141 mp_tableWidget->setHorizontalHeaderLabels(headerlist);
142 mp_tableWidget->horizontalHeader()->setHighlightSections(false);
143 mp_tableWidget->setSelectionBehavior(QAbstractItemView::SelectRows);
144 mp_tableWidget->setSortingEnabled(false); /* rows move on insert if sorting enabled */
146 if (mainWin->m_sqlDebug) {
147 Pmsg1(000, "Query cmd : %s\n",query.toUtf8().data());
151 if (m_console->sql_cmd(query, results)) {
152 m_resultCount = results.count();
154 QStringList fieldlist;
155 mp_tableWidget->setRowCount(results.size());
158 /* Iterate through the record returned from the query */
160 foreach (resultline, results) {
161 fieldlist = resultline.split("\t");
162 if (fieldlist.size() < 13)
163 continue; /* some fields missing, ignore row */
165 TableItemFormatter jobitem(*mp_tableWidget, row);
167 /* Iterate through fields in the record */
168 QStringListIterator fld(fieldlist);
172 jobitem.setNumericFld(col++, fld.next());
175 jobitem.setTextFld(col++, fld.next());
178 jobitem.setTextFld(col++, fld.next());
181 jobitem.setTextFld(col++, fld.next(), true);
184 jobitem.setJobTypeFld(col++, fld.next());
187 jobitem.setJobLevelFld(col++, fld.next());
190 jobitem.setNumericFld(col++, fld.next());
193 jobitem.setBytesFld(col++, fld.next());
196 jobitem.setJobStatusFld(col++, fld.next());
199 jobitem.setBoolFld(col++, fld.next());
202 jobitem.setTextFld(col++, fld.next());
205 jobitem.setTextFld(col++, fld.next());
208 jobitem.setTextFld(col++, fld.next());
211 jobitem.setNumericFld(col++, fld.next());
215 /* set default sorting */
216 mp_tableWidget->sortByColumn(m_jobIdIndex, Qt::DescendingOrder);
217 mp_tableWidget->setSortingEnabled(true);
219 /* Resize the columns */
220 mp_tableWidget->resizeColumnsToContents();
221 mp_tableWidget->resizeRowsToContents();
222 mp_tableWidget->verticalHeader()->hide();
223 if ((m_mediaName != tr("Any")) && (m_resultCount == 0)){
224 /* for context sensitive searches, let the user know if there were no
226 QMessageBox::warning(this, "Bat",
227 tr("The Jobs query returned no results.\n"
228 "Press OK to continue?"), QMessageBox::Ok );
232 int rcnt = mp_tableWidget->rowCount();
233 int ccnt = mp_tableWidget->columnCount();
234 for(int r=0; r < rcnt; r++) {
235 for(int c=0; c < ccnt; c++) {
236 QTableWidgetItem* item = mp_tableWidget->item(r, c);
238 item->setFlags(Qt::ItemFlags(item->flags() & (~Qt::ItemIsEditable)));
244 void JobList::prepareFilterWidgets()
247 clientComboBox->addItem(tr("Any"));
248 clientComboBox->addItems(m_console->client_list);
249 comboSel(clientComboBox, m_clientName);
251 QStringList volumeList;
252 getVolumeList(volumeList);
253 volumeComboBox->addItem(tr("Any"));
254 volumeComboBox->addItems(volumeList);
255 comboSel(volumeComboBox, m_mediaName);
257 jobComboBox->addItem(tr("Any"));
258 jobComboBox->addItems(m_console->job_list);
259 comboSel(jobComboBox, m_jobName);
261 levelComboFill(levelComboBox);
263 boolComboFill(purgedComboBox);
265 fileSetComboBox->addItem(tr("Any"));
266 fileSetComboBox->addItems(m_console->fileset_list);
267 comboSel(fileSetComboBox, m_filesetName);
269 poolComboBox->addItem(tr("Any"));
270 poolComboBox->addItems(m_console->pool_list);
272 jobStatusComboFill(statusComboBox);
276 void JobList::fillQueryString(QString &query)
279 int volumeIndex = volumeComboBox->currentIndex();
280 if (volumeIndex != -1)
281 m_mediaName = volumeComboBox->itemText(volumeIndex);
282 QString distinct = "";
283 if (m_mediaName != tr("Any")) { distinct = "DISTINCT "; }
284 query += "SELECT " + distinct + "Job.JobId AS JobId, Job.Name AS JobName, "
285 " Client.Name AS Client,"
286 " Job.Starttime AS JobStart, Job.Type AS JobType,"
287 " Job.Level AS BackupLevel, Job.Jobfiles AS FileCount,"
288 " Job.JobBytes AS Bytes, Job.JobStatus AS Status,"
289 " Job.PurgedFiles AS Purged, FileSet.FileSet,"
290 " Pool.Name AS Pool,"
291 " (SELECT Media.VolumeName FROM JobMedia JOIN Media ON JobMedia.MediaId=Media.MediaId WHERE JobMedia.JobId=Job.JobId ORDER BY JobMediaId LIMIT 1) AS FirstVolume,"
292 " (SELECT count(DISTINCT MediaId) FROM JobMedia WHERE JobMedia.JobId=Job.JobId) AS Volumes"
294 " JOIN Client ON (Client.ClientId=Job.ClientId)"
295 " LEFT OUTER JOIN FileSet ON (FileSet.FileSetId=Job.FileSetId) "
296 " LEFT OUTER JOIN Pool ON Job.PoolId = Pool.PoolId ";
297 QStringList conditions;
298 if (m_mediaName != tr("Any")) {
299 query += " LEFT OUTER JOIN JobMedia ON (JobMedia.JobId=Job.JobId) "
300 " LEFT OUTER JOIN Media ON (JobMedia.MediaId=Media.MediaId) ";
301 conditions.append("Media.VolumeName='" + m_mediaName + "'");
304 comboCond(conditions, clientComboBox, "Client.Name");
305 comboCond(conditions, jobComboBox, "Job.Name");
306 levelComboCond(conditions, levelComboBox, "Job.Level");
307 jobStatusComboCond(conditions, statusComboBox, "Job.JobStatus");
308 boolComboCond(conditions, purgedComboBox, "Job.PurgedFiles");
309 comboCond(conditions, fileSetComboBox, "FileSet.FileSet");
310 comboCond(conditions, poolComboBox, "Pool.Name");
312 /* If Limit check box For limit by days is checked */
313 if (daysCheckBox->checkState() == Qt::Checked) {
314 QDateTime stamp = QDateTime::currentDateTime().addDays(-daysSpinBox->value());
315 QString since = stamp.toString(Qt::ISODate);
316 conditions.append("Job.Starttime > '" + since + "'");
318 if (filterCopyCheckBox->checkState() == Qt::Checked) {
319 conditions.append("Job.Type != 'c'" );
321 if (filterMigrationCheckBox->checkState() == Qt::Checked) {
322 conditions.append("Job.Type != 'g'" );
325 foreach (QString condition, conditions) {
327 query += " WHERE " + condition;
330 query += " AND " + condition;
334 query += " ORDER BY Job.JobId DESC";
335 /* If Limit check box for limit records returned is checked */
336 if (limitCheckBox->checkState() == Qt::Checked) {
338 limit.setNum(limitSpinBox->value());
339 query += " LIMIT " + limit;
344 * When the treeWidgetItem in the page selector tree is singleclicked, Make sure
345 * The tree has been populated.
347 void JobList::PgSeltreeWidgetClicked()
351 /* Lets make sure the splitter is not all the way to size index 0 == 0 */
352 QList<int> sizes = m_splitter->sizes();
354 int frameMax = frame->maximumHeight();
356 foreach(int size, sizes) { sizeSum += size; }
357 int tabHeight = mainWin->tabWidget->geometry().height();
359 sizes[1] = tabHeight - frameMax;
360 m_splitter->setSizes(sizes);
367 * Virtual function override of pages function which is called when this page
368 * is visible on the stack
370 void JobList::currentStackItem()
372 /* if (!m_populated) populate every time user comes back to this object */
377 * Virtual Function to return the name for the medialist tree widget
379 void JobList::treeWidgetName(QString &desc)
381 if (m_mediaName != "" ) {
382 desc = tr("Jobs Run on Volume %1").arg(m_mediaName);
383 } else if (m_clientName != "" ) {
384 desc = tr("Jobs Run from Client %1").arg(m_clientName);
385 } else if (m_jobName != "" ) {
386 desc = tr("Jobs Run of Job %1").arg(m_jobName);
387 } else if (m_filesetName != "" ) {
388 desc = tr("Jobs Run with fileset %1").arg(m_filesetName);
390 desc = tr("Jobs Run");
395 * Function to create connections for context sensitive menu for this and
398 void JobList::createConnections()
400 /* connect to the action specific to this pages class that shows up in the
401 * page selector tree */
402 connect(actionRefreshJobList, SIGNAL(triggered()), this, SLOT(populateTable()));
403 connect(refreshButton, SIGNAL(pressed()), this, SLOT(populateTable()));
405 connect(graphButton, SIGNAL(pressed()), this, SLOT(graphTable()));
407 graphButton->setEnabled(false);
408 graphButton->setVisible(false);
410 /* for the selectionChanged to maintain m_currentJob and a delete selection */
411 connect(mp_tableWidget, SIGNAL(itemSelectionChanged()), this, SLOT(selectionChanged()));
412 connect(mp_tableWidget, SIGNAL(itemDoubleClicked()), this, SLOT(showInfoForJob()));
414 /* Do what is required for the local context sensitive menu */
417 /* setContextMenuPolicy is required */
418 mp_tableWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
420 connect(actionListFilesOnJob, SIGNAL(triggered()), this, SLOT(consoleListFilesOnJob()));
421 connect(actionListJobMedia, SIGNAL(triggered()), this, SLOT(consoleListJobMedia()));
422 connect(actionListVolumes, SIGNAL(triggered()), this, SLOT(consoleListVolumes()));
423 connect(actionDeleteJob, SIGNAL(triggered()), this, SLOT(consoleDeleteJob()));
424 connect(actionPurgeFiles, SIGNAL(triggered()), this, SLOT(consolePurgeFiles()));
425 connect(actionRestoreFromJob, SIGNAL(triggered()), this, SLOT(preRestoreFromJob()));
426 connect(actionRestoreFromTime, SIGNAL(triggered()), this, SLOT(preRestoreFromTime()));
427 connect(actionShowLogForJob, SIGNAL(triggered()), this, SLOT(showLogForJob()));
428 connect(actionShowInfoForJob, SIGNAL(triggered()), this, SLOT(showInfoForJob()));
429 connect(actionCancelJob, SIGNAL(triggered()), this, SLOT(consoleCancelJob()));
430 connect(actionListJobTotals, SIGNAL(triggered()), this, SLOT(consoleListJobTotals()));
431 connect(m_splitter, SIGNAL(splitterMoved(int, int)), this, SLOT(splitterMoved(int, int)));
433 m_contextActions.append(actionRefreshJobList);
434 m_contextActions.append(actionListJobTotals);
438 * Functions to respond to local context sensitive menu sending console commands
439 * If I could figure out how to make these one function passing a string, Yaaaaaa
441 void JobList::consoleListFilesOnJob()
443 QString cmd("list files jobid=");
445 if (mainWin->m_longList) { cmd.prepend("l"); }
448 void JobList::consoleListJobMedia()
450 QString cmd("list jobmedia jobid=");
452 if (mainWin->m_longList) { cmd.prepend("l"); }
455 void JobList::consoleListVolumes()
457 QString cmd("list volumes jobid=");
459 if (mainWin->m_longList) { cmd.prepend("l"); }
462 void JobList::consoleListJobTotals()
464 QString cmd("list jobtotals");
465 if (mainWin->m_longList) { cmd.prepend("l"); }
468 void JobList::consoleDeleteJob()
470 if (QMessageBox::warning(this, "Bat",
471 tr("Are you sure you want to delete?? !!!.\n"
472 "This delete command is used to delete a Job record and all associated catalog"
473 " records that were created. This command operates only on the Catalog"
474 " database and has no effect on the actual data written to a Volume. This"
475 " command can be dangerous and we strongly recommend that you do not use"
476 " it unless you know what you are doing. The Job and all its associated"
477 " records (File and JobMedia) will be deleted from the catalog."
478 "Press OK to proceed with delete operation.?"),
479 QMessageBox::Ok | QMessageBox::Cancel)
480 == QMessageBox::Cancel) { return; }
482 QString cmd("delete job jobid=");
483 cmd += m_selectedJobs;
484 consoleCommand(cmd, false);
487 void JobList::consolePurgeFiles()
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"
497 "Press OK to proceed with the purge operation?"),
498 QMessageBox::Ok | QMessageBox::Cancel)
499 == QMessageBox::Cancel) { return; }
501 m_console->m_warningPrevent = true;
502 foreach(QString job, m_selectedJobsList) {
503 QString cmd("purge files jobid=");
505 consoleCommand(cmd, false);
507 m_console->m_warningPrevent = false;
512 * Subroutine to call preRestore to restore from a select job
514 void JobList::preRestoreFromJob()
516 new prerestorePage(m_currentJob, R_JOBIDLIST);
520 * Subroutine to call preRestore to restore from a select job
522 void JobList::preRestoreFromTime()
524 new prerestorePage(m_currentJob, R_JOBDATETIME);
528 * Subroutine to call class to show the log in the database from that job
530 void JobList::showLogForJob()
532 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
533 new JobLog(m_currentJob, pageSelectorTreeWidgetItem);
537 * Subroutine to call class to show the log in the database from that job
539 void JobList::showInfoForJob()
541 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
542 new Job(m_currentJob, pageSelectorTreeWidgetItem);
546 * Cancel a running job
548 void JobList::consoleCancelJob()
550 QString cmd("cancel jobid=");
558 void JobList::graphTable()
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();
574 QTreeWidgetItem* pageSelectorTreeWidgetItem = mainWin->getFromHash(this);
575 new JobPlot(pageSelectorTreeWidgetItem, pass);
580 * Save user settings associated with this page
582 void JobList::writeSettings()
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());
593 * Read and restore user settings associated with this page
595 void JobList::readSettings()
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());
604 filterCopyCheckBox->setCheckState((Qt::CheckState)settings.value("FilterCopyCheckState").toInt());
605 filterMigrationCheckBox->setCheckState((Qt::CheckState)settings.value("FilterMigrationCheckState").toInt());
610 * Function to fill m_selectedJobsCount and m_selectedJobs with selected values
612 void JobList::selectionChanged()
615 QList<QTableWidgetItem *> sitems = mp_tableWidget->selectedItems();
616 foreach (QTableWidgetItem *sitem, sitems) {
617 int row = sitem->row();
618 if (!rowList.contains(row)) {
624 m_selectedJobsList.clear();
626 foreach(int row, rowList) {
627 QTableWidgetItem * sitem = mp_tableWidget->item(row, m_jobIdIndex);
628 if (!first) m_selectedJobs.append(",");
630 m_selectedJobs.append(sitem->text());
631 m_selectedJobsList.append(sitem->text());
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);
640 actionDeleteJob->setText(tr("Delete Single Job"));
641 actionPurgeFiles->setText(tr("Purge Files from single job"));
644 /* remove all actions */
645 foreach(QAction* mediaAction, mp_tableWidget->actions()) {
646 mp_tableWidget->removeAction(mediaAction);
650 mp_tableWidget->addAction(actionRefreshJobList);
651 if (m_selectedJobsCount == 1) {
652 mp_tableWidget->addAction(actionListFilesOnJob);
653 mp_tableWidget->addAction(actionListJobMedia);
654 mp_tableWidget->addAction(actionListVolumes);
655 mp_tableWidget->addAction(actionRestoreFromJob);
656 mp_tableWidget->addAction(actionRestoreFromTime);
657 mp_tableWidget->addAction(actionShowLogForJob);
658 mp_tableWidget->addAction(actionShowInfoForJob);
660 if (m_selectedJobsCount >= 1) {
661 mp_tableWidget->addAction(actionDeleteJob);
662 mp_tableWidget->addAction(actionPurgeFiles);
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();
671 /* include purged action or not */
672 jobitem = mp_tableWidget->item(row, m_purgedIndex);
673 QString purged = jobitem->text();
674 /* mp_tableWidget->removeAction(actionPurgeFiles);
675 if (purged == tr("No") ) {
676 mp_tableWidget->addAction(actionPurgeFiles);
678 /* include restore from time and job action or not */
679 jobitem = mp_tableWidget->item(row, m_typeIndex);
680 QString type = jobitem->text();
681 if (m_selectedJobsCount == 1) {
682 mp_tableWidget->removeAction(actionRestoreFromJob);
683 mp_tableWidget->removeAction(actionRestoreFromTime);
684 if (type == tr("Backup")) {
685 mp_tableWidget->addAction(actionRestoreFromJob);
686 mp_tableWidget->addAction(actionRestoreFromTime);
689 /* include cancel action or not */
690 jobitem = mp_tableWidget->item(row, m_statusIndex);
691 QString status = jobitem->text();
692 mp_tableWidget->removeAction(actionCancelJob);
693 if (status == tr("Running") || status == tr("Created, not yet running")) {
694 mp_tableWidget->addAction(actionCancelJob);
700 * Function to prevent the splitter from making index 0 of the size larger than it
703 void JobList::splitterMoved(int /*pos*/, int /*index*/)
705 int frameMax = frame->maximumHeight();
706 QList<int> sizes = m_splitter->sizes();
708 foreach(int size, sizes) { sizeSum += size; }
709 if (sizes[0] > frameMax) {
711 sizes[1] = sizeSum - frameMax;
712 m_splitter->setSizes(sizes);