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