- version bump:
[barry.git] / gui / src / BackupWindow.cc
blob5062e10d01ac003bbd60f78be5909b7ff6f64dde
1 ///
2 /// \file BackupWindow.cc
3 /// GUI window class
4 ///
6 /*
7 Copyright (C) 2007, Net Direct Inc. (http://www.netdirect.ca/)
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
18 See the GNU General Public License in the COPYING file at the
19 root directory of this project for more details.
22 #include "BackupWindow.h"
23 #include "DeviceSelectDlg.h"
24 #include "PasswordDlg.h"
25 #include "ConfigDlg.h"
26 #include "util.h"
27 #include <gtkmm/aboutdialog.h>
28 #include <iostream>
29 #include <iomanip>
30 #include <sstream>
31 #include <unistd.h>
33 BackupWindow::BackupWindow(BaseObjectType *cobject,
34 const Glib::RefPtr<Gnome::Glade::Xml> &xml)
35 : Gtk::Window(cobject)
36 , m_xml(xml)
37 , m_recordTotal(0)
38 , m_finishedRecords(0)
39 , m_pProgressBar(0)
40 , m_pStatusBar(0)
41 , m_pBackupButton(0)
42 , m_pRestoreButton(0)
43 , m_scanned(false)
44 , m_working(false)
46 // setup menu signals
47 Gtk::MenuItem *pItem = 0;
48 m_xml->get_widget("menu_file_quit", pItem);
49 pItem->signal_activate().connect(
50 sigc::mem_fun(*this, &BackupWindow::on_file_quit));
52 m_xml->get_widget("menu_edit_config", pItem);
53 pItem->signal_activate().connect(
54 sigc::mem_fun(*this, &BackupWindow::on_edit_config));
56 m_xml->get_widget("menu_help_about", pItem);
57 pItem->signal_activate().connect(
58 sigc::mem_fun(*this, &BackupWindow::on_help_about));
60 // get various widget pointers we will use later
61 m_xml->get_widget("BackupButton", m_pBackupButton);
62 m_xml->get_widget("RestoreButton", m_pRestoreButton);
63 m_xml->get_widget("progressbar1", m_pProgressBar);
64 m_xml->get_widget("statusbar1", m_pStatusBar);
65 m_xml->get_widget("entry1", m_pPINEntry);
66 m_xml->get_widget("entry2", m_pDatabaseEntry);
68 // setup widget signals
69 m_pBackupButton->signal_clicked().connect(
70 sigc::mem_fun(*this, &BackupWindow::on_backup));
71 m_pRestoreButton->signal_clicked().connect(
72 sigc::mem_fun(*this, &BackupWindow::on_restore));
74 // setup thread dispatcher signals
75 m_signal_progress.connect(
76 sigc::mem_fun(*this, &BackupWindow::on_thread_progress));
77 m_signal_error.connect(
78 sigc::mem_fun(*this, &BackupWindow::on_thread_error));
79 m_signal_done.connect(
80 sigc::mem_fun(*this, &BackupWindow::on_thread_done));
81 m_signal_erase_db.connect(
82 sigc::mem_fun(*this, &BackupWindow::on_thread_erase_db));
84 // setup startup device scan
85 Glib::signal_timeout().connect(
86 sigc::mem_fun(*this, &BackupWindow::on_startup), 500);
88 m_pStatusBar->push("Ready");
89 m_pProgressBar->set_fraction(0.00);
91 // do this last so that any exceptions in the constructor
92 // won't cause a connected signal handler to a non-object
93 // (i.e. ~BackupWindow() won't get called if constructor throws)
94 m_signal_handler_connection = Glib::add_exception_handler(
95 sigc::mem_fun(*this, &BackupWindow::signal_exception_handler) );
98 BackupWindow::~BackupWindow()
100 // disconnect the signal, as we're going out of business
101 m_signal_handler_connection.disconnect();
104 void BackupWindow::ScanAndConnect()
106 m_pStatusBar->push("Scanning for devices...");
107 m_pStatusBar->show_now();
109 int tries = 0;
111 sac_retry:
112 tries++;
113 Barry::Probe probe;
114 uint32_t pin = 0;
115 int nSelection = -1;
117 if( probe.GetCount() > 1 ) {
118 DeviceSelectDlg dlg(probe);
119 if( dlg.run() == Gtk::RESPONSE_OK ) {
120 pin = dlg.GetPIN();
121 nSelection = probe.FindActive(pin);
123 else {
124 // no selection, exit
125 hide();
126 return;
129 else if( probe.GetCount() == 1 ) {
130 // default to first
131 pin = probe.Get(0).m_pin;
132 nSelection = 0;
134 else {
135 Gtk::MessageDialog msg("No BlackBerry devices found.");
136 msg.run();
137 hide();
138 return;
141 if( nSelection == -1 ) {
142 Gtk::MessageDialog msg("Internal error: unable to find pin.");
143 msg.run();
144 hide();
145 return;
148 bool out_of_tries = false, password_required = false;
149 int remaining_tries = 0;
150 try {
151 if( !m_dev.Connect(probe.Get(nSelection)) ) {
152 Gtk::MessageDialog msg(m_dev.get_last_error());
153 msg.run();
154 hide();
155 return;
158 catch( Barry::BadPassword &bp ) {
159 out_of_tries = bp.out_of_tries();
160 remaining_tries = bp.remaining_tries();
161 password_required = true;
163 catch( Barry::BadSize &bs ) {
164 std::cerr << "Barry::BadSize caught in ScanAndConnect: "
165 << bs.what() << std::endl;
166 if( tries < 2 ) {
167 // BadSize during connect at startup usually means
168 // the device didn't shutdown properly, so try
169 // a reset or two before we give up
170 Usb::Device dev(probe.Get(nSelection).m_dev);
171 dev.Reset();
172 sleep(2);
173 goto sac_retry;
175 else {
176 Gtk::MessageDialog msg(bs.what());
177 msg.run();
178 hide();
179 return;
183 if( password_required ) {
184 // try password repeatedly until out of tries or
185 // the user cancels... or success :-)
187 bool connected = false;
188 while( !connected && !out_of_tries ) try {
189 PasswordDlg dlg(remaining_tries);
190 if( dlg.run() == Gtk::RESPONSE_OK ) {
191 connected = m_dev.Password(dlg.GetPassword());
192 if( !connected ) {
193 Gtk::MessageDialog msg(m_dev.get_last_error());
194 msg.run();
195 hide();
196 return;
199 else {
200 // user cancelled
201 hide();
202 return;
205 catch( Barry::BadPassword &bp ) {
206 out_of_tries = bp.out_of_tries();
207 remaining_tries = bp.remaining_tries();
208 if( out_of_tries ) {
209 Gtk::MessageDialog msg(bp.what());
210 msg.run();
211 hide();
212 return;
216 if( !connected ) {
217 hide();
218 return;
222 std::ostringstream oss;
223 oss << std::hex << pin;
224 m_pPINEntry->set_text(oss.str());
226 // open configuration now that we know which device we're talking to
227 m_pConfig.reset( new ConfigFile(oss.str(), m_dev.GetDBDB()) );
229 m_pStatusBar->pop();
232 void BackupWindow::SetWorkingMode(const std::string &taskname)
234 m_working = true;
235 m_thread_error = false;
236 m_pBackupButton->set_sensitive(false);
237 m_pRestoreButton->set_sensitive(false);
238 m_pStatusBar->push(taskname + " in progress...");
239 m_pProgressBar->set_fraction(0.00);
242 void BackupWindow::ClearWorkingMode()
244 m_working = false;
245 m_pBackupButton->set_sensitive(true);
246 m_pRestoreButton->set_sensitive(true);
247 m_pStatusBar->pop();
248 if( m_finishedRecords >= m_recordTotal ) {
249 // only reset the progress bar on success
250 m_pProgressBar->set_fraction(0.00);
253 std::ostringstream oss;
254 oss << m_finishedRecords << " total records processed.";
255 m_pDatabaseEntry->set_text(oss.str());
258 void BackupWindow::UpdateProgress()
260 double done = (double)m_finishedRecords / m_recordTotal;
261 // never say 100% unless really done
262 if( done >= 1.0 && m_finishedRecords < m_recordTotal ) {
263 done = 0.99;
265 m_pProgressBar->set_fraction(done);
267 m_pDatabaseEntry->set_text(m_dev.GetThreadDBName());
272 void BackupWindow::signal_exception_handler()
274 try {
275 throw;
277 catch( Glib::Exception &e ) {
278 // This usually just means a missing .glade file,
279 // so we try to carry on.
280 std::cerr << "Glib::Exception caught in main: " << std::endl;
281 std::cerr << e.what() << std::endl;
282 Gtk::MessageDialog msg(e.what());
283 msg.run();
285 catch( ... ) {
286 // anything else, terminate window and pass on to next handler
287 // (which should be in main.cc)
288 hide();
289 throw;
294 //////////////////////////////////////////////////////////////////////////////
295 // signal handlers
297 void BackupWindow::on_backup()
299 // already working?
300 if( m_working ) {
301 Gtk::MessageDialog msg("Thread already in progress.");
302 msg.run();
303 return;
306 // make sure our target directory exists
307 if( !::CheckPath(m_pConfig->GetPath()) ) {
308 Gtk::MessageDialog msg("Could not create directory: " + m_pConfig->GetPath());
309 msg.run();
310 return;
313 // prepare for the progress bar
314 m_recordTotal = m_dev.GetDeviceRecordTotal(m_pConfig->GetBackupList());
315 m_finishedRecords = 0;
316 m_modeName = "Backup";
318 // anything to do?
319 if( m_recordTotal == 0 ) {
320 Gtk::MessageDialog msg("No databases selected in configuration.");
321 msg.run();
322 return;
325 // start the thread
326 m_working = m_dev.StartBackup(
327 DeviceInterface::AppComm(&m_signal_progress,
328 &m_signal_error,
329 &m_signal_done,
330 &m_signal_erase_db),
331 m_pConfig->GetBackupList(), m_pConfig->GetPath(),
332 m_pConfig->GetPIN());
333 if( !m_working ) {
334 Gtk::MessageDialog msg("Error starting backup thread: " +
335 m_dev.get_last_error());
336 msg.run();
339 // update the GUI
340 SetWorkingMode("Backup");
343 bool BackupWindow::PromptForRestoreTarball(std::string &restoreFilename,
344 const std::string &start_path)
346 char buffer[PATH_MAX];
347 char *buf = getcwd(buffer, PATH_MAX);
349 // start at the base path given... if it fails, just open
350 // the dialog where we are
351 chdir(start_path.c_str());
353 Gtk::FileChooserDialog dlg(*this, "Select backup to restore from");
354 dlg.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
355 dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
356 int result = dlg.run();
358 if( buf )
359 chdir(buf);
361 if( result != Gtk::RESPONSE_OK )
362 return false;
364 restoreFilename = dlg.get_filename();
365 return true;
368 void BackupWindow::on_restore()
370 // already working?
371 if( m_working ) {
372 Gtk::MessageDialog msg("Thread already in progress.");
373 msg.run();
374 return;
377 std::string restoreFilename;
378 if( !PromptForRestoreTarball(restoreFilename, m_pConfig->GetPath()) )
379 return; // nothing to do
381 // prepare for the progress bar
382 m_finishedRecords = 0;
383 m_modeName = "Restore";
385 // start the thread
386 m_working = m_dev.StartRestore(
387 DeviceInterface::AppComm(&m_signal_progress,
388 &m_signal_error,
389 &m_signal_done,
390 &m_signal_erase_db),
391 m_pConfig->GetRestoreList(), restoreFilename, &m_recordTotal);
392 // m_working = m_dev.StartRestoreAndBackup(
393 // DeviceInterface::AppComm(&m_signal_progress,
394 // &m_signal_error,
395 // &m_signal_done,
396 // &m_signal_erase_db),
397 // m_pConfig->GetRestoreList(), restoreFilename,
398 // m_pConfig->GetPath(), m_pConfig->GetPIN(),
399 // &m_recordTotal);
400 if( !m_working ) {
401 Gtk::MessageDialog msg("Error starting restore thread: " +
402 m_dev.get_last_error());
403 msg.run();
406 std::cerr << "m_recordTotal for restore: " << m_recordTotal << std::endl;
408 // update the GUI
409 SetWorkingMode("Restore");
412 void BackupWindow::on_file_quit()
414 m_dev.Disconnect();
415 hide();
418 void BackupWindow::on_edit_config()
420 ConfigDlg dlg(m_dev.GetDBDB(), *m_pConfig);
421 if( dlg.run() == Gtk::RESPONSE_OK ) {
422 m_pConfig->SetBackupList(dlg.GetBackupList());
423 m_pConfig->SetRestoreList(dlg.GetRestoreList());
424 if( !m_pConfig->Save() ) {
425 Gtk::MessageDialog msg("Error saving config: " +
426 m_pConfig->get_last_error());
427 msg.run();
432 void BackupWindow::on_help_about()
434 Gtk::AboutDialog dlg;
435 dlg.set_copyright("Copyright (C) 2007, Net Direct Inc.");
436 dlg.set_license(
437 " This program is free software; you can redistribute it and/or modify\n"
438 " it under the terms of the GNU General Public License as published by\n"
439 " the Free Software Foundation; either version 2 of the License, or\n"
440 " (at your option) any later version.\n"
441 "\n"
442 " This program is distributed in the hope that it will be useful,\n"
443 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
444 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
445 "\n"
446 " See the GNU General Public License in the COPYING file at the\n"
447 " root directory of this project for more details.\n");
449 std::vector<std::string> authors;
450 authors.push_back("Chris Frey <cdfrey@foursquare.net>");
452 dlg.set_authors(authors);
454 int major, minor;
455 const char *BarryVersion = Barry::Version(major, minor);
456 dlg.set_name("Barry Backup");
457 dlg.set_version("0.12");
458 dlg.set_comments(std::string("Using library: ") + BarryVersion);
459 dlg.set_website("http://www.netdirect.ca/software/packages/barry/");
460 dlg.run();
463 bool BackupWindow::on_startup()
465 if( !m_scanned ) {
466 ScanAndConnect();
467 m_scanned = true;
469 return false;
472 void BackupWindow::on_thread_progress()
474 m_finishedRecords++;
475 UpdateProgress();
478 void BackupWindow::on_thread_error()
480 m_thread_error = true;
482 Gtk::MessageDialog msg(m_modeName + " error: " + m_dev.get_last_thread_error());
483 msg.run();
486 void BackupWindow::on_thread_done()
488 if( !m_thread_error ) {
489 Gtk::MessageDialog msg(m_modeName + " complete!");
490 msg.run();
493 // done!
494 ClearWorkingMode();
495 m_working = false;
498 void BackupWindow::on_thread_erase_db()
500 std::string name = m_dev.GetThreadDBName();
501 m_pDatabaseEntry->set_text("Erasing database: " + name);
507 void on_showtext()
509 Glib::ustring text = pEntry->get_text();
510 Gtk::MessageDialog dialog("This is the text entered: " + text);
511 // dialog.set_secondary_text(text);
512 dialog.run();
515 void on_close()
517 // response(Gtk::RESPONSE_CLOSE);
518 // signal_delete_event().emit();
519 // Gtk::Main::quit();
520 hide();