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"
50 #include "jobs/jobs.h"
53 * Daemon message callback
55 void message_callback(int /* type */, char *msg)
57 QMessageBox::warning(mainWin, "Bat", msg, QMessageBox::Ok);
60 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
62 m_dtformat = "yyyy-MM-dd HH:mm:ss";
64 setupUi(this); /* Setup UI defined by main.ui (designer) */
65 register_message_callback(message_callback);
68 treeWidget->setColumnCount(1);
69 treeWidget->setHeaderLabel("Select Page");
70 treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
82 foreach(Console *console, m_consoleHash) {
83 console->connect_dir();
85 m_currentConsole = (Console*)getFromHash(m_firstItem);
86 m_currentConsole->setCurrent();
88 QString directoryResourceName;
89 m_currentConsole->getDirResName(directoryResourceName);
90 Pmsg1(000, "Setting initial window to %s\n", directoryResourceName.toUtf8().data());
94 void MainWin::createPages()
97 QTreeWidgetItem *item, *topItem;
101 foreach_res(dir, R_DIRECTOR) {
103 /* Create console tree stacked widget item */
104 m_currentConsole = new Console(stackedWidget);
105 m_currentConsole->setDirRes(dir);
106 m_currentConsole->readSettings();
108 /* The top tree item representing the director */
109 topItem = new QTreeWidgetItem(treeWidget);
110 topItem->setText(0, dir->name());
111 topItem->setIcon(0, QIcon(":images/server.png"));
112 /* Set background to grey for ease of identification of inactive Director */
113 QBrush greyBrush(Qt::lightGray);
114 topItem->setBackground(0, greyBrush);
115 m_currentConsole->setDirectorTreeItem(topItem);
116 m_consoleHash.insert(topItem, m_currentConsole);
118 /* Create Tree Widget Item */
119 item = new QTreeWidgetItem(topItem);
120 item->setText(0, "Console");
121 if (!m_firstItem){ m_firstItem = item; }
122 item->setIcon(0,QIcon(QString::fromUtf8(":images/utilities-terminal.svg")));
124 /* insert the cosole and tree widget item into the hashes */
125 hashInsert(item, m_currentConsole);
127 /* Set Color of treeWidgetItem for the console
128 * It will be set to green in the console class if the connection is made.
130 QBrush redBrush(Qt::red);
131 item->setForeground(0, redBrush);
132 m_currentConsole->dockPage();
135 * Create instances in alphabetic order of the rest
136 * of the classes that will by default exist under each Director.
142 createPageJobList("", "", "", "", NULL);
146 treeWidget->expandItem(topItem);
147 stackedWidget->setCurrentWidget(m_currentConsole);
153 * create an instance of the the joblist class on the stack
155 void MainWin::createPageJobList(const QString &media, const QString &client,
156 const QString &job, const QString &fileset, QTreeWidgetItem *parentTreeWidgetItem)
158 QTreeWidgetItem *holdItem;
160 /* save current tree widget item in case query produces no results */
161 holdItem = treeWidget->currentItem();
162 JobList* joblist = new JobList(media, client, job, fileset, parentTreeWidgetItem);
163 /* If this is a query of jobs on a specific media */
164 if ((media != "") || (client != "") || (job != "") || (fileset != "")) {
165 joblist->setCurrent();
166 /* did query produce results, if not close window and set back to hold */
167 if (joblist->m_resultCount == 0) {
168 joblist->closeStackPage();
169 treeWidget->setCurrentItem(holdItem);
175 * Handle up and down arrow keys for the command line
178 void MainWin::keyPressEvent(QKeyEvent *event)
180 if (m_cmd_history.size() == 0) {
184 switch (event->key()) {
186 if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
193 if (m_cmd_last == 0) {
197 if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
198 m_cmd_last = m_cmd_history.size() - 1;
207 lineEdit->setText(m_cmd_history[m_cmd_last]);
210 void MainWin::createConnections()
212 /* Connect signals to slots */
213 connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
214 connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
215 connect(actionBat_Help, SIGNAL(triggered()), this, SLOT(help()));
216 connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this,
217 SLOT(treeItemClicked(QTreeWidgetItem *, int)));
218 connect(treeWidget, SIGNAL(
219 currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
220 this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
221 connect(stackedWidget, SIGNAL(currentChanged(int)),
222 this, SLOT(stackItemChanged(int)));
223 connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
224 connect(actionLabel, SIGNAL(triggered()), this, SLOT(labelButtonClicked()));
225 connect(actionRun, SIGNAL(triggered()), this, SLOT(runButtonClicked()));
226 connect(actionEstimate, SIGNAL(triggered()), this, SLOT(estimateButtonClicked()));
227 connect(actionRestore, SIGNAL(triggered()), this, SLOT(restoreButtonClicked()));
228 connect(actionUndock, SIGNAL(triggered()), this, SLOT(undockWindowButton()));
229 connect(actionToggleDock, SIGNAL(triggered()), this, SLOT(toggleDockContextWindow()));
230 connect(actionClosePage, SIGNAL(triggered()), this, SLOT(closePage()));
231 connect(actionPreferences, SIGNAL(triggered()), this, SLOT(setPreferences()));
235 * Reimplementation of QWidget closeEvent virtual function
237 void MainWin::closeEvent(QCloseEvent *event)
240 foreach(Console *console, m_consoleHash){
241 console->writeSettings();
242 console->terminate();
245 foreach(Pages *page, m_pagehash) {
246 if (!page->isDocked())
251 void MainWin::writeSettings()
253 QSettings settings("bacula.org", "bat");
255 settings.beginGroup("MainWin");
256 settings.setValue("winSize", size());
257 settings.setValue("winPos", pos());
258 settings.setValue("state", saveState());
262 void MainWin::readSettings()
264 QSettings settings("bacula.org", "bat");
266 settings.beginGroup("MainWin");
267 resize(settings.value("winSize", QSize(1041, 801)).toSize());
268 move(settings.value("winPos", QPoint(200, 150)).toPoint());
269 restoreState(settings.value("state").toByteArray());
274 * This subroutine is called with an item in the Page Selection window
277 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
279 /* Is this a page that has been inserted into the hash */
280 if (getFromHash(item)) {
281 Pages* page = getFromHash(item);
282 int stackindex=stackedWidget->indexOf(page);
284 if (stackindex >= 0) {
285 stackedWidget->setCurrentWidget(page);
287 /* run the virtual function in case this class overrides it */
288 page->PgSeltreeWidgetClicked();
293 * Called with a change of the highlighed tree widget item in the page selector.
295 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
297 Pages *previousPage, *nextPage;
298 Console *previousConsole, *nextConsole;
300 /* first determine the next item */
302 /* knowing the treeWidgetItem, get the page from the hash */
303 nextPage = getFromHash(currentitem);
304 nextConsole = m_consoleHash.value(currentitem);
305 /* Is this a page that has been inserted into the hash */
307 nextConsole = nextPage->console();
308 /* then is it a treeWidgetItem representing a director */
309 } else if (nextConsole) {
310 /* let the next page BE the console */
311 nextPage = nextConsole;
313 /* Should never get here */
318 /* The Previous item */
320 /* this condition prevents a segfault. The first time there is no previousitem*/
322 /* knowing the treeWidgetItem, get the page from the hash */
323 previousPage = getFromHash(previousitem);
324 previousConsole = m_consoleHash.value(previousitem);
326 previousConsole = previousPage->console();
327 } else if (previousConsole) {
328 previousPage = previousConsole;
330 if ((previousPage) || (previousConsole)) {
331 if (nextConsole != previousConsole) {
332 /* remove connections to the current console */
333 disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect_dir()));
334 disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
335 disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
336 disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
337 QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
338 QBrush greyBrush(Qt::lightGray);
339 dirItem->setBackground(0, greyBrush);
341 /* make sure the close window and toggle dock options are removed */
342 treeWidget->removeAction(actionClosePage);
343 treeWidget->removeAction(actionToggleDock);
344 /* Is this a page that has been inserted into the hash */
346 foreach(QAction* pageaction, previousPage->m_contextActions) {
347 treeWidget->removeAction(pageaction);
353 /* process the current (next) item */
355 if ((nextPage) || (nextConsole)) {
356 if (nextConsole != previousConsole) {
357 /* make connections to the current console */
358 m_currentConsole = nextConsole;
359 connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect_dir()));
360 connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
361 connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
362 connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
363 /* Set director's tree widget background to magenta for ease of identification */
364 QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
365 QBrush magentaBrush(Qt::magenta);
366 dirItem->setBackground(0, magentaBrush);
368 /* set the value for the currently active console */
369 int stackindex = stackedWidget->indexOf(nextPage);
371 /* Is this page currently on the stack or is it undocked */
372 if (stackindex >= 0) {
373 /* put this page on the top of the stack */
374 stackedWidget->setCurrentIndex(stackindex);
376 /* it is undocked, raise it to the front */
379 /* for the page selectors menu action to dock or undock, set the text */
380 nextPage->setContextMenuDockText();
382 treeWidget->addAction(actionToggleDock);
383 /* if this page is closeable, then add that action */
384 if (nextPage->isCloseable()) {
385 treeWidget->addAction(actionClosePage);
388 /* Add the actions to the Page Selectors tree widget that are part of the
389 * current items list of desired actions regardless of whether on top of stack*/
390 treeWidget->addActions(nextPage->m_contextActions);
394 void MainWin::labelButtonClicked()
399 void MainWin::runButtonClicked()
404 void MainWin::estimateButtonClicked()
409 void MainWin::restoreButtonClicked()
411 new prerestorePage();
415 * The user just finished typing a line in the command line edit box
417 void MainWin::input_line()
419 QString cmdStr = lineEdit->text(); /* Get the text */
420 lineEdit->clear(); /* clear the lineEdit box */
421 if (m_currentConsole->is_connected()) {
422 m_currentConsole->consoleCommand(cmdStr);
424 set_status("Director not connected. Click on connect button.");
426 m_cmd_history.append(cmdStr);
428 if (treeWidget->currentItem() != getFromHash(m_currentConsole))
429 m_currentConsole->setCurrent();
433 void MainWin::about()
435 QMessageBox::about(this, tr("About bat"),
436 tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
437 "<p>Copyright © " BYEAR " Free Software Foundation Europe e.V."
438 "<p>The <b>bat</b> is an administrative console"
439 " interface to the Director."));
444 Help::displayFile("index.html");
447 void MainWin::set_statusf(const char *fmt, ...)
452 va_start(arg_ptr, fmt);
453 len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
458 void MainWin::set_status_ready()
460 set_status(" Ready");
463 void MainWin::set_status(const char *buf)
465 statusBar()->showMessage(buf);
469 * Function to respond to the button bar button to undock
471 void MainWin::undockWindowButton()
473 Pages* page = (Pages*)stackedWidget->currentWidget();
474 page->togglePageDocking();
478 * Function to respond to action on page selector context menu to toggle the
479 * dock status of the window associated with the page selectors current
482 void MainWin::toggleDockContextWindow()
484 QTreeWidgetItem *currentitem = treeWidget->currentItem();
486 /* Is this a page that has been inserted into the hash */
487 if (getFromHash(currentitem)) {
488 Pages* page = getFromHash(currentitem);
489 page->togglePageDocking();
494 * This function is called when the stack item is changed. Call
495 * the virtual function here. Avoids a window being undocked leaving
496 * a window at the top of the stack unpopulated.
498 void MainWin::stackItemChanged(int)
500 Pages* page = (Pages*)stackedWidget->currentWidget();
501 /* run the virtual function in case this class overrides it */
502 page->currentStackItem();
506 * Function to simplify insertion of QTreeWidgetItem <-> Page association
507 * into a double direction hash.
509 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
511 m_pagehash.insert(item, page);
512 m_widgethash.insert(page, item);
516 * Function to simplify removal of QTreeWidgetItem <-> Page association
517 * into a double direction hash.
519 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
521 /* I had all sorts of return status checking code here. Do we have a log
522 * level capability in bat. I would have left it in but it used printf's
523 * and it should really be some kind of log level facility ???
524 * ******FIXME********/
525 m_pagehash.remove(item);
526 m_widgethash.remove(page);
530 * Function to retrieve a Page* when the item in the page selector's tree is
533 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
535 return m_pagehash.value(item);
539 * Function to retrieve the page selectors tree widget item when the page is
542 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
544 return m_widgethash.value(page);
548 * Function to respond to action on page selector context menu to close the
551 void MainWin::closePage()
553 QTreeWidgetItem *currentitem = treeWidget->currentItem();
555 /* Is this a page that has been inserted into the hash */
556 if (getFromHash(currentitem)) {
557 Pages* page = getFromHash(currentitem);
558 if (page->isCloseable()) {
559 page->closeStackPage();
564 /* Quick function to return the current console */
565 Console *MainWin::currentConsole()
567 return m_currentConsole;
569 /* Quick function to return the tree item for the director */
570 QTreeWidgetItem *MainWin::currentTopItem()
572 return m_currentConsole->directorTreeItem();
575 /* Preferences menu item clicked */
576 void MainWin::setPreferences()
579 prefs.commDebug->setCheckState(m_commDebug ? Qt::Checked : Qt::Unchecked);
580 prefs.displayAll->setCheckState(m_displayAll ? Qt::Checked : Qt::Unchecked);
581 prefs.sqlDebug->setCheckState(m_sqlDebug ? Qt::Checked : Qt::Unchecked);
582 prefs.commandDebug->setCheckState(m_commandDebug ? Qt::Checked : Qt::Unchecked);
583 prefs.miscDebug->setCheckState(m_miscDebug ? Qt::Checked : Qt::Unchecked);
584 prefs.recordLimit->setCheckState(m_recordLimitCheck ? Qt::Checked : Qt::Unchecked);
585 prefs.recordSpinBox->setValue(m_recordLimitVal);
586 prefs.daysLimit->setCheckState(m_daysLimitCheck ? Qt::Checked : Qt::Unchecked);
587 prefs.daysSpinBox->setValue(m_daysLimitVal);
588 prefs.checkMessages->setCheckState(m_checkMessages ? Qt::Checked : Qt::Unchecked);
589 prefs.checkMessagesSpin->setValue(m_checkMessagesInterval);
590 prefs.executeLongCheckBox->setCheckState(m_longList ? Qt::Checked : Qt::Unchecked);
595 /* Preferences dialog */
596 prefsDialog::prefsDialog()
601 void prefsDialog::accept()
604 mainWin->m_commDebug = this->commDebug->checkState() == Qt::Checked;
605 mainWin->m_displayAll = this->displayAll->checkState() == Qt::Checked;
606 mainWin->m_sqlDebug = this->sqlDebug->checkState() == Qt::Checked;
607 mainWin->m_commandDebug = this->commandDebug->checkState() == Qt::Checked;
608 mainWin->m_miscDebug = this->miscDebug->checkState() == Qt::Checked;
609 mainWin->m_recordLimitCheck = this->recordLimit->checkState() == Qt::Checked;
610 mainWin->m_recordLimitVal = this->recordSpinBox->value();
611 mainWin->m_daysLimitCheck = this->daysLimit->checkState() == Qt::Checked;
612 mainWin->m_daysLimitVal = this->daysSpinBox->value();
613 mainWin->m_checkMessages = this->checkMessages->checkState() == Qt::Checked;
614 mainWin->m_checkMessagesInterval = this->checkMessagesSpin->value();
615 mainWin->m_longList = this->executeLongCheckBox->checkState() == Qt::Checked;
616 QSettings settings("www.bacula.org", "bat");
617 settings.beginGroup("Debug");
618 settings.setValue("commDebug", mainWin->m_commDebug);
619 settings.setValue("displayAll", mainWin->m_displayAll);
620 settings.setValue("sqlDebug", mainWin->m_sqlDebug);
621 settings.setValue("commandDebug", mainWin->m_commandDebug);
622 settings.setValue("miscDebug", mainWin->m_miscDebug);
624 settings.beginGroup("JobList");
625 settings.setValue("recordLimitCheck", mainWin->m_recordLimitCheck);
626 settings.setValue("recordLimitVal", mainWin->m_recordLimitVal);
627 settings.setValue("daysLimitCheck", mainWin->m_daysLimitCheck);
628 settings.setValue("daysLimitVal", mainWin->m_daysLimitVal);
630 settings.beginGroup("Messages");
631 settings.setValue("checkMessages", mainWin->m_checkMessages);
632 settings.setValue("checkMessagesInterval", mainWin->m_checkMessagesInterval);
634 settings.beginGroup("Misc");
635 settings.setValue("longList", mainWin->m_longList);
637 foreach(Console *console, mainWin->m_consoleHash) {
638 console->startTimer();
642 void prefsDialog::reject()
645 mainWin->set_status("Canceled");
648 /* read preferences for the prefences dialog box */
649 void MainWin::readPreferences()
651 QSettings settings("www.bacula.org", "bat");
652 settings.beginGroup("Debug");
653 m_commDebug = settings.value("commDebug", false).toBool();
654 m_displayAll = settings.value("displayAll", false).toBool();
655 m_sqlDebug = settings.value("sqlDebug", false).toBool();
656 m_commandDebug = settings.value("commandDebug", false).toBool();
657 m_miscDebug = settings.value("miscDebug", false).toBool();
659 settings.beginGroup("JobList");
660 m_recordLimitCheck = settings.value("recordLimitCheck", true).toBool();
661 m_recordLimitVal = settings.value("recordLimitVal", 150).toInt();
662 m_daysLimitCheck = settings.value("daysLimitCheck", false).toBool();
663 m_daysLimitVal = settings.value("daysLimitVal", 28).toInt();
665 settings.beginGroup("Messages");
666 m_checkMessages = settings.value("checkMessages", false).toBool();
667 m_checkMessagesInterval = settings.value("checkMessagesInterval", 28).toInt();
669 settings.beginGroup("Misc");
670 m_longList = settings.value("longList", false).toBool();