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