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.png")));
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->display_text(cmdStr + "\n");
482 m_currentConsole->write_dir(cmdStr.toUtf8().data()); /* send to dir */
484 set_status("Director not connected. Click on connect button.");
486 m_cmd_history.append(cmdStr);
488 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
489 m_currentConsole->setCurrent();
493 void MainWin::about()
495 QMessageBox::about(this, tr("About bat"),
496 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
497 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
498 "<p>The <b>bat</b> is an administrative console"
499 " interface to the Director."));
504 Help::displayFile("index.html");
507 void MainWin::set_statusf(const char *fmt, ...)
512 va_start(arg_ptr, fmt);
513 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
518 void MainWin::set_status_ready()
520 set_status(" Ready");
523 void MainWin::set_status(const char *buf)
525 statusBar()->showMessage(buf);
529 * Function to respond to the button bar button to undock
531 void MainWin::undockWindowButton()
533 Pages* page = (Pages*)stackedWidget->currentWidget();
534 page->togglePageDocking();
538 * Function to respond to action on page selector context menu to toggle the
539 * dock status of the window associated with the page selectors current
542 void MainWin::toggleDockContextWindow()
544 QTreeWidgetItem *currentitem = treeWidget->currentItem();
546 /* Is this a page that has been inserted into the hash */
547 if (getFromHash(currentitem)) {
548 Pages* page = getFromHash(currentitem);
549 page->togglePageDocking();
554 * This function is called when the stack item is changed. Call
555 * the virtual function here. Avoids a window being undocked leaving
556 * a window at the top of the stack unpopulated.
558 void MainWin::stackItemChanged(int)
560 Pages* page = (Pages*)stackedWidget->currentWidget();
561 /* run the virtual function in case this class overrides it */
562 page->currentStackItem();
566 * Function to simplify insertion of QTreeWidgetItem <-> Page association
567 * into a double direction hash.
569 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
571 m_pagehash.insert(item, page);
572 m_widgethash.insert(page, item);
576 * Function to simplify removal of QTreeWidgetItem <-> Page association
577 * into a double direction hash.
579 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
581 /* I had all sorts of return status checking code here. Do we have a log
582 * level capability in bat. I would have left it in but it used printf's
583 * and it should really be some kind of log level facility ???
584 * ******FIXME********/
585 m_pagehash.remove(item);
586 m_widgethash.remove(page);
590 * Function to retrieve a Page* when the item in the page selector's tree is
593 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
595 return m_pagehash.value(item);
599 * Function to retrieve the page selectors tree widget item when the page is
602 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
604 return m_widgethash.value(page);
608 * Function to respond to action on page selector context menu to close the
611 void MainWin::closePage()
613 QTreeWidgetItem *currentitem = treeWidget->currentItem();
615 /* Is this a page that has been inserted into the hash */
616 if (getFromHash(currentitem)) {
617 Pages* page = getFromHash(currentitem);
618 if (page->isCloseable()) {
619 page->closeStackPage();
624 /* Quick function to return the current console */
625 Console *MainWin::currentConsole()
627 return m_currentConsole;
629 /* Quick function to return the tree item for the director */
630 QTreeWidgetItem *MainWin::currentTopItem()
632 return m_currentConsole->directorTreeItem();
635 /* Preferences menu item clicked */
636 void MainWin::setPreferences()
639 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
640 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
641 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
642 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
643 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
644 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
645 prefs.recordSpinBox->setValue(m_recordLimitVal);
646 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
647 prefs.daysSpinBox->setValue(m_daysLimitVal);
648 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
649 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
653 /* Preferences dialog */
654 prefsDialog::prefsDialog()
659 void prefsDialog::accept()
662 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
663 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
664 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
665 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
666 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
667 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
668 mainWin->m_recordLimitVal = this->recordSpinBox->value();
669 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
670 mainWin->m_daysLimitVal = this->daysSpinBox->value();
671 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
672 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
673 QSettings settings("www.bacula.org", "bat");
674 settings.beginGroup("Debug");
675 settings.setValue("commDebug", mainWin->m_commDebug);
676 settings.setValue("displayAll", mainWin->m_displayAll);
677 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
678 settings.setValue("commandDebug", mainWin->m_commandDebug);
679 settings.setValue("miscDebug", mainWin->m_miscDebug);
681 settings.beginGroup("JobList");
682 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
683 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
684 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
685 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
687 settings.beginGroup("Messages");
688 settings.setValue("checkMessages", mainWin->m_checkMessages);
689 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
691 foreach(Console *console, mainWin->m_consoleHash) {
692 console->startTimer();
696 void prefsDialog::reject()
699 mainWin->set_status("Canceled");
702 /* read preferences for the prefences dialog box */
703 void MainWin::readPreferences()
705 QSettings settings("www.bacula.org", "bat");
706 settings.beginGroup("Debug");
707 m_commDebug = settings.value("commDebug", false).toBool();
708 m_displayAll = settings.value("displayAll", false).toBool();
709 m_sqlDebug = settings.value("sqlDebug", false).toBool();
710 m_commandDebug = settings.value("commandDebug", false).toBool();
711 m_miscDebug = settings.value("miscDebug", false).toBool();
713 settings.beginGroup("JobList");
714 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
715 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
716 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
717 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
719 settings.beginGroup("Messages");
720 m_checkMessages = settings.value("checkMessages", false).toBool();
721 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();