]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/mainwin.cpp
kes Implement auto display of messages in bat.
[bacula/bacula] / bacula / src / qt-console / mainwin.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2007 Free Software Foundation Europe e.V.
5
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.
12
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.
17
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
21    02110-1301, USA.
22
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.
27 */
28
29 /*
30  *   Version $Id$
31  *
32  *  Main Window control for bat (qt-console)
33  *
34  *   Kern Sibbald, January MMVII
35  *
36  */ 
37
38 #include "bat.h"
39 #include "joblist/joblist.h"
40 #include "storage/storage.h"
41 #include "fileset/fileset.h"
42 #include "label/label.h"
43 #include "run/run.h"
44 #include "pages.h"
45 #include "restore/restore.h"
46 #include "medialist/medialist.h"
47 #include "joblist/joblist.h"
48 #include "clients/clients.h"
49
50 MainWin::MainWin(QWidget *parent) : QMainWindow(parent)
51 {
52
53    mainWin = this;
54    setupUi(this);                     /* Setup UI defined by main.ui (designer) */
55    treeWidget->clear();
56    treeWidget->setColumnCount(1);
57    treeWidget->setHeaderLabel("Select Page");
58    treeWidget->setContextMenuPolicy(Qt::ActionsContextMenu);
59
60    createPages();
61
62    resetFocus();
63
64    createConnections();
65
66    this->show();
67
68    readSettings();
69
70    foreach(Console *console, m_consoleHash){
71       console->connect();
72    }
73    m_currentConsole = (Console*)getFromHash(m_firstItem);
74    m_currentConsole->setCurrent();
75    /*  FIXME
76     *  I'd like to turn this into a debug item
77     *  DIRRES* dirres = m_currentConsole->getDirRes();
78     *  printf("Setting initial window to %s\n", dirres->name());
79     */
80 }
81
82 void MainWin::createPages()
83 {
84    DIRRES *dir;
85    QTreeWidgetItem *item, *topItem;
86    m_firstItem = NULL;
87
88    LockRes();
89    foreach_res(dir, R_DIRECTOR) {
90
91       /* Create console tree stacked widget item */
92       m_currentConsole = new Console(stackedWidget);
93       m_currentConsole->setDirRes(dir);
94       m_currentConsole->readSettings();
95
96       /* The top tree item representing the director */
97       topItem = createTopPage(dir->name());
98       topItem->setIcon(0, QIcon(":images/server.png"));
99       /* Set background to grey for ease of identification of inactive dirfector */
100       QBrush greyBrush(Qt::lightGray);
101       topItem->setBackground(0, greyBrush);
102       m_currentConsole->setDirectorTreeItem(topItem);
103       m_consoleHash.insert(topItem, m_currentConsole);
104
105       /* Create Tree Widget Item */
106       item = createPage("Console", topItem);
107       if (!m_firstItem){ m_firstItem = item; }
108
109       /* insert the cosole and tree widget item into the hashes */
110       hashInsert(item, m_currentConsole);
111
112       /* Set Color of treeWidgetItem for the console
113       * It will be set to green in the console class if the connection is made.
114       */
115       QBrush redBrush(Qt::red);
116       item->setForeground(0, redBrush);
117       m_currentConsole->dockPage();
118
119       /* create instances of the rest of the classes that will by default exist
120       * under each director */
121 //    createPagebRestore();
122       createPageMediaList();
123       QString emptymedia(""), emptyclient("");
124       createPageJobList(emptymedia, emptyclient, NULL);
125       createPageClients();
126       createPageStorage();
127       createPageFileSet();
128
129       treeWidget->expandItem(topItem);
130       stackedWidget->setCurrentWidget(m_currentConsole);
131    }
132    UnlockRes();
133 }
134
135 /*
136  * create an instance of the the brestore class on the stack
137  */
138 void MainWin::createPagebRestore()
139 {
140    bRestore* brestore = new bRestore();
141    brestore->dockPage();
142 }
143
144 /*
145  * create an instance of the the medialist class on the stack
146  */
147 void MainWin::createPageMediaList()
148 {
149    MediaList* medialist = new MediaList();
150    medialist->dockPage();
151 }
152
153 /*
154  * create an instance of the the joblist class on the stack
155  */
156 void MainWin::createPageJobList(QString &media, QString &client,
157               QTreeWidgetItem *parentTreeWidgetItem)
158 {
159    QTreeWidgetItem *holdItem;
160
161    /* save current tree widget item in case query produces no results */
162    holdItem = treeWidget->currentItem();
163    JobList* joblist = new JobList(media, client, parentTreeWidgetItem);
164    joblist->dockPage();
165    /* If this is a query of jobs on a specific media */
166    if ((media != "") || (client != "")) {
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);
172       }
173    }
174 }
175
176 /*
177  * create an instance of the the Clients class on the stack
178  */
179 void MainWin::createPageClients()
180 {
181    Clients* clients = new Clients();
182    clients->dockPage();
183 }
184
185 /*
186  * create an instance of the the storage class on the stack
187  */
188 void MainWin::createPageStorage()
189 {
190    Storage* storage = new Storage();
191    storage->dockPage();
192 }
193
194 /*
195  * create an instance of the the fileset class on the stack
196  */
197 void MainWin::createPageFileSet()
198 {
199    FileSet* fileset = new FileSet();
200    fileset->dockPage();
201 }
202
203 /* Create a root Tree Widget */
204 QTreeWidgetItem *MainWin::createTopPage(char *name)
205 {
206    QTreeWidgetItem *item = new QTreeWidgetItem(treeWidget);
207    item->setText(0, name);
208    return item;
209 }
210
211 /* Create A Tree Widget Item which will be associated with a Page in the stacked widget */
212 QTreeWidgetItem *MainWin::createPage(char *name, QTreeWidgetItem *parent)
213 {
214    QTreeWidgetItem *item = new QTreeWidgetItem(parent);
215    item->setText(0, name);
216    return item;
217 }
218
219 /*
220  * Handle up and down arrow keys for the command line
221  *  history.
222  */
223 void MainWin::keyPressEvent(QKeyEvent *event)
224 {
225    if (m_cmd_history.size() == 0) {
226       event->ignore();
227       return;
228    }
229    switch (event->key()) {
230    case Qt::Key_Down:
231       if (m_cmd_last < 0 || m_cmd_last >= (m_cmd_history.size()-1)) {
232          event->ignore();
233          return;
234       }
235       m_cmd_last++;
236       break;
237    case Qt::Key_Up:
238       if (m_cmd_last == 0) {
239          event->ignore();
240          return;
241       }
242       if (m_cmd_last < 0 || m_cmd_last > (m_cmd_history.size()-1)) {
243          m_cmd_last = m_cmd_history.size() - 1;
244       } else {
245          m_cmd_last--;
246       }
247       break;
248    default:
249       event->ignore();
250       return;
251    }
252    lineEdit->setText(m_cmd_history[m_cmd_last]);
253 }
254
255 void MainWin::createConnections()
256 {
257    /* Connect signals to slots */
258    connect(lineEdit, SIGNAL(returnPressed()), this, SLOT(input_line()));
259    connect(actionAbout_bat, SIGNAL(triggered()), this, SLOT(about()));
260    connect(treeWidget, SIGNAL(itemClicked(QTreeWidgetItem *, int)), this, 
261            SLOT(treeItemClicked(QTreeWidgetItem *, int)));
262    connect(treeWidget, SIGNAL(
263            currentItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)),
264            this, SLOT(treeItemChanged(QTreeWidgetItem *, QTreeWidgetItem *)));
265    connect(stackedWidget, SIGNAL(currentChanged(int)),
266            this, SLOT(stackItemChanged(int)));
267    connect(actionQuit, SIGNAL(triggered()), app, SLOT(closeAllWindows()));
268    connect(actionLabel, SIGNAL(triggered()), this,  SLOT(labelButtonClicked()));
269    connect(actionRun, SIGNAL(triggered()), this,  SLOT(runButtonClicked()));
270    connect(actionRestore, SIGNAL(triggered()), this,  SLOT(restoreButtonClicked()));
271    connect(actionUndock, SIGNAL(triggered()), this,  SLOT(undockWindowButton()));
272    connect(actionToggleDock, SIGNAL(triggered()), this,  SLOT(toggleDockContextWindow()));
273    connect(actionClosePage, SIGNAL(triggered()), this,  SLOT(closePage()));
274    connect(actionPreferences, SIGNAL(triggered()), this,  SLOT(setPreferences()));
275 }
276
277 /* 
278  * Reimplementation of QWidget closeEvent virtual function   
279  */
280 void MainWin::closeEvent(QCloseEvent *event)
281 {
282    writeSettings();
283    foreach(Console *console, m_consoleHash){
284       console->writeSettings();
285       console->terminate();
286    }
287    event->accept();
288    foreach(Pages *page, m_pagehash) {
289       if (!page->isDocked())
290          page->close();
291    }
292 }
293
294 void MainWin::writeSettings()
295 {
296    QSettings settings("bacula.org", "bat");
297
298    settings.beginGroup("MainWin");
299    settings.setValue("winSize", size());
300    settings.setValue("winPos", pos());
301    settings.endGroup();
302 }
303
304 void MainWin::readSettings()
305
306    QSettings settings("bacula.org", "bat");
307
308    settings.beginGroup("MainWin");
309    resize(settings.value("winSize", QSize(1041, 801)).toSize());
310    move(settings.value("winPos", QPoint(200, 150)).toPoint());
311    settings.endGroup();
312 }
313
314 /*
315  * This subroutine is called with an item in the Page Selection window
316  *   is clicked 
317  */
318 void MainWin::treeItemClicked(QTreeWidgetItem *item, int /*column*/)
319 {
320    /* Is this a page that has been inserted into the hash  */
321    if (getFromHash(item)) {
322       Pages* page = getFromHash(item);
323       int stackindex=stackedWidget->indexOf(page);
324
325       if (stackindex >= 0) {
326          stackedWidget->setCurrentWidget(page);
327       }
328       /* run the virtual function in case this class overrides it */
329       page->PgSeltreeWidgetClicked();
330    }
331 }
332
333 /*
334  * Called with a change of the highlighed tree widget item in the page selector.
335  */
336 void MainWin::treeItemChanged(QTreeWidgetItem *currentitem, QTreeWidgetItem *previousitem)
337 {
338    Pages *previousPage, *nextPage;
339    Console *previousConsole, *nextConsole;
340
341    /* first determine the next item */
342
343    /* knowing the treeWidgetItem, get the page from the hash */
344    nextPage = getFromHash(currentitem);
345    nextConsole = m_consoleHash.value(currentitem);
346    /* Is this a page that has been inserted into the hash  */
347    if (nextPage) {
348       nextConsole = nextPage->console();
349       /* then is it a treeWidgetItem representing a director */
350    } else if (nextConsole) {
351       /* let the next page BE the console */
352       nextPage = nextConsole;
353    } else {
354       /* Should never get here */
355       nextPage = NULL;
356       nextConsole = NULL;
357    }
358           
359    /* The Previous item */
360
361    /* this condition prevents a segfault.  The first time there is no previousitem*/
362    if (previousitem) {
363       /* knowing the treeWidgetItem, get the page from the hash */
364       previousPage = getFromHash(previousitem);
365       previousConsole = m_consoleHash.value(previousitem);
366       if (previousPage) {
367          previousConsole = previousPage->console();
368       } else if (previousConsole) {
369          previousPage = previousConsole;
370       }
371       if ((previousPage) || (previousConsole)) {
372          if (nextConsole != previousConsole) {
373             /* remove connections to the current console */
374             disconnect(actionConnect, SIGNAL(triggered()), previousConsole, SLOT(connect()));
375             disconnect(actionStatusDir, SIGNAL(triggered()), previousConsole, SLOT(status_dir()));
376             disconnect(actionMessages, SIGNAL(triggered()), previousConsole, SLOT(messages()));
377             disconnect(actionSelectFont, SIGNAL(triggered()), previousConsole, SLOT(set_font()));
378             QTreeWidgetItem *dirItem = previousConsole->directorTreeItem();
379             QBrush greyBrush(Qt::lightGray);
380             dirItem->setBackground(0, greyBrush);
381          }
382          /* make sure the close window and toggle dock options are removed */
383          treeWidget->removeAction(actionClosePage);
384          treeWidget->removeAction(actionToggleDock);
385          /* Is this a page that has been inserted into the hash  */
386          if (previousPage) {
387             foreach(QAction* pageaction, previousPage->m_contextActions) {
388                treeWidget->removeAction(pageaction);
389             }
390          } 
391       }
392    }
393
394    /* process the current (next) item */
395    
396    if ((nextPage) || (nextConsole)) {
397       if (nextConsole != previousConsole) {
398          /* make connections to the current console */
399          m_currentConsole = nextConsole;
400          connect(actionConnect, SIGNAL(triggered()), m_currentConsole, SLOT(connect()));
401          connect(actionSelectFont, SIGNAL(triggered()), m_currentConsole, SLOT(set_font()));
402          connect(actionStatusDir, SIGNAL(triggered()), m_currentConsole, SLOT(status_dir()));
403          connect(actionMessages, SIGNAL(triggered()), m_currentConsole, SLOT(messages()));
404          /* Set director's tree widget background to magenta for ease of identification */
405          QTreeWidgetItem *dirItem = m_currentConsole->directorTreeItem();
406          QBrush magentaBrush(Qt::magenta);
407          dirItem->setBackground(0, magentaBrush);
408       }
409       /* set the value for the currently active console */
410       int stackindex = stackedWidget->indexOf(nextPage);
411    
412       /* Is this page currently on the stack or is it undocked */
413       if (stackindex >= 0) {
414          /* put this page on the top of the stack */
415          stackedWidget->setCurrentIndex(stackindex);
416       } else {
417          /* it is undocked, raise it to the front */
418          nextPage->raise();
419       }
420       /* for the page selectors menu action to dock or undock, set the text */
421       nextPage->setContextMenuDockText();
422
423       treeWidget->addAction(actionToggleDock);
424       /* if this page is closeable, then add that action */
425       if (nextPage->isCloseable()) {
426          treeWidget->addAction(actionClosePage);
427       }
428
429       /* Add the actions to the Page Selectors tree widget that are part of the
430        * current items list of desired actions regardless of whether on top of stack*/
431       treeWidget->addActions(nextPage->m_contextActions);
432    }
433 }
434
435 void MainWin::labelButtonClicked() 
436 {
437    new labelPage();
438 }
439
440 void MainWin::runButtonClicked() 
441 {
442    new runPage();
443 }
444
445 void MainWin::restoreButtonClicked() 
446 {
447    new prerestorePage();
448 }
449
450 /*
451  * The user just finished typing a line in the command line edit box
452  */
453 void MainWin::input_line()
454 {
455    QString cmdStr = lineEdit->text();    /* Get the text */
456    lineEdit->clear();                    /* clear the lineEdit box */
457    if (m_currentConsole->is_connected()) {
458       m_currentConsole->display_text(cmdStr + "\n");
459       m_currentConsole->write_dir(cmdStr.toUtf8().data());         /* send to dir */
460    } else {
461       set_status("Director not connected. Click on connect button.");
462    }
463    m_cmd_history.append(cmdStr);
464    m_cmd_last = -1;
465    if (treeWidget->currentItem() != getFromHash(m_currentConsole))
466       m_currentConsole->setCurrent();
467 }
468
469
470 void MainWin::about()
471 {
472    QMessageBox::about(this, tr("About bat"),
473       tr("<br><h2>bat 1.0, by Dirk H Bartley and Kern Sibbald</h2>"
474          "<p>Copyright &copy; " BYEAR " Free Software Foundation Europe e.V."
475          "<p>The <b>bat</b> is an administrative console"
476          " interface to the Director."));
477 }
478
479 void MainWin::set_statusf(const char *fmt, ...)
480 {
481    va_list arg_ptr;
482    char buf[1000];
483    int len;
484    va_start(arg_ptr, fmt);
485    len = bvsnprintf(buf, sizeof(buf), fmt, arg_ptr);
486    va_end(arg_ptr);
487    set_status(buf);
488 }
489
490 void MainWin::set_status_ready()
491 {
492    set_status(" Ready");
493 }
494
495 void MainWin::set_status(const char *buf)
496 {
497    statusBar()->showMessage(buf);
498 }
499
500 /*
501  * Function to respond to the button bar button to undock
502  */
503 void MainWin::undockWindowButton()
504 {
505    Pages* page = (Pages*)stackedWidget->currentWidget();
506    page->togglePageDocking();
507 }
508
509 /*
510  * Function to respond to action on page selector context menu to toggle the 
511  * dock status of the window associated with the page selectors current
512  * tree widget item.
513  */
514 void MainWin::toggleDockContextWindow()
515 {
516    QTreeWidgetItem *currentitem = treeWidget->currentItem();
517    
518    /* Is this a page that has been inserted into the hash  */
519    if (getFromHash(currentitem)) {
520       Pages* page = getFromHash(currentitem);
521       page->togglePageDocking();
522    }
523 }
524
525 /*
526  * This function is called when the stack item is changed.  Call
527  * the virtual function here.  Avoids a window being undocked leaving
528  * a window at the top of the stack unpopulated.
529  */
530 void MainWin::stackItemChanged(int)
531 {
532    Pages* page = (Pages*)stackedWidget->currentWidget();
533    /* run the virtual function in case this class overrides it */
534    page->currentStackItem();
535 }
536
537 /*
538  * Function to simplify insertion of QTreeWidgetItem <-> Page association
539  * into a double direction hash.
540  */
541 void MainWin::hashInsert(QTreeWidgetItem *item, Pages *page)
542 {
543    m_pagehash.insert(item, page);
544    m_widgethash.insert(page, item);
545 }
546
547 /*
548  * Function to simplify removal of QTreeWidgetItem <-> Page association
549  * into a double direction hash.
550  */
551 void MainWin::hashRemove(QTreeWidgetItem *item, Pages *page)
552 {
553    /* I had all sorts of return status checking code here.  Do we have a log
554     * level capability in bat.  I would have left it in but it used printf's
555     * and it should really be some kind of log level facility ???
556     * ******FIXME********/
557    m_pagehash.remove(item);
558    m_widgethash.remove(page);
559 }
560
561 /*
562  * Function to retrieve a Page* when the item in the page selector's tree is
563  * known.
564  */
565 Pages* MainWin::getFromHash(QTreeWidgetItem *item)
566 {
567    return m_pagehash.value(item);
568 }
569
570 /*
571  * Function to retrieve the page selectors tree widget item when the page is
572  * known.
573  */
574 QTreeWidgetItem* MainWin::getFromHash(Pages *page)
575 {
576    return m_widgethash.value(page);
577 }
578
579 /*
580  * Function to respond to action on page selector context menu to close the
581  * current window.
582  */
583 void MainWin::closePage()
584 {
585    QTreeWidgetItem *currentitem = treeWidget->currentItem();
586    
587    /* Is this a page that has been inserted into the hash  */
588    if (getFromHash(currentitem)) {
589       Pages* page = getFromHash(currentitem);
590       if (page->isCloseable()) {
591          page->closeStackPage();
592       }
593    }
594 }
595
596 /* Quick function to return the current console */
597 Console *MainWin::currentConsole()
598 {
599    return m_currentConsole;
600 }
601 /* Quick function to return the tree item for the director */
602 QTreeWidgetItem *MainWin::currentTopItem()
603 {
604    return m_currentConsole->directorTreeItem();
605 }
606
607 /* Preferences menu item clicked */
608 void MainWin::setPreferences()
609 {
610    prefsDialog prefs;
611    prefs.commDebug->setCheckState(g_commDebug ? Qt::Checked : Qt::Unchecked);
612    prefs.displayAll->setCheckState(g_displayAll ? Qt::Checked : Qt::Unchecked);
613    prefs.exec();
614 }
615
616 /* Preferences dialog */
617 prefsDialog::prefsDialog()
618 {
619    setupUi(this);
620 }
621
622 void prefsDialog::accept()
623 {
624    this->hide();
625    g_commDebug = this->commDebug->checkState() == Qt::Checked;
626    g_displayAll = this->displayAll->checkState() == Qt::Checked;
627 }
628
629 void prefsDialog::reject()
630 {
631    this->hide();
632    mainWin->set_status("Canceled");
633 }