Updated copyright dates for 2009
[barry/progweb.git] / gui / src / BackupWindow.cc
blob16583c37b1cca792f2247b09a9978f72db99e3f4
1 ///
2 /// \file BackupWindow.cc
3 /// GUI window class
4 ///
6 /*
7 Copyright (C) 2007-2009, 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 "PromptDlg.h"
26 #include "ConfigDlg.h"
27 #include "util.h"
28 #include <gtkmm/aboutdialog.h>
29 #include <iostream>
30 #include <iomanip>
31 #include <sstream>
32 #include <unistd.h>
34 BackupWindow::BackupWindow(BaseObjectType *cobject,
35 const Glib::RefPtr<Gnome::Glade::Xml> &xml)
36 : Gtk::Window(cobject)
37 , m_xml(xml)
38 , m_recordTotal(0)
39 , m_finishedRecords(0)
40 , m_pProgressBar(0)
41 , m_pStatusBar(0)
42 , m_pBackupButton(0)
43 , m_pRestoreButton(0)
44 , m_pDeviceNameLabel(0)
45 , m_scanned(false)
46 , m_working(false)
48 // setup menu signals
49 Gtk::MenuItem *pItem = 0;
50 m_xml->get_widget("menu_file_quit", pItem);
51 pItem->signal_activate().connect(
52 sigc::mem_fun(*this, &BackupWindow::on_file_quit));
54 m_xml->get_widget("menu_edit_config", pItem);
55 pItem->signal_activate().connect(
56 sigc::mem_fun(*this, &BackupWindow::on_edit_config));
58 m_xml->get_widget("menu_help_about", pItem);
59 pItem->signal_activate().connect(
60 sigc::mem_fun(*this, &BackupWindow::on_help_about));
62 // get various widget pointers we will use later
63 m_xml->get_widget("BackupButton", m_pBackupButton);
64 m_xml->get_widget("RestoreButton", m_pRestoreButton);
65 m_xml->get_widget("progressbar1", m_pProgressBar);
66 m_xml->get_widget("statusbar1", m_pStatusBar);
67 m_xml->get_widget("entry1", m_pPINEntry);
68 m_xml->get_widget("entry2", m_pDatabaseEntry);
69 m_xml->get_widget("DeviceNameLabel", m_pDeviceNameLabel);
71 // setup widget signals
72 m_pBackupButton->signal_clicked().connect(
73 sigc::mem_fun(*this, &BackupWindow::on_backup));
74 m_pRestoreButton->signal_clicked().connect(
75 sigc::mem_fun(*this, &BackupWindow::on_restore));
77 // setup thread dispatcher signals
78 m_signal_progress.connect(
79 sigc::mem_fun(*this, &BackupWindow::on_thread_progress));
80 m_signal_error.connect(
81 sigc::mem_fun(*this, &BackupWindow::on_thread_error));
82 m_signal_done.connect(
83 sigc::mem_fun(*this, &BackupWindow::on_thread_done));
84 m_signal_erase_db.connect(
85 sigc::mem_fun(*this, &BackupWindow::on_thread_erase_db));
87 // setup startup device scan
88 Glib::signal_timeout().connect(
89 sigc::mem_fun(*this, &BackupWindow::on_startup), 500);
91 m_pStatusBar->push("Ready");
92 m_pProgressBar->set_fraction(0.00);
94 // do this last so that any exceptions in the constructor
95 // won't cause a connected signal handler to a non-object
96 // (i.e. ~BackupWindow() won't get called if constructor throws)
97 m_signal_handler_connection = Glib::add_exception_handler(
98 sigc::mem_fun(*this, &BackupWindow::signal_exception_handler) );
101 BackupWindow::~BackupWindow()
103 // disconnect the signal, as we're going out of business
104 m_signal_handler_connection.disconnect();
107 void BackupWindow::ScanAndConnect()
109 m_pStatusBar->push("Scanning for devices...");
110 m_pStatusBar->show_now();
112 int tries = 0;
114 sac_retry:
115 tries++;
116 Barry::Probe probe;
117 uint32_t pin = 0;
118 int nSelection = -1;
120 if( probe.GetCount() > 1 ) {
121 DeviceSelectDlg dlg(probe);
122 if( dlg.run() == Gtk::RESPONSE_OK ) {
123 pin = dlg.GetPIN();
124 nSelection = probe.FindActive(pin);
126 else {
127 // no selection, exit
128 hide();
129 return;
132 else if( probe.GetCount() == 1 ) {
133 // default to first
134 pin = probe.Get(0).m_pin;
135 nSelection = 0;
137 else {
138 Gtk::MessageDialog msg("No BlackBerry devices found.");
139 msg.run();
140 hide();
141 return;
144 if( nSelection == -1 ) {
145 Gtk::MessageDialog msg("Internal error: unable to find pin.");
146 msg.run();
147 hide();
148 return;
151 bool out_of_tries = false, password_required = false;
152 int remaining_tries = 0;
153 try {
154 if( !m_dev.Connect(probe.Get(nSelection)) ) {
155 Gtk::MessageDialog msg(m_dev.get_last_error());
156 msg.run();
157 hide();
158 return;
161 catch( Barry::BadPassword &bp ) {
162 out_of_tries = bp.out_of_tries();
163 remaining_tries = bp.remaining_tries();
164 password_required = true;
166 catch( Barry::BadSize &bs ) {
167 std::cerr << "Barry::BadSize caught in ScanAndConnect: "
168 << bs.what() << std::endl;
169 if( tries < 2 ) {
170 // BadSize during connect at startup usually means
171 // the device didn't shutdown properly, so try
172 // a reset or two before we give up
173 Usb::Device dev(probe.Get(nSelection).m_dev);
174 dev.Reset();
175 sleep(2);
176 goto sac_retry;
178 else {
179 Gtk::MessageDialog msg(bs.what());
180 msg.run();
181 hide();
182 return;
186 if( password_required ) {
187 // try password repeatedly until out of tries or
188 // the user cancels... or success :-)
190 bool connected = false;
191 while( !connected && !out_of_tries ) try {
192 PasswordDlg dlg(remaining_tries);
193 if( dlg.run() == Gtk::RESPONSE_OK ) {
194 connected = m_dev.Password(dlg.GetPassword());
195 if( !connected ) {
196 Gtk::MessageDialog msg(m_dev.get_last_error());
197 msg.run();
198 hide();
199 return;
202 else {
203 // user cancelled
204 hide();
205 return;
208 catch( Barry::BadPassword &bp ) {
209 out_of_tries = bp.out_of_tries();
210 remaining_tries = bp.remaining_tries();
211 if( out_of_tries ) {
212 Gtk::MessageDialog msg(bp.what());
213 msg.run();
214 hide();
215 return;
219 if( !connected ) {
220 hide();
221 return;
225 std::ostringstream oss;
226 oss << std::hex << pin;
227 m_pPINEntry->set_text(oss.str());
229 // open configuration now that we know which device we're talking to
230 m_pConfig.reset( new ConfigFile(oss.str(), m_dev.GetDBDB()) );
231 CheckDeviceName();
232 SetDeviceName(m_pConfig->GetDeviceName());
234 m_pStatusBar->pop();
237 void BackupWindow::CheckDeviceName()
239 if( !m_pConfig->HasDeviceName() ) {
240 PromptDlg dlg;
241 dlg.SetPrompt("Unnamed device found. Please enter a name for it:");
242 if( dlg.run() == Gtk::RESPONSE_OK ) {
243 m_pConfig->SetDeviceName(dlg.GetAnswer());
245 else {
246 m_pConfig->SetDeviceName(" ");
248 if( !m_pConfig->Save() ) {
249 Gtk::MessageDialog msg("Error saving config: " +
250 m_pConfig->get_last_error());
251 msg.run();
256 void BackupWindow::SetDeviceName(const std::string &name)
258 // format the device name prompt
259 std::ostringstream dn;
260 dn << "Device: <i>" << m_pConfig->GetDeviceName() << "</i>";
261 m_pDeviceNameLabel->set_label(dn.str());
265 void BackupWindow::SetWorkingMode(const std::string &taskname)
267 m_working = true;
268 m_thread_error = false;
269 m_pBackupButton->set_sensitive(false);
270 m_pRestoreButton->set_sensitive(false);
271 m_pStatusBar->push(taskname + " in progress...");
272 m_pProgressBar->set_fraction(0.00);
275 void BackupWindow::ClearWorkingMode()
277 m_working = false;
278 m_pBackupButton->set_sensitive(true);
279 m_pRestoreButton->set_sensitive(true);
280 m_pStatusBar->pop();
281 if( m_finishedRecords >= m_recordTotal ) {
282 // only reset the progress bar on success
283 m_pProgressBar->set_fraction(0.00);
286 std::ostringstream oss;
287 oss << m_finishedRecords << " total records processed.";
288 m_pDatabaseEntry->set_text(oss.str());
291 void BackupWindow::UpdateProgress()
293 double done = (double)m_finishedRecords / m_recordTotal;
294 // never say 100% unless really done
295 if( done >= 1.0 && m_finishedRecords < m_recordTotal ) {
296 done = 0.99;
298 m_pProgressBar->set_fraction(done);
300 m_pDatabaseEntry->set_text(m_dev.GetThreadDBName());
305 void BackupWindow::signal_exception_handler()
307 try {
308 throw;
310 catch( Glib::Exception &e ) {
311 // This usually just means a missing .glade file,
312 // so we try to carry on.
313 std::cerr << "Glib::Exception caught in main: " << std::endl;
314 std::cerr << e.what() << std::endl;
315 Gtk::MessageDialog msg(e.what());
316 msg.run();
318 catch( ... ) {
319 // anything else, terminate window and pass on to next handler
320 // (which should be in main.cc)
321 hide();
322 throw;
327 //////////////////////////////////////////////////////////////////////////////
328 // signal handlers
330 void BackupWindow::on_backup()
332 // already working?
333 if( m_working ) {
334 Gtk::MessageDialog msg("Thread already in progress.");
335 msg.run();
336 return;
339 // make sure our target directory exists
340 if( !::CheckPath(m_pConfig->GetPath()) ) {
341 Gtk::MessageDialog msg("Could not create directory: " + m_pConfig->GetPath());
342 msg.run();
343 return;
346 // anything to do?
347 if( m_pConfig->GetBackupList().size() == 0 ) {
348 Gtk::MessageDialog msg("No databases selected in configuration.");
349 msg.run();
350 return;
353 // prepare for the progress bar
354 m_recordTotal = m_dev.GetDeviceRecordTotal(m_pConfig->GetBackupList());
355 m_finishedRecords = 0;
356 m_modeName = "Backup";
358 // anything to do?
359 if( m_recordTotal == 0 ) {
360 Gtk::MessageDialog msg("There are no records available in the selected databases.");
361 msg.run();
362 return;
365 // prompt for a backup label, if so configured
366 std::string backupLabel;
367 if( m_pConfig->PromptBackupLabel() ) {
368 PromptDlg dlg;
369 dlg.SetPrompt("Please enter a label for this backup (blank is ok):");
370 if( dlg.run() == Gtk::RESPONSE_OK ) {
371 backupLabel = dlg.GetAnswer();
373 else {
374 // user cancelled
375 return;
379 // start the thread
380 m_working = m_dev.StartBackup(
381 DeviceInterface::AppComm(&m_signal_progress,
382 &m_signal_error,
383 &m_signal_done,
384 &m_signal_erase_db),
385 m_pConfig->GetBackupList(), m_pConfig->GetPath(),
386 m_pConfig->GetPIN(), backupLabel);
387 if( !m_working ) {
388 Gtk::MessageDialog msg("Error starting backup thread: " +
389 m_dev.get_last_error());
390 msg.run();
393 // update the GUI
394 SetWorkingMode("Backup");
397 bool BackupWindow::PromptForRestoreTarball(std::string &restoreFilename,
398 const std::string &start_path)
400 char buffer[PATH_MAX];
401 char *buf = getcwd(buffer, PATH_MAX);
403 // start at the base path given... if it fails, just open
404 // the dialog where we are
405 chdir(start_path.c_str());
407 Gtk::FileChooserDialog dlg(*this, "Select backup to restore from");
408 dlg.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
409 dlg.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
410 int result = dlg.run();
412 if( buf )
413 chdir(buf);
415 if( result != Gtk::RESPONSE_OK )
416 return false;
418 restoreFilename = dlg.get_filename();
419 return true;
422 void BackupWindow::on_restore()
424 // already working?
425 if( m_working ) {
426 Gtk::MessageDialog msg("Thread already in progress.");
427 msg.run();
428 return;
431 std::string restoreFilename;
432 if( !PromptForRestoreTarball(restoreFilename, m_pConfig->GetPath()) )
433 return; // nothing to do
435 // prepare for the progress bar
436 m_finishedRecords = 0;
437 m_modeName = "Restore";
439 // start the thread
440 m_working = m_dev.StartRestore(
441 DeviceInterface::AppComm(&m_signal_progress,
442 &m_signal_error,
443 &m_signal_done,
444 &m_signal_erase_db),
445 m_pConfig->GetRestoreList(), restoreFilename, &m_recordTotal);
446 // m_working = m_dev.StartRestoreAndBackup(
447 // DeviceInterface::AppComm(&m_signal_progress,
448 // &m_signal_error,
449 // &m_signal_done,
450 // &m_signal_erase_db),
451 // m_pConfig->GetRestoreList(), restoreFilename,
452 // m_pConfig->GetPath(), m_pConfig->GetPIN(),
453 // &m_recordTotal);
454 if( !m_working ) {
455 Gtk::MessageDialog msg("Error starting restore thread: " +
456 m_dev.get_last_error());
457 msg.run();
460 std::cerr << "m_recordTotal for restore: " << m_recordTotal << std::endl;
462 // update the GUI
463 SetWorkingMode("Restore");
466 void BackupWindow::on_file_quit()
468 m_dev.Disconnect();
469 hide();
472 void BackupWindow::on_edit_config()
474 ConfigDlg dlg(m_dev.GetDBDB(), *m_pConfig);
475 if( dlg.run() == Gtk::RESPONSE_OK ) {
476 m_pConfig->SetBackupList(dlg.GetBackupList());
477 m_pConfig->SetRestoreList(dlg.GetRestoreList());
478 m_pConfig->SetDeviceName(dlg.GetDeviceName());
479 m_pConfig->SetPromptBackupLabel(dlg.GetPromptBackupLabel());
480 if( !m_pConfig->Save() ) {
481 Gtk::MessageDialog msg("Error saving config: " +
482 m_pConfig->get_last_error());
483 msg.run();
485 SetDeviceName(m_pConfig->GetDeviceName());
489 void BackupWindow::on_help_about()
491 Gtk::AboutDialog dlg;
492 dlg.set_copyright("Copyright (C) 2007-2009, Net Direct Inc.");
493 dlg.set_license(
494 " This program is free software; you can redistribute it and/or modify\n"
495 " it under the terms of the GNU General Public License as published by\n"
496 " the Free Software Foundation; either version 2 of the License, or\n"
497 " (at your option) any later version.\n"
498 "\n"
499 " This program is distributed in the hope that it will be useful,\n"
500 " but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
501 " MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"
502 "\n"
503 " See the GNU General Public License in the COPYING file at the\n"
504 " root directory of this project for more details.\n");
506 std::vector<std::string> authors;
507 authors.push_back("Chris Frey <cdfrey@foursquare.net>");
509 dlg.set_authors(authors);
511 int major, minor;
512 const char *BarryVersion = Barry::Version(major, minor);
513 dlg.set_name("Barry Backup");
514 dlg.set_version("0.15");
515 dlg.set_comments(std::string("Using library: ") + BarryVersion);
516 dlg.set_website("http://www.netdirect.ca/software/packages/barry/");
517 dlg.run();
520 bool BackupWindow::on_startup()
522 if( !m_scanned ) {
523 ScanAndConnect();
524 m_scanned = true;
526 return false;
529 void BackupWindow::on_thread_progress()
531 m_finishedRecords++;
532 UpdateProgress();
535 void BackupWindow::on_thread_error()
537 m_thread_error = true;
539 Gtk::MessageDialog msg(m_modeName + " error: " + m_dev.get_last_thread_error());
540 msg.run();
543 void BackupWindow::on_thread_done()
545 if( !m_thread_error ) {
546 Gtk::MessageDialog msg(m_modeName + " complete!");
547 msg.run();
550 // done!
551 ClearWorkingMode();
552 m_working = false;
555 void BackupWindow::on_thread_erase_db()
557 std::string name = m_dev.GetThreadDBName();
558 m_pDatabaseEntry->set_text("Erasing database: " + name);
564 void on_showtext()
566 Glib::ustring text = pEntry->get_text();
567 Gtk::MessageDialog dialog("This is the text entered: " + text);
568 // dialog.set_secondary_text(text);
569 dialog.run();
572 void on_close()
574 // response(Gtk::RESPONSE_CLOSE);
575 // signal_delete_event().emit();
576 // Gtk::Main::quit();
577 hide();