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