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 "restore/restoretree.h"
50 #include "help/help.h"
51 #include "jobs/jobs.h"
54 * Daemon message callback
56 void message_callback(int /* type */, char *msg)
58 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
61 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
63 m_dtformat = "yyyy-MM-dd HH:mm:ss";
65 setupUi(this); /* Setup UI defined by main.ui (designer) */
66 register_message_callback(message_callback);
69 treeWidget->setColumnCount(1);
70 treeWidget->setHeaderLabel("Select Page");
71 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
83 foreach(Console *console, m_consoleHash) {
84 console->connect_dir();
86 m_currentConsole = (Console*)getFromHash(m_firstItem);
87 m_currentConsole->setCurrent();
89 QString directoryResourceName;
90 m_currentConsole->getDirResName(directoryResourceName);
91 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
95 void MainWin::createPages()
98 QTreeWidgetItem *item, *topItem;
102 foreach_res(dir, R_DIRECTOR) {
104 /* Create console tree stacked widget item */
105 m_currentConsole = new Console(stackedWidget);
106 m_currentConsole->setDirRes(dir);
107 m_currentConsole->readSettings();
109 /* The top tree item representing the director */
110 topItem = new QTreeWidgetItem(treeWidget);
111 topItem->setText(0, dir->name());
112 topItem->setIcon(0, QIcon(":images/server.png"));
113 /* Set background to grey for ease of identification of inactive Director */
114 QBrush greyBrush(Qt::lightGray);
115 topItem->setBackground(0, greyBrush);
116 m_currentConsole->setDirectorTreeItem(topItem);
117 m_consoleHash.insert(topItem, m_currentConsole);
119 /* Create Tree Widget Item */
120 item = new QTreeWidgetItem(topItem);
121 item->setText(0, "Console");
122 if (!m_firstItem){ m_firstItem = item; }
123 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
125 /* insert the cosole and tree widget item into the hashes */
126 hashInsert(item, m_currentConsole);
128 /* Set Color of treeWidgetItem for the console
129 * It will be set to green in the console class if the connection is made.
131 QBrush redBrush(Qt::red);
132 item->setForeground(0, redBrush);
133 m_currentConsole->dockPage();
136 * Create instances in alphabetic order of the rest
137 * of the classes that will by default exist under each Director.
143 createPageJobList("", "", "", "", NULL);
148 treeWidget->expandItem(topItem);
149 stackedWidget->setCurrentWidget(m_currentConsole);
155 * create an instance of the the joblist class on the stack
157 void MainWin::createPageJobList(const QString &media, const QString &client,
158 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
160 QTreeWidgetItem *holdItem;
162 /* save current tree widget item in case query produces no results */
163 holdItem = treeWidget->currentItem();
164 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
165 /* If this is a query of jobs on a specific media */
166 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
167 joblist->setCurrent();
168 /* did query produce results, if not close window and set back to hold */
169 if (joblist->m_resultCount == 0) {
170 joblist->closeStackPage();
171 treeWidget->setCurrentItem(holdItem);
177 * Handle up and down arrow keys for the command line
180 void MainWin::keyPressEvent(QKeyEvent *event)
182 if (m_cmd_history.size() == 0) {
186 switch (event->key()) {
188 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
195 if (m_cmd_last == 0) {
199 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
200 m_cmd_last = m_cmd_history.size() - 1;
209 lineEdit->setText(m_cmd_history[m_cmd_last]);
212 void MainWin::createConnections()
214 /* Connect signals to slots */
215 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
216 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
217 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
218 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
219 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
220 connect(treeWidget, SIGNAL(
221 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
222 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
223 connect(stackedWidget, SIGNAL(currentChanged(int)),
224 this, SLOT(stackItemChanged(int)));
225 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
226 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
227 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
228 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
229 connect(actionBrowse, SIGNAL(triggered()), this, SLOT(browseButtonClicked()));
230 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
231 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
232 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
233 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
234 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
238 * Reimplementation of QWidget closeEvent virtual function
240 void MainWin::closeEvent(QCloseEvent *event)
243 foreach(Console *console, m_consoleHash){
244 console->writeSettings();
245 console->terminate();
248 foreach(Pages *page, m_pagehash) {
249 if (!page->isDocked())
254 void MainWin::writeSettings()
256 QSettings settings("bacula.org", "bat");
258 settings.beginGroup("MainWin");
259 settings.setValue("winSize", size());
260 settings.setValue("winPos", pos());
261 settings.setValue("state", saveState());
265 void MainWin::readSettings()
267 QSettings settings("bacula.org", "bat");
269 settings.beginGroup("MainWin");
270 resize(settings.value("winSize", QSize(1041, 801)).toSize());
271 move(settings.value("winPos", QPoint(200, 150)).toPoint());
272 restoreState(settings.value("state").toByteArray());
277 * This subroutine is called with an item in the Page Selection window
280 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
282 /* Is this a page that has been inserted into the hash */
283 if (getFromHash(item)) {
284 Pages* page = getFromHash(item);
285 int stackindex=stackedWidget->indexOf(page);
287 if (stackindex >= 0) {
288 stackedWidget->setCurrentWidget(page);
290 /* run the virtual function in case this class overrides it */
291 page->PgSeltreeWidgetClicked();
296 * Called with a change of the highlighed tree widget item in the page selector.
298 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
300 Pages *previousPage, *nextPage;
301 Console *previousConsole, *nextConsole;
303 /* first determine the next item */
305 /* knowing the treeWidgetItem, get the page from the hash */
306 nextPage = getFromHash(currentitem);
307 nextConsole = m_consoleHash.value(currentitem);
308 /* Is this a page that has been inserted into the hash */
310 nextConsole = nextPage->console();
311 /* then is it a treeWidgetItem representing a director */
312 } else if (nextConsole) {
313 /* let the next page BE the console */
314 nextPage = nextConsole;
316 /* Should never get here */
321 /* The Previous item */
323 /* this condition prevents a segfault. The first time there is no previousitem*/
325 /* knowing the treeWidgetItem, get the page from the hash */
326 previousPage = getFromHash(previousitem);
327 previousConsole = m_consoleHash.value(previousitem);
329 previousConsole = previousPage->console();
330 } else if (previousConsole) {
331 previousPage = previousConsole;
333 if ((previousPage) || (previousConsole)) {
334 if (nextConsole != previousConsole) {
335 /* remove connections to the current console */
336 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
337 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
338 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
339 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
340 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
341 QBrush greyBrush(Qt::lightGray);
342 dirItem->setBackground(0, greyBrush);
344 /* make sure the close window and toggle dock options are removed */
345 treeWidget->removeAction(actionClosePage);
346 treeWidget->removeAction(actionToggleDock);
347 /* Is this a page that has been inserted into the hash */
349 foreach(QAction* pageaction, previousPage->m_contextActions) {
350 treeWidget->removeAction(pageaction);
356 /* process the current (next) item */
358 if ((nextPage) || (nextConsole)) {
359 if (nextConsole != previousConsole) {
360 /* make connections to the current console */
361 m_currentConsole = nextConsole;
362 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
363 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
364 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
365 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
366 /* Set director's tree widget background to magenta for ease of identification */
367 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
368 QBrush magentaBrush(Qt::magenta);
369 dirItem->setBackground(0, magentaBrush);
371 /* set the value for the currently active console */
372 int stackindex = stackedWidget->indexOf(nextPage);
374 /* Is this page currently on the stack or is it undocked */
375 if (stackindex >= 0) {
376 /* put this page on the top of the stack */
377 stackedWidget->setCurrentIndex(stackindex);
379 /* it is undocked, raise it to the front */
382 /* for the page selectors menu action to dock or undock, set the text */
383 nextPage->setContextMenuDockText();
385 treeWidget->addAction(actionToggleDock);
386 /* if this page is closeable, and it has no childern, then add that action */
387 if ((nextPage->isCloseable()) && (currentitem->child(0) == NULL))
388 treeWidget->addAction(actionClosePage);
390 /* Add the actions to the Page Selectors tree widget that are part of the
391 * current items list of desired actions regardless of whether on top of stack*/
392 treeWidget->addActions(nextPage->m_contextActions);
396 void MainWin::labelButtonClicked()
401 void MainWin::runButtonClicked()
406 void MainWin::estimateButtonClicked()
411 void MainWin::browseButtonClicked()
416 void MainWin::restoreButtonClicked()
418 new prerestorePage();
422 * The user just finished typing a line in the command line edit box
424 void MainWin::input_line()
426 QString cmdStr = lineEdit->text(); /* Get the text */
427 lineEdit->clear(); /* clear the lineEdit box */
428 if (m_currentConsole->is_connected()) {
429 m_currentConsole->consoleCommand(cmdStr);
431 set_status("Director not connected. Click on connect button.");
433 m_cmd_history.append(cmdStr);
435 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
436 m_currentConsole->setCurrent();
440 void MainWin::about()
442 QMessageBox::about(this, tr("About bat"),
443 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
444 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
445 "<p>The <b>bat</b> is an administrative console"
446 " interface to the Director."));
451 Help::displayFile("index.html");
454 void MainWin::set_statusf(const char *fmt, ...)
459 va_start(arg_ptr, fmt);
460 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
465 void MainWin::set_status_ready()
467 set_status(" Ready");
470 void MainWin::set_status(const char *buf)
472 statusBar()->showMessage(buf);
476 * Function to respond to the button bar button to undock
478 void MainWin::undockWindowButton()
480 Pages* page = (Pages*)stackedWidget->currentWidget();
481 page->togglePageDocking();
485 * Function to respond to action on page selector context menu to toggle the
486 * dock status of the window associated with the page selectors current
489 void MainWin::toggleDockContextWindow()
491 QTreeWidgetItem *currentitem = treeWidget->currentItem();
493 /* Is this a page that has been inserted into the hash */
494 if (getFromHash(currentitem)) {
495 Pages* page = getFromHash(currentitem);
496 page->togglePageDocking();
501 * This function is called when the stack item is changed. Call
502 * the virtual function here. Avoids a window being undocked leaving
503 * a window at the top of the stack unpopulated.
505 void MainWin::stackItemChanged(int)
507 Pages* page = (Pages*)stackedWidget->currentWidget();
508 /* run the virtual function in case this class overrides it */
509 page->currentStackItem();
513 * Function to simplify insertion of QTreeWidgetItem <-> Page association
514 * into a double direction hash.
516 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
518 m_pagehash.insert(item, page);
519 m_widgethash.insert(page, item);
523 * Function to simplify removal of QTreeWidgetItem <-> Page association
524 * into a double direction hash.
526 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
528 /* I had all sorts of return status checking code here. Do we have a log
529 * level capability in bat. I would have left it in but it used printf's
530 * and it should really be some kind of log level facility ???
531 * ******FIXME********/
532 m_pagehash.remove(item);
533 m_widgethash.remove(page);
537 * Function to retrieve a Page* when the item in the page selector's tree is
540 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
542 return m_pagehash.value(item);
546 * Function to retrieve the page selectors tree widget item when the page is
549 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
551 return m_widgethash.value(page);
555 * Function to respond to action on page selector context menu to close the
558 void MainWin::closePage()
560 QTreeWidgetItem *currentitem = treeWidget->currentItem();
562 /* Is this a page that has been inserted into the hash */
563 if (getFromHash(currentitem)) {
564 Pages* page = getFromHash(currentitem);
565 if (page->isCloseable()) {
566 page->closeStackPage();
571 /* Quick function to return the current console */
572 Console *MainWin::currentConsole()
574 return m_currentConsole;
576 /* Quick function to return the tree item for the director */
577 QTreeWidgetItem *MainWin::currentTopItem()
579 return m_currentConsole->directorTreeItem();
582 /* Preferences menu item clicked */
583 void MainWin::setPreferences()
586 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
587 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
588 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
589 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
590 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
591 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
592 prefs.recordSpinBox->setValue(m_recordLimitVal);
593 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
594 prefs.daysSpinBox->setValue(m_daysLimitVal);
595 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
596 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
597 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
602 /* Preferences dialog */
603 prefsDialog::prefsDialog()
608 void prefsDialog::accept()
611 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
612 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
613 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
614 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
615 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
616 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
617 mainWin->m_recordLimitVal = this->recordSpinBox->value();
618 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
619 mainWin->m_daysLimitVal = this->daysSpinBox->value();
620 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
621 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
622 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
623 QSettings settings("www.bacula.org", "bat");
624 settings.beginGroup("Debug");
625 settings.setValue("commDebug", mainWin->m_commDebug);
626 settings.setValue("displayAll", mainWin->m_displayAll);
627 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
628 settings.setValue("commandDebug", mainWin->m_commandDebug);
629 settings.setValue("miscDebug", mainWin->m_miscDebug);
631 settings.beginGroup("JobList");
632 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
633 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
634 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
635 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
637 settings.beginGroup("Messages");
638 settings.setValue("checkMessages", mainWin->m_checkMessages);
639 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
641 settings.beginGroup("Misc");
642 settings.setValue("longList", mainWin->m_longList);
644 foreach(Console *console, mainWin->m_consoleHash) {
645 console->startTimer();
649 void prefsDialog::reject()
652 mainWin->set_status("Canceled");
655 /* read preferences for the prefences dialog box */
656 void MainWin::readPreferences()
658 QSettings settings("www.bacula.org", "bat");
659 settings.beginGroup("Debug");
660 m_commDebug = settings.value("commDebug", false).toBool();
661 m_displayAll = settings.value("displayAll", false).toBool();
662 m_sqlDebug = settings.value("sqlDebug", false).toBool();
663 m_commandDebug = settings.value("commandDebug", false).toBool();
664 m_miscDebug = settings.value("miscDebug", false).toBool();
666 settings.beginGroup("JobList");
667 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
668 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
669 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
670 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
672 settings.beginGroup("Messages");
673 m_checkMessages = settings.value("checkMessages", false).toBool();
674 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
676 settings.beginGroup("Misc");
677 m_longList = settings.value("longList", false).toBool();