]> git.sur5r.net Git - bacula/bacula/blob - bacula/src/qt-console/restore/brestore.cpp
bat: fix drag&drop for new brestore screen
[bacula/bacula] / bacula / src / qt-console / restore / brestore.cpp
1 /*
2    Bacula® - The Network Backup Solution
3
4    Copyright (C) 2007-2009 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 three of the GNU Affero General Public
10    License as published by the Free Software Foundation and included
11    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 Affero 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 Kern Sibbald.
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  *
31  *  bRestore Class  (Eric's brestore)
32  *
33  *   Kern Sibbald, January MMVII
34  *
35  */ 
36
37 #include "bat.h"
38 #include "restore.h"
39 #include "util/fmtwidgetitem.h"
40
41 bRestore::bRestore()
42 {
43    m_name = tr("bRestore");
44    m_client = "";
45    setupUi(this);
46    pgInitialize();
47    QTreeWidgetItem* thisitem = mainWin->getFromHash(this);
48    thisitem->setIcon(0, QIcon(QString::fromUtf8(":images/browse.png")));
49    m_populated = false;
50    m_current = NULL;
51    RestoreList->setAcceptDrops(true);
52 }
53
54 void bRestore::setClient()
55 {
56    Pmsg0(000, "Repopulating client table\n");
57    // Select the same client, don't touch
58    if (m_client == ClientList->currentText()) {
59       return;
60    }
61    m_client = ClientList->currentText();
62    FileList->clearContents();
63    FileRevisions->clearContents();
64    JobList->clear();
65    JobList->setEnabled(true);
66    LocationEntry->clear();
67    m_path = "";
68    m_pathid = 0;
69
70    if (ClientList->currentIndex() < 1) {
71       JobList->setEnabled(false);
72       return;
73    }
74
75    JobList->addItem("Job list for " + m_client);
76
77    QString jobQuery =
78       "SELECT Job.Jobid AS JobId, Job.StartTime AS StartTime,"
79       " Job.Level AS Level,"
80       " Job.Name AS Name"
81       " FROM Job JOIN Client USING (ClientId)"
82       " WHERE"
83       " Job.JobStatus IN ('T','W') AND Job.Type='B' AND"
84       " Client.Name='" + m_client + "' ORDER BY StartTime DESC" ;
85
86    QString job;
87    QStringList results;
88    if (m_console->sql_cmd(jobQuery, results)) {
89       QStringList fieldlist;
90
91       /* Iterate through the record returned from the query */
92       foreach (QString resultline, results) {
93          fieldlist = resultline.split("\t");
94          job = fieldlist[1] + " " + fieldlist[3] + "(" + fieldlist[2] + ") " + fieldlist[0];
95          JobList->addItem(job, QVariant(fieldlist[0]));
96       }
97    }
98 }
99
100
101 void bRestore::setJob()
102 {
103    if (JobList->currentIndex() < 1) {
104       FileList->clearContents();
105       FileList->setRowCount(0);
106       FileRevisions->clearContents();
107       FileRevisions->setRowCount(0);
108       return ;
109    }
110    QStringList results;
111    QVariant tmp = JobList->itemData(JobList->currentIndex(), Qt::UserRole);
112
113    m_jobids = tmp.toString();
114    QString cmd = ".bvfs_get_jobids jobid=" + m_jobids;
115    if (MergeChk->checkState() == Qt::Checked) {
116       cmd.append(" all");
117    }
118
119    m_console->dir_cmd(cmd, results);
120
121    if (results.size() < 1) {
122       FileList->clearContents();
123       FileList->setRowCount(0);
124       FileRevisions->clearContents();
125       FileRevisions->setRowCount(0);
126       return;
127    }
128
129    m_jobids = results.at(0);
130    cmd = ".bvfs_update jobid=" + m_jobids;
131    m_console->dir_cmd(cmd, results);
132
133    Pmsg1(0, "jobids=%s\n", m_jobids.toLocal8Bit().constData());
134
135    displayFiles(m_pathid, QString(""));
136    Pmsg0(000, "update done\n");
137 }
138
139 extern int decode_stat(char *buf, struct stat *statp, int32_t *LinkFI);
140
141 void bRestore::displayFiles(int64_t pathid, QString path)
142 {
143    QString arg;
144    QStringList results;
145    QStringList fieldlist;
146    struct stat statp;
147    int32_t LinkFI;
148    int nb;
149    int row=0;
150    Freeze frz_lst(*FileList); /* disable updating*/
151    Freeze frz_rev(*FileRevisions); /* disable updating*/
152    FileList->clearContents();
153    FileRevisions->clearContents();
154    FileRevisions->setRowCount(0);
155
156    if (pathid > 0) {
157       arg = " pathid=" + QString().setNum(pathid);
158
159       if (path == "..") {
160          if (m_path == "/") {
161             m_path = "";
162          } else {
163             m_path.remove(QRegExp("[^/]+/$"));
164          }
165
166       } else if (path == "/" && m_path == "") {
167          m_path += path;
168
169       } else if (path != "/" && path != ".") {
170          m_path += path;
171       }
172    } else {
173       m_path = path;
174       arg = " path=\"" + m_path + "\"";
175    }
176    LocationEntry->setText(m_path);
177
178    QString q = ".bvfs_lsdir jobid=" + m_jobids + arg;
179    if (m_console->dir_cmd(q, results)) {
180       nb = results.size();
181       FileList->setRowCount(nb);
182       foreach (QString resultline, results) {
183          int col=0;
184          //PathId, FilenameId, fileid, jobid, lstat, path
185          fieldlist = resultline.split("\t");
186          TableItemFormatter item(*FileList, row++);
187          item.setFileType(col++, QString("folder")); // folder or file
188          item.setTextFld(col++, fieldlist.at(5)); // path
189          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
190                      &statp, &LinkFI);
191          item.setDateFld(col++, statp.st_mtime); // date
192          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
193       }
194    }
195
196    results.clear();
197    q = ".bvfs_lsfiles jobid=" + m_jobids + arg;
198    if (m_console->dir_cmd(q, results)) {
199       FileList->setRowCount(results.size() + nb);
200       foreach (QString resultline, results) {
201          int col=1;            // skip icon
202          //PathId, FilenameId, fileid, jobid, lstat, name
203          fieldlist = resultline.split("\t");
204          TableItemFormatter item(*FileList, row++);
205          item.setTextFld(col++, fieldlist.at(5)); // name
206          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
207                      &statp, &LinkFI);
208          item.setBytesFld(col++, QString().setNum(statp.st_size));
209          item.setDateFld(col++, statp.st_mtime);
210          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
211       }
212    }
213    FileList->verticalHeader()->hide();
214    FileList->resizeColumnsToContents();
215    FileList->resizeRowsToContents();
216    FileList->setEditTriggers(QAbstractItemView::NoEditTriggers);
217 }
218
219 void bRestore::PgSeltreeWidgetClicked()
220 {
221    if(!m_populated) {
222       setupPage();
223    }
224    if (!isOnceDocked()) {
225       dockPage();
226    }
227 }
228
229 void bRestore::displayFileVersion(QString pathid, QString fnid, 
230                                   QString client, QString filename)
231 {
232    int row=0;
233    struct stat statp;
234    int32_t LinkFI;
235    Freeze frz_rev(*FileRevisions); /* disable updating*/
236    FileRevisions->clearContents();
237    
238    QString q = ".bvfs_versions jobid=" + m_jobids +
239       " pathid=" + pathid + 
240       " fnid=" + fnid + 
241       " client=" + client;
242
243    if (VersionsChk->checkState() == Qt::Checked) {
244       q.append(" versions");
245    }
246
247    QStringList results;
248    QStringList fieldlist;
249    QString tmp;
250    if (m_console->dir_cmd(q, results)) {
251       FileRevisions->setRowCount(results.size());
252       foreach (QString resultline, results) {
253          int col=0;
254          // 0        1          2        3      4    5      6        7
255          //PathId, FilenameId, fileid, jobid, lstat, Md5, VolName, Inchanger
256          fieldlist = resultline.split("\t");
257          TableItemFormatter item(*FileRevisions, row++);
258          item.setInChanger(col++, fieldlist.at(7));    // inchanger
259          item.setTextFld(col++, fieldlist.at(6)); // Volume
260          item.setNumericFld(col++, fieldlist.at(3)); // JobId
261          decode_stat(fieldlist.at(4).toLocal8Bit().data(), 
262                      &statp, &LinkFI);
263          item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
264          item.setDateFld(col++, statp.st_mtime); // date
265          item.setTextFld(col++, fieldlist.at(5)); // chksum
266
267          // Adjust the fieldlist for drag&drop
268          fieldlist.removeLast(); // inchanger
269          fieldlist.removeLast(); // volname
270          fieldlist.removeLast(); // md5
271          fieldlist << m_path + filename;
272          item.widget(1)->setData(Qt::UserRole, fieldlist.join("\t")); // keep info
273       }
274    }
275    FileRevisions->verticalHeader()->hide();
276    FileRevisions->resizeColumnsToContents();
277    FileRevisions->resizeRowsToContents();
278    FileRevisions->setEditTriggers(QAbstractItemView::NoEditTriggers);
279 }
280
281 void bRestore::showInfoForFile(QTableWidgetItem *widget)
282 {
283    m_current = widget;
284    QTableWidgetItem *first = FileList->item(widget->row(), 1);
285    QStringList lst = first->data(Qt::UserRole).toString().split("\t");
286    if (lst.at(1) == "0") {      // no filenameid, should be a path
287       displayFiles(lst.at(0).toLongLong(), lst.at(5));
288    } else {
289       displayFileVersion(lst.at(0), lst.at(1), m_client, lst.at(5));
290    }
291 }
292
293 void bRestore::applyLocation()
294 {
295    displayFiles(0, LocationEntry->text());
296 }
297
298 void bRestore::clearVersions(QTableWidgetItem *item)
299 {
300    if (item != m_current) {
301       FileRevisions->clearContents();
302       FileRevisions->setRowCount(0);
303    }
304    m_current = item ;
305 }
306
307 void bRestore::setupPage()
308 {
309    ClientList->addItem("Client list");
310    ClientList->addItems(m_console->client_list);
311    connect(ClientList, SIGNAL(currentIndexChanged(int)), this, SLOT(setClient()));
312    connect(JobList, SIGNAL(currentIndexChanged(int)), this, SLOT(setJob()));
313    connect(FileList, SIGNAL(itemClicked(QTableWidgetItem*)), 
314            this, SLOT(clearVersions(QTableWidgetItem *)));
315    connect(FileList, SIGNAL(itemDoubleClicked(QTableWidgetItem*)), 
316            this, SLOT(showInfoForFile(QTableWidgetItem *)));
317    connect(LocationBp, SIGNAL(pressed()), this, SLOT(applyLocation()));
318    connect(MergeChk, SIGNAL(clicked()), this, SLOT(setJob()));
319
320    m_populated = true;
321 }
322
323 bRestore::~bRestore()
324 {
325 }
326
327 void bRestoreTable::mousePressEvent(QMouseEvent *event)
328 {
329    QTableWidget::mousePressEvent(event);
330
331    if (event->button() == Qt::LeftButton) {
332       dragStartPosition = event->pos();
333    }
334 }
335
336 // This event permits to send set custom data on drag&drop
337 // Don't forget to call original class if we are not interested
338 void bRestoreTable::mouseMoveEvent(QMouseEvent *event)
339 {
340    int lastrow=-1;
341
342    // Look just for drag&drop
343    if (!(event->buttons() & Qt::LeftButton)) {
344       QTableWidget::mouseMoveEvent(event);
345       return;
346    }
347    if ((event->pos() - dragStartPosition).manhattanLength()
348        < QApplication::startDragDistance())
349    {
350       QTableWidget::mouseMoveEvent(event);
351       return;
352    }
353
354    QList<QTableWidgetItem *> lst = selectedItems();
355    qDebug() << this << " selectedItems: " << lst;
356    if (lst.isEmpty()) {
357       return;
358    }
359
360    QDrag *drag = new QDrag(this);
361    QMimeData *mimeData = new QMimeData;
362    for (int i=0; i < lst.size(); i++) {
363       if (lastrow != lst[i]->row()) {
364          lastrow = lst[i]->row();
365          QTableWidgetItem *it = item(lastrow, 1);
366          mimeData->setText(it->data(Qt::UserRole).toString());
367          break;                  // at this time, we do it one by one
368       }
369    }
370    drag->setMimeData(mimeData);
371    drag->exec();
372 }
373
374 // This event is called when the drag item enters in the destination area
375 void bRestoreTable::dragEnterEvent(QDragEnterEvent *event)
376 {
377    if (event->source() == this) {
378       event->ignore();
379       return;
380    }
381    if (event->mimeData()->hasText()) {
382       event->acceptProposedAction();
383    } else {
384       event->ignore();
385    }
386 }
387
388 // It should not be essential to redefine this event, but it
389 // doesn't work if not defined
390 void bRestoreTable::dragMoveEvent(QDragMoveEvent *event)
391 {
392    if (event->mimeData()->hasText()) {
393       event->acceptProposedAction();
394    } else {
395       event->ignore();
396    }
397 }
398
399 void bRestoreTable::dropEvent(QDropEvent *event)
400 {
401    int col=1;
402    struct stat statp;
403    int32_t LinkFI;
404    if (event->mimeData()->hasText()) {
405       TableItemFormatter item(*this, rowCount());
406       setRowCount(rowCount() + 1);
407       QStringList fields = event->mimeData()->text().split("\t");
408       if (fields.size() != 6) {
409          event->ignore();
410          return;
411       }
412       if (fields.at(1) == "0") {
413          item.setFileType(0, "folder");
414       }
415       item.setTextFld(col++, fields.at(5)); // filename
416       decode_stat(fields.at(4).toLocal8Bit().data(), 
417                   &statp, &LinkFI);
418       item.setBytesFld(col++, QString().setNum(statp.st_size)); // size
419       item.setDateFld(col++, statp.st_mtime); // date
420       item.setNumericFld(col++, fields.at(3)); // jobid
421       item.setNumericFld(col++, fields.at(2)); // fileid
422       item.widget(1)->setData(Qt::UserRole, event->mimeData()->text());
423       event->acceptProposedAction();
424    } else {
425       event->ignore();
426    }
427 }
428