2 Bacula® - The Network Backup Solution
4 Copyright (C) 2007-2007 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 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.
32 * Main Window control for bat (qt-console)
34 * Kern Sibbald, January MMVII
39 #include "joblist/joblist.h"
40 #include "storage/storage.h"
41 #include "fileset/fileset.h"
42 #include "label/label.h"
45 #include "restore/restore.h"
46 #include "medialist/medialist.h"
47 #include "joblist/joblist.h"
48 #include "clients/clients.h"
49 #include "restore/restoretree.h"
50 #include "help/help.h"
51 #include "jobs/jobs.h"
52 #include "jobgraphs/jobplot.h"
55 * Daemon message callback
57 void message_callback(int /* type */, char *msg)
59 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
62 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
65 m_dtformat = "yyyy-MM-dd HH:mm:ss";
67 setupUi(this); /* Setup UI defined by main.ui (designer) */
68 register_message_callback(message_callback);
71 treeWidget->setColumnCount(1);
72 treeWidget->setHeaderLabel("Select Page");
73 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
85 foreach(Console *console, m_consoleHash) {
86 console->connect_dir();
88 m_currentConsole = (Console*)getFromHash(m_firstItem);
89 m_currentConsole->setCurrent();
91 QString directoryResourceName;
92 m_currentConsole->getDirResName(directoryResourceName);
93 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
97 void MainWin::createPages()
100 QTreeWidgetItem *item, *topItem;
104 foreach_res(dir, R_DIRECTOR) {
106 /* Create console tree stacked widget item */
107 m_currentConsole = new Console(stackedWidget);
108 m_currentConsole->setDirRes(dir);
109 m_currentConsole->readSettings();
111 /* The top tree item representing the director */
112 topItem = new QTreeWidgetItem(treeWidget);
113 topItem->setText(0, dir->name());
114 topItem->setIcon(0, QIcon(":images/server.png"));
115 /* Set background to grey for ease of identification of inactive Director */
116 QBrush greyBrush(Qt::lightGray);
117 topItem->setBackground(0, greyBrush);
118 m_currentConsole->setDirectorTreeItem(topItem);
119 m_consoleHash.insert(topItem, m_currentConsole);
121 /* Create Tree Widget Item */
122 item = new QTreeWidgetItem(topItem);
123 item->setText(0, "Console");
124 if (!m_firstItem){ m_firstItem = item; }
125 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
127 /* insert the cosole and tree widget item into the hashes */
128 hashInsert(item, m_currentConsole);
130 /* Set Color of treeWidgetItem for the console
131 * It will be set to green in the console class if the connection is made.
133 QBrush redBrush(Qt::red);
134 item->setForeground(0, redBrush);
135 m_currentConsole->dockPage();
138 * Create instances in alphabetic order of the rest
139 * of the classes that will by default exist under each Director.
145 createPageJobList("", "", "", "", NULL);
151 new JobPlot(NULL, pass);
153 treeWidget->expandItem(topItem);
154 stackedWidget->setCurrentWidget(m_currentConsole);
160 * create an instance of the the joblist class on the stack
162 void MainWin::createPageJobList(const QString &media, const QString &client,
163 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
165 QTreeWidgetItem *holdItem;
167 /* save current tree widget item in case query produces no results */
168 holdItem = treeWidget->currentItem();
169 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
170 /* If this is a query of jobs on a specific media */
171 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
172 joblist->setCurrent();
173 /* did query produce results, if not close window and set back to hold */
174 if (joblist->m_resultCount == 0) {
175 joblist->closeStackPage();
176 treeWidget->setCurrentItem(holdItem);
182 * Handle up and down arrow keys for the command line
185 void MainWin::keyPressEvent(QKeyEvent *event)
187 if (m_cmd_history.size() == 0) {
191 switch (event->key()) {
193 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
200 if (m_cmd_last == 0) {
204 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
205 m_cmd_last = m_cmd_history.size() - 1;
214 lineEdit->setText(m_cmd_history[m_cmd_last]);
217 void MainWin::createConnections()
219 /* Connect signals to slots */
220 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
221 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
222 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
223 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
224 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
225 connect(treeWidget, SIGNAL(
226 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
227 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
228 connect(stackedWidget, SIGNAL(currentChanged(int)),
229 this, SLOT(stackItemChanged(int)));
230 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
231 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
232 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
233 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
234 connect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
235 connect(actionJobPlot, SIGNAL(triggered()), this, SLOT(jobPlotButtonClicked()));
236 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
237 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
238 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
239 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
240 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
244 * Reimplementation of QWidget closeEvent virtual function
246 void MainWin::closeEvent(QCloseEvent *event)
250 /* close all non console pages, this will call settings in destructors */
251 foreach(Pages *page, m_pagehash) {
252 if (page != page->console()) {
253 page->console()->setCurrent();
254 page->closeStackPage();
257 /* close the console pages and terminate connection */
258 foreach(Console *console, m_consoleHash){
259 console->writeSettings();
260 console->terminate();
261 console->closeStackPage();
266 void MainWin::writeSettings()
268 QSettings settings("bacula.org", "bat");
270 settings.beginGroup("MainWin");
271 settings.setValue("winSize", size());
272 settings.setValue("winPos", pos());
273 settings.setValue("state", saveState());
277 void MainWin::readSettings()
279 QSettings settings("bacula.org", "bat");
281 settings.beginGroup("MainWin");
282 resize(settings.value("winSize", QSize(1041, 801)).toSize());
283 move(settings.value("winPos", QPoint(200, 150)).toPoint());
284 restoreState(settings.value("state").toByteArray());
289 * This subroutine is called with an item in the Page Selection window
292 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
294 /* Is this a page that has been inserted into the hash */
295 if (getFromHash(item)) {
296 Pages* page = getFromHash(item);
297 int stackindex=stackedWidget->indexOf(page);
299 if (stackindex >= 0) {
300 stackedWidget->setCurrentWidget(page);
302 /* run the virtual function in case this class overrides it */
303 page->PgSeltreeWidgetClicked();
308 * Called with a change of the highlighed tree widget item in the page selector.
310 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
312 if (m_isClosing) return; /* if closing the application, do nothing here */
314 Pages *previousPage, *nextPage;
315 Console *previousConsole = NULL;
316 Console *nextConsole;
318 /* remove all actions before adding actions appropriate for new page */
319 foreach(QAction* pageAction, treeWidget->actions()) {
320 treeWidget->removeAction(pageAction);
323 /* first determine the next item */
325 /* knowing the treeWidgetItem, get the page from the hash */
326 nextPage = getFromHash(currentitem);
327 nextConsole = m_consoleHash.value(currentitem);
328 /* Is this a page that has been inserted into the hash */
330 nextConsole = nextPage->console();
331 /* then is it a treeWidgetItem representing a director */
332 } else if (nextConsole) {
333 /* let the next page BE the console */
334 nextPage = nextConsole;
336 /* Should never get here */
341 /* The Previous item */
343 /* this condition prevents a segfault. The first time there is no previousitem*/
345 /* knowing the treeWidgetItem, get the page from the hash */
346 previousPage = getFromHash(previousitem);
347 previousConsole = m_consoleHash.value(previousitem);
349 previousConsole = previousPage->console();
350 } else if (previousConsole) {
351 previousPage = previousConsole;
353 if ((previousPage) || (previousConsole)) {
354 if (nextConsole != previousConsole) {
355 /* remove connections to the current console */
356 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
357 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
358 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
359 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
360 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
361 QBrush greyBrush(Qt::lightGray);
362 dirItem->setBackground(0, greyBrush);
367 /* process the current (next) item */
369 if ((nextPage) || (nextConsole)) {
370 if (nextConsole != previousConsole) {
371 /* make connections to the current console */
372 m_currentConsole = nextConsole;
373 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
374 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
375 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
376 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
377 /* Set director's tree widget background to magenta for ease of identification */
378 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
379 QBrush magentaBrush(Qt::magenta);
380 dirItem->setBackground(0, magentaBrush);
382 /* set the value for the currently active console */
383 int stackindex = stackedWidget->indexOf(nextPage);
385 /* Is this page currently on the stack or is it undocked */
386 if (stackindex >= 0) {
387 /* put this page on the top of the stack */
388 stackedWidget->setCurrentIndex(stackindex);
390 /* it is undocked, raise it to the front */
393 /* for the page selectors menu action to dock or undock, set the text */
394 nextPage->setContextMenuDockText();
396 treeWidget->addAction(actionToggleDock);
397 /* if this page is closeable, and it has no childern, then add that action */
398 if ((nextPage->isCloseable()) && (currentitem->child(0) == NULL))
399 treeWidget->addAction(actionClosePage);
401 /* Add the actions to the Page Selectors tree widget that are part of the
402 * current items list of desired actions regardless of whether on top of stack*/
403 treeWidget->addActions(nextPage->m_contextActions);
407 void MainWin::labelButtonClicked()
412 void MainWin::runButtonClicked()
417 void MainWin::estimateButtonClicked()
422 void MainWin::browseButtonClicked()
427 void MainWin::restoreButtonClicked()
429 new prerestorePage();
432 void MainWin::jobPlotButtonClicked()
436 new JobPlot(NULL, pass);
440 * The user just finished typing a line in the command line edit box
442 void MainWin::input_line()
444 QString cmdStr = lineEdit->text(); /* Get the text */
445 lineEdit->clear(); /* clear the lineEdit box */
446 if (m_currentConsole->is_connected()) {
447 /* Use consoleInput to allow typing anything */
448 m_currentConsole->consoleInput(cmdStr);
450 set_status("Director not connected. Click on connect button.");
452 m_cmd_history.append(cmdStr);
454 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
455 m_currentConsole->setCurrent();
459 void MainWin::about()
461 QMessageBox::about(this, tr("About bat"),
462 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
463 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
464 "<p>The <b>bat</b> is an administrative console"
465 " interface to the Director."));
470 Help::displayFile("index.html");
473 void MainWin::set_statusf(const char *fmt, ...)
478 va_start(arg_ptr, fmt);
479 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
484 void MainWin::set_status_ready()
486 set_status(" Ready");
489 void MainWin::set_status(const char *buf)
491 statusBar()->showMessage(buf);
495 * Function to respond to the button bar button to undock
497 void MainWin::undockWindowButton()
499 Pages* page = (Pages*)stackedWidget->currentWidget();
500 page->togglePageDocking();
504 * Function to respond to action on page selector context menu to toggle the
505 * dock status of the window associated with the page selectors current
508 void MainWin::toggleDockContextWindow()
510 QTreeWidgetItem *currentitem = treeWidget->currentItem();
512 /* Is this a page that has been inserted into the hash */
513 if (getFromHash(currentitem)) {
514 Pages* page = getFromHash(currentitem);
515 page->togglePageDocking();
520 * This function is called when the stack item is changed. Call
521 * the virtual function here. Avoids a window being undocked leaving
522 * a window at the top of the stack unpopulated.
524 void MainWin::stackItemChanged(int)
526 if (m_isClosing) return; /* if closing the application, do nothing here */
527 Pages* page = (Pages*)stackedWidget->currentWidget();
528 /* run the virtual function in case this class overrides it */
529 page->currentStackItem();
533 * Function to simplify insertion of QTreeWidgetItem <-> Page association
534 * into a double direction hash.
536 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
538 m_pagehash.insert(item, page);
539 m_widgethash.insert(page, item);
543 * Function to simplify removal of QTreeWidgetItem <-> Page association
544 * into a double direction hash.
546 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
548 /* I had all sorts of return status checking code here. Do we have a log
549 * level capability in bat. I would have left it in but it used printf's
550 * and it should really be some kind of log level facility ???
551 * ******FIXME********/
552 m_pagehash.remove(item);
553 m_widgethash.remove(page);
557 * Function to retrieve a Page* when the item in the page selector's tree is
560 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
562 return m_pagehash.value(item);
566 * Function to retrieve the page selectors tree widget item when the page is
569 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
571 return m_widgethash.value(page);
575 * Function to respond to action on page selector context menu to close the
578 void MainWin::closePage()
580 QTreeWidgetItem *currentitem = treeWidget->currentItem();
582 /* Is this a page that has been inserted into the hash */
583 if (getFromHash(currentitem)) {
584 Pages* page = getFromHash(currentitem);
585 if (page->isCloseable()) {
586 page->closeStackPage();
591 /* Quick function to return the current console */
592 Console *MainWin::currentConsole()
594 return m_currentConsole;
596 /* Quick function to return the tree item for the director */
597 QTreeWidgetItem *MainWin::currentTopItem()
599 return m_currentConsole->directorTreeItem();
602 /* Preferences menu item clicked */
603 void MainWin::setPreferences()
606 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
607 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
608 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
609 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
610 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
611 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
612 prefs.recordSpinBox->setValue(m_recordLimitVal);
613 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
614 prefs.daysSpinBox->setValue(m_daysLimitVal);
615 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
616 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
617 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
622 /* Preferences dialog */
623 prefsDialog::prefsDialog()
628 void prefsDialog::accept()
631 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
632 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
633 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
634 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
635 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
636 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
637 mainWin->m_recordLimitVal = this->recordSpinBox->value();
638 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
639 mainWin->m_daysLimitVal = this->daysSpinBox->value();
640 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
641 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
642 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
643 QSettings settings("www.bacula.org", "bat");
644 settings.beginGroup("Debug");
645 settings.setValue("commDebug", mainWin->m_commDebug);
646 settings.setValue("displayAll", mainWin->m_displayAll);
647 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
648 settings.setValue("commandDebug", mainWin->m_commandDebug);
649 settings.setValue("miscDebug", mainWin->m_miscDebug);
651 settings.beginGroup("JobList");
652 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
653 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
654 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
655 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
657 settings.beginGroup("Messages");
658 settings.setValue("checkMessages", mainWin->m_checkMessages);
659 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
661 settings.beginGroup("Misc");
662 settings.setValue("longList", mainWin->m_longList);
664 foreach(Console *console, mainWin->m_consoleHash) {
665 console->startTimer();
669 void prefsDialog::reject()
672 mainWin->set_status("Canceled");
675 /* read preferences for the prefences dialog box */
676 void MainWin::readPreferences()
678 QSettings settings("www.bacula.org", "bat");
679 settings.beginGroup("Debug");
680 m_commDebug = settings.value("commDebug", false).toBool();
681 m_displayAll = settings.value("displayAll", false).toBool();
682 m_sqlDebug = settings.value("sqlDebug", false).toBool();
683 m_commandDebug = settings.value("commandDebug", false).toBool();
684 m_miscDebug = settings.value("miscDebug", false).toBool();
686 settings.beginGroup("JobList");
687 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
688 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
689 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
690 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
692 settings.beginGroup("Messages");
693 m_checkMessages = settings.value("checkMessages", false).toBool();
694 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
696 settings.beginGroup("Misc");
697 m_longList = settings.value("longList", false).toBool();