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 plus additions
11 that are listed in the file LICENSE.
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 "help/help.h"
52 * Daemon message callback
54 void message_callback(int /* type */, char *msg)
56 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
59 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
61 m_dtformat = "yyyy-MM-dd HH:mm:ss";
63 setupUi(this); /* Setup UI defined by main.ui (designer) */
64 register_message_callback(message_callback);
67 treeWidget->setColumnCount(1);
68 treeWidget->setHeaderLabel("Select Page");
69 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
81 foreach(Console *console, m_consoleHash) {
82 console->connect_dir();
84 m_currentConsole = (Console*)getFromHash(m_firstItem);
85 m_currentConsole->setCurrent();
87 QString directoryResourceName;
88 m_currentConsole->getDirResName(directoryResourceName);
89 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
93 void MainWin::createPages()
96 QTreeWidgetItem *item, *topItem;
100 foreach_res(dir, R_DIRECTOR) {
102 /* Create console tree stacked widget item */
103 m_currentConsole = new Console(stackedWidget);
104 m_currentConsole->setDirRes(dir);
105 m_currentConsole->readSettings();
107 /* The top tree item representing the director */
108 topItem = createTopPage(dir->name());
109 topItem->setIcon(0, QIcon(":images/server.png"));
110 /* Set background to grey for ease of identification of inactive Director */
111 QBrush greyBrush(Qt::lightGray);
112 topItem->setBackground(0, greyBrush);
113 m_currentConsole->setDirectorTreeItem(topItem);
114 m_consoleHash.insert(topItem, m_currentConsole);
116 /* Create Tree Widget Item */
117 item = createPage("Console", topItem);
118 if (!m_firstItem){ m_firstItem = item; }
119 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
121 /* insert the cosole and tree widget item into the hashes */
122 hashInsert(item, m_currentConsole);
124 /* Set Color of treeWidgetItem for the console
125 * It will be set to green in the console class if the connection is made.
127 QBrush redBrush(Qt::red);
128 item->setForeground(0, redBrush);
129 m_currentConsole->dockPage();
132 * Create instances in alphabetic order of the rest
133 * of the classes that will by default exist under each Director.
135 // createPagebRestore();
138 QString emptymedia(""), emptyclient("");
139 createPageJobList(emptymedia, emptyclient, NULL);
140 createPageMediaList();
143 treeWidget->expandItem(topItem);
144 stackedWidget->setCurrentWidget(m_currentConsole);
150 * create an instance of the the brestore class on the stack
152 void MainWin::createPagebRestore()
154 bRestore* brestore = new bRestore();
155 brestore->dockPage();
159 * create an instance of the the medialist class on the stack
161 void MainWin::createPageMediaList()
163 MediaList* medialist = new MediaList();
164 medialist->dockPage();
168 * create an instance of the the joblist class on the stack
170 void MainWin::createPageJobList(QString &media, QString &client,
171 QTreeWidgetItem *parentTreeWidgetItem)
173 QTreeWidgetItem *holdItem;
175 /* save current tree widget item in case query produces no results */
176 holdItem = treeWidget->currentItem();
177 JobList* joblist = new JobList(media, client, parentTreeWidgetItem);
179 /* If this is a query of jobs on a specific media */
180 if ((media != "") || (client != "")) {
181 joblist->setCurrent();
182 /* did query produce results, if not close window and set back to hold */
183 if (joblist->m_resultCount == 0) {
184 joblist->closeStackPage();
185 treeWidget->setCurrentItem(holdItem);
191 * create an instance of the the Clients class on the stack
193 void MainWin::createPageClients()
195 Clients* clients = new Clients();
200 * create an instance of the the storage class on the stack
202 void MainWin::createPageStorage()
204 Storage* storage = new Storage();
209 * create an instance of the the fileset class on the stack
211 void MainWin::createPageFileSet()
213 FileSet* fileset = new FileSet();
217 /* Create a root Tree Widget */
218 QTreeWidgetItem *MainWin::createTopPage(char *name)
220 QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
221 item->setText(0, name);
225 /* Create A Tree Widget Item which will be associated with a Page in the stacked widget */
226 QTreeWidgetItem *MainWin::createPage(char *name, QTreeWidgetItem *parent)
228 QTreeWidgetItem *item = new QTreeWidgetItem(parent);
229 item->setText(0, name);
234 * Handle up and down arrow keys for the command line
237 void MainWin::keyPressEvent(QKeyEvent *event)
239 if (m_cmd_history.size() == 0) {
243 switch (event->key()) {
245 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
252 if (m_cmd_last == 0) {
256 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
257 m_cmd_last = m_cmd_history.size() - 1;
266 lineEdit->setText(m_cmd_history[m_cmd_last]);
269 void MainWin::createConnections()
271 /* Connect signals to slots */
272 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
273 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
274 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
275 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
276 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
277 connect(treeWidget, SIGNAL(
278 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
279 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
280 connect(stackedWidget, SIGNAL(currentChanged(int)),
281 this, SLOT(stackItemChanged(int)));
282 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
283 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
284 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
285 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
286 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
287 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
288 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
289 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
290 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
294 * Reimplementation of QWidget closeEvent virtual function
296 void MainWin::closeEvent(QCloseEvent *event)
299 foreach(Console *console, m_consoleHash){
300 console->writeSettings();
301 console->terminate();
304 foreach(Pages *page, m_pagehash) {
305 if (!page->isDocked())
310 void MainWin::writeSettings()
312 QSettings settings("bacula.org", "bat");
314 settings.beginGroup("MainWin");
315 settings.setValue("winSize", size());
316 settings.setValue("winPos", pos());
317 settings.setValue("state", saveState());
321 void MainWin::readSettings()
323 QSettings settings("bacula.org", "bat");
325 settings.beginGroup("MainWin");
326 resize(settings.value("winSize", QSize(1041, 801)).toSize());
327 move(settings.value("winPos", QPoint(200, 150)).toPoint());
328 restoreState(settings.value("state").toByteArray());
333 * This subroutine is called with an item in the Page Selection window
336 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
338 /* Is this a page that has been inserted into the hash */
339 if (getFromHash(item)) {
340 Pages* page = getFromHash(item);
341 int stackindex=stackedWidget->indexOf(page);
343 if (stackindex >= 0) {
344 stackedWidget->setCurrentWidget(page);
346 /* run the virtual function in case this class overrides it */
347 page->PgSeltreeWidgetClicked();
352 * Called with a change of the highlighed tree widget item in the page selector.
354 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
356 Pages *previousPage, *nextPage;
357 Console *previousConsole, *nextConsole;
359 /* first determine the next item */
361 /* knowing the treeWidgetItem, get the page from the hash */
362 nextPage = getFromHash(currentitem);
363 nextConsole = m_consoleHash.value(currentitem);
364 /* Is this a page that has been inserted into the hash */
366 nextConsole = nextPage->console();
367 /* then is it a treeWidgetItem representing a director */
368 } else if (nextConsole) {
369 /* let the next page BE the console */
370 nextPage = nextConsole;
372 /* Should never get here */
377 /* The Previous item */
379 /* this condition prevents a segfault. The first time there is no previousitem*/
381 /* knowing the treeWidgetItem, get the page from the hash */
382 previousPage = getFromHash(previousitem);
383 previousConsole = m_consoleHash.value(previousitem);
385 previousConsole = previousPage->console();
386 } else if (previousConsole) {
387 previousPage = previousConsole;
389 if ((previousPage) || (previousConsole)) {
390 if (nextConsole != previousConsole) {
391 /* remove connections to the current console */
392 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
393 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
394 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
395 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
396 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
397 QBrush greyBrush(Qt::lightGray);
398 dirItem->setBackground(0, greyBrush);
400 /* make sure the close window and toggle dock options are removed */
401 treeWidget->removeAction(actionClosePage);
402 treeWidget->removeAction(actionToggleDock);
403 /* Is this a page that has been inserted into the hash */
405 foreach(QAction* pageaction, previousPage->m_contextActions) {
406 treeWidget->removeAction(pageaction);
412 /* process the current (next) item */
414 if ((nextPage) || (nextConsole)) {
415 if (nextConsole != previousConsole) {
416 /* make connections to the current console */
417 m_currentConsole = nextConsole;
418 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
419 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
420 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
421 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
422 /* Set director's tree widget background to magenta for ease of identification */
423 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
424 QBrush magentaBrush(Qt::magenta);
425 dirItem->setBackground(0, magentaBrush);
427 /* set the value for the currently active console */
428 int stackindex = stackedWidget->indexOf(nextPage);
430 /* Is this page currently on the stack or is it undocked */
431 if (stackindex >= 0) {
432 /* put this page on the top of the stack */
433 stackedWidget->setCurrentIndex(stackindex);
435 /* it is undocked, raise it to the front */
438 /* for the page selectors menu action to dock or undock, set the text */
439 nextPage->setContextMenuDockText();
441 treeWidget->addAction(actionToggleDock);
442 /* if this page is closeable, then add that action */
443 if (nextPage->isCloseable()) {
444 treeWidget->addAction(actionClosePage);
447 /* Add the actions to the Page Selectors tree widget that are part of the
448 * current items list of desired actions regardless of whether on top of stack*/
449 treeWidget->addActions(nextPage->m_contextActions);
453 void MainWin::labelButtonClicked()
458 void MainWin::runButtonClicked()
463 void MainWin::estimateButtonClicked()
468 void MainWin::restoreButtonClicked()
470 new prerestorePage();
474 * The user just finished typing a line in the command line edit box
476 void MainWin::input_line()
478 QString cmdStr = lineEdit->text(); /* Get the text */
479 lineEdit->clear(); /* clear the lineEdit box */
480 if (m_currentConsole->is_connected()) {
481 m_currentConsole->consoleCommand(cmdStr);
483 set_status("Director not connected. Click on connect button.");
485 m_cmd_history.append(cmdStr);
487 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
488 m_currentConsole->setCurrent();
492 void MainWin::about()
494 QMessageBox::about(this, tr("About bat"),
495 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
496 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
497 "<p>The <b>bat</b> is an administrative console"
498 " interface to the Director."));
503 Help::displayFile("index.html");
506 void MainWin::set_statusf(const char *fmt, ...)
511 va_start(arg_ptr, fmt);
512 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
517 void MainWin::set_status_ready()
519 set_status(" Ready");
522 void MainWin::set_status(const char *buf)
524 statusBar()->showMessage(buf);
528 * Function to respond to the button bar button to undock
530 void MainWin::undockWindowButton()
532 Pages* page = (Pages*)stackedWidget->currentWidget();
533 page->togglePageDocking();
537 * Function to respond to action on page selector context menu to toggle the
538 * dock status of the window associated with the page selectors current
541 void MainWin::toggleDockContextWindow()
543 QTreeWidgetItem *currentitem = treeWidget->currentItem();
545 /* Is this a page that has been inserted into the hash */
546 if (getFromHash(currentitem)) {
547 Pages* page = getFromHash(currentitem);
548 page->togglePageDocking();
553 * This function is called when the stack item is changed. Call
554 * the virtual function here. Avoids a window being undocked leaving
555 * a window at the top of the stack unpopulated.
557 void MainWin::stackItemChanged(int)
559 Pages* page = (Pages*)stackedWidget->currentWidget();
560 /* run the virtual function in case this class overrides it */
561 page->currentStackItem();
565 * Function to simplify insertion of QTreeWidgetItem <-> Page association
566 * into a double direction hash.
568 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
570 m_pagehash.insert(item, page);
571 m_widgethash.insert(page, item);
575 * Function to simplify removal of QTreeWidgetItem <-> Page association
576 * into a double direction hash.
578 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
580 /* I had all sorts of return status checking code here. Do we have a log
581 * level capability in bat. I would have left it in but it used printf's
582 * and it should really be some kind of log level facility ???
583 * ******FIXME********/
584 m_pagehash.remove(item);
585 m_widgethash.remove(page);
589 * Function to retrieve a Page* when the item in the page selector's tree is
592 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
594 return m_pagehash.value(item);
598 * Function to retrieve the page selectors tree widget item when the page is
601 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
603 return m_widgethash.value(page);
607 * Function to respond to action on page selector context menu to close the
610 void MainWin::closePage()
612 QTreeWidgetItem *currentitem = treeWidget->currentItem();
614 /* Is this a page that has been inserted into the hash */
615 if (getFromHash(currentitem)) {
616 Pages* page = getFromHash(currentitem);
617 if (page->isCloseable()) {
618 page->closeStackPage();
623 /* Quick function to return the current console */
624 Console *MainWin::currentConsole()
626 return m_currentConsole;
628 /* Quick function to return the tree item for the director */
629 QTreeWidgetItem *MainWin::currentTopItem()
631 return m_currentConsole->directorTreeItem();
634 /* Preferences menu item clicked */
635 void MainWin::setPreferences()
638 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
639 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
640 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
641 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
642 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
643 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
644 prefs.recordSpinBox->setValue(m_recordLimitVal);
645 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
646 prefs.daysSpinBox->setValue(m_daysLimitVal);
647 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
648 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
649 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
654 /* Preferences dialog */
655 prefsDialog::prefsDialog()
660 void prefsDialog::accept()
663 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
664 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
665 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
666 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
667 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
668 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
669 mainWin->m_recordLimitVal = this->recordSpinBox->value();
670 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
671 mainWin->m_daysLimitVal = this->daysSpinBox->value();
672 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
673 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
674 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
675 QSettings settings("www.bacula.org", "bat");
676 settings.beginGroup("Debug");
677 settings.setValue("commDebug", mainWin->m_commDebug);
678 settings.setValue("displayAll", mainWin->m_displayAll);
679 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
680 settings.setValue("commandDebug", mainWin->m_commandDebug);
681 settings.setValue("miscDebug", mainWin->m_miscDebug);
683 settings.beginGroup("JobList");
684 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
685 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
686 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
687 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
689 settings.beginGroup("Messages");
690 settings.setValue("checkMessages", mainWin->m_checkMessages);
691 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
693 settings.beginGroup("Misc");
694 settings.setValue("longList", mainWin->m_longList);
696 foreach(Console *console, mainWin->m_consoleHash) {
697 console->startTimer();
701 void prefsDialog::reject()
704 mainWin->set_status("Canceled");
707 /* read preferences for the prefences dialog box */
708 void MainWin::readPreferences()
710 QSettings settings("www.bacula.org", "bat");
711 settings.beginGroup("Debug");
712 m_commDebug = settings.value("commDebug", false).toBool();
713 m_displayAll = settings.value("displayAll", false).toBool();
714 m_sqlDebug = settings.value("sqlDebug", false).toBool();
715 m_commandDebug = settings.value("commandDebug", false).toBool();
716 m_miscDebug = settings.value("miscDebug", false).toBool();
718 settings.beginGroup("JobList");
719 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
720 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
721 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
722 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
724 settings.beginGroup("Messages");
725 m_checkMessages = settings.value("checkMessages", false).toBool();
726 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
728 settings.beginGroup("Misc");
729 m_longList = settings.value("longList", false).toBool();