2 /// \file MigrateDlg.cc
3 /// Dialog for the "Migrate Device" main menu mode button...
4 /// going with a dialog instead of a mode class this time.
8 Copyright (C) 2012, Net Direct Inc. (http://www.netdirect.ca/)
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 2 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
19 See the GNU General Public License in the COPYING file at the
20 root directory of this project for more details.
23 #include "MigrateDlg.h"
24 #include "windowids.h"
26 #include "barrydesktop.h"
28 #include <wx/statline.h>
31 using namespace OpenSync
;
33 DEFINE_EVENT_TYPE(MET_THREAD_FINISHED
)
34 DEFINE_EVENT_TYPE(MET_CHECK_DEST_PIN
)
35 DEFINE_EVENT_TYPE(MET_SET_STATUS_MSG
)
36 DEFINE_EVENT_TYPE(MET_PROMPT_PASSWORD
)
37 DEFINE_EVENT_TYPE(MET_ERROR_MSG
)
39 BEGIN_EVENT_TABLE(MigrateDlg
, wxDialog
)
40 EVT_BUTTON (Dialog_Migrate_MigrateNowButton
,
41 MigrateDlg::OnMigrateNow
)
42 EVT_BUTTON (Dialog_Migrate_CancelButton
,
44 EVT_CLOSE (MigrateDlg::OnCloseWindow
)
45 EVT_COMMAND (wxID_ANY
, MET_THREAD_FINISHED
,
46 MigrateDlg::OnThreadFinished
)
47 EVT_COMMAND (wxID_ANY
, MET_CHECK_DEST_PIN
,
48 MigrateDlg::OnCheckDestPin
)
49 EVT_COMMAND (wxID_ANY
, MET_SET_STATUS_MSG
,
50 MigrateDlg::OnSetStatusMsg
)
51 EVT_COMMAND (wxID_ANY
, MET_PROMPT_PASSWORD
,
52 MigrateDlg::OnPromptPassword
)
53 EVT_COMMAND (wxID_ANY
, MET_ERROR_MSG
,
54 MigrateDlg::OnErrorMsg
)
58 class EventDesktopConnector
: public Barry::DesktopConnector
63 EventDesktopConnector(MigrateDlg
*dlg
, const char *password
,
64 const std::string
&locale
, const Barry::ProbeResult
&result
)
65 : Barry::DesktopConnector(password
, locale
, result
)
70 virtual bool PasswordPrompt(const Barry::BadPassword
&bp
,
71 std::string
&password_result
);
74 bool EventDesktopConnector::PasswordPrompt(const Barry::BadPassword
&bp
,
75 std::string
&password_result
)
77 // ping the parent and wait for finish
78 wxCommandEvent
event(MET_PROMPT_PASSWORD
, wxID_ANY
);
79 event
.SetEventObject(m_dlg
);
80 event
.SetInt(bp
.remaining_tries());
81 m_dlg
->AddPendingEvent(event
);
82 m_dlg
->WaitForEvent();
84 password_result
= m_dlg
->GetPassword().utf8_str();
86 // assume that a blank password means the user wishes to quit...
87 // wxWidgets doesn't seem to handle this very well?
88 return password_result
.size() > 0;
92 //////////////////////////////////////////////////////////////////////////////
95 MigrateDlg::MigrateDlg(wxWindow
*parent
,
96 const Barry::Probe::Results
&results
,
97 int current_device_index
)
98 : wxDialog(parent
, Dialog_GroupCfg
, _T("Migrate Device"))
100 , m_current_device_index(current_device_index
)
101 , m_migrate_thread_created(false)
102 , m_migrate_thread(NULL
)
103 , m_abort_flag(false)
104 , m_thread_running(false)
110 , m_write_mode_combo(0)
111 , m_migrate_button(0)
120 void MigrateDlg::WaitForEvent()
125 void MigrateDlg::CreateLayout()
127 m_topsizer
= new wxBoxSizer(wxVERTICAL
);
129 // AddDescriptionSizer(m_topsizer);
130 AddMainSizer(m_topsizer
);
131 AddStatusSizer(m_topsizer
);
133 SetSizer(m_topsizer
);
134 m_topsizer
->SetSizeHints(this);
135 m_topsizer
->Layout();
138 void MigrateDlg::AddDescriptionSizer(wxSizer
*sizer
)
141 new wxStaticText(this, wxID_ANY
,
142 _T("Migrate device data from source device to target device.")),
143 0, wxALIGN_CENTRE
| wxALIGN_CENTRE_VERTICAL
|
144 wxTOP
| wxLEFT
| wxRIGHT
, 10);
147 void MigrateDlg::AddMainSizer(wxSizer
*sizer
)
149 // add 3 main sections together into one sizer
150 wxSizer
*main
= new wxBoxSizer(wxHORIZONTAL
);
151 Main_AddSourceSizer(main
);
152 Main_AddButtonSizer(main
);
153 Main_AddDestSizer(main
);
155 // add main sizer to top level sizer
156 sizer
->Add(main
, 0, wxEXPAND
| wxTOP
| wxLEFT
| wxRIGHT
, 10);
159 void MigrateDlg::AddStatusSizer(wxSizer
*sizer
)
161 sizer
->Add( new wxStaticLine(this),
162 0, wxEXPAND
| wxTOP
| wxLEFT
| wxRIGHT
, 10);
165 m_status
= new wxStaticText(this, wxID_ANY
, _T("Ready...")),
166 0, wxEXPAND
| wxLEFT
| wxRIGHT
, 10);
167 // Reduce font size for status text
168 wxFont font
= m_status
->GetFont();
169 font
.SetPointSize(font
.GetPointSize() - 1);
170 m_status
->SetFont( font
);
173 m_progress
= new wxGauge(this, wxID_ANY
, 100),
174 0, wxEXPAND
| wxLEFT
| wxRIGHT
| wxBOTTOM
, 10);
177 void MigrateDlg::Main_AddSourceSizer(wxSizer
*sizer
)
179 wxArrayString devices
;
180 for( Barry::Probe::Results::const_iterator i
= m_results
.begin();
181 i
!= m_results
.end(); ++i
)
183 devices
.Add(wxString(i
->GetDisplayName().c_str(), wxConvUTF8
));
186 wxSizer
*source
= new wxStaticBoxSizer(
187 new wxStaticBox(this, wxID_ANY
, _T("Source device")),
191 m_source_combo
= new wxChoice(this, wxID_ANY
,
192 wxDefaultPosition
, wxSize(225, -1), devices
),
195 if( m_current_device_index
>= 0 )
196 m_source_combo
->SetSelection(m_current_device_index
);
198 sizer
->Add(source
, 0, wxEXPAND
, 0);
201 void MigrateDlg::Main_AddButtonSizer(wxSizer
*sizer
)
203 wxSizer
*buttons
= new wxBoxSizer(wxVERTICAL
);
204 buttons
->Add( m_migrate_button
= new wxButton(this,
205 Dialog_Migrate_MigrateNowButton
, _T("Migrate Now")),
206 0, wxALIGN_CENTRE
, 0);
207 m_migrate_button
->SetDefault();
208 buttons
->AddSpacer(10);
209 buttons
->Add( new wxButton(this, Dialog_Migrate_CancelButton
,
211 0, wxALIGN_CENTRE
, 0);
213 sizer
->Add(buttons
, 1, wxALIGN_CENTRE
| wxLEFT
| wxRIGHT
, 10);
216 void MigrateDlg::Main_AddDestSizer(wxSizer
*sizer
)
218 wxArrayString devices
;
219 devices
.Add(_T("Prompt to plug in later..."));
220 for( Barry::Probe::Results::const_iterator i
= m_results
.begin();
221 i
!= m_results
.end(); ++i
)
223 devices
.Add(wxString(i
->GetDisplayName().c_str(), wxConvUTF8
));
226 wxSizer
*dest
= new wxStaticBoxSizer(
227 new wxStaticBox(this, wxID_ANY
, _T("Destination device")),
231 m_dest_combo
= new wxChoice(this, wxID_ANY
,
232 wxDefaultPosition
, wxSize(225, -1), devices
),
234 m_dest_combo
->SetSelection(0);
237 wxArrayString write_modes
;
238 write_modes
.Add(_T("Erase all, then restore"));
239 write_modes
.Add(_T("Add new, and overwrite existing"));
240 write_modes
.Add(_T("Add only, don't overwrite existing"));
241 write_modes
.Add(_T("Add every record as a new entry (may cause duplicates)"));
243 dest
->Add( new wxStaticText(this, wxID_ANY
, _T("Write Mode:")),
244 0, wxTOP
| wxLEFT
| wxRIGHT
, 5);
245 dest
->Add( m_write_mode_combo
= new wxChoice(this, wxID_ANY
,
246 wxDefaultPosition
, wxSize(225, -1), write_modes
),
248 m_write_mode_combo
->SetSelection(0);
250 // dest->Add( m_wipe_check = wxCheckBox(maybe a checkbox for "wipe device before restore"));
252 sizer
->Add(dest
, 0, wxEXPAND
, 0);
255 void MigrateDlg::EnableControls(bool enable
)
257 m_source_combo
->Enable(enable
);
258 m_dest_combo
->Enable(enable
);
259 m_write_mode_combo
->Enable(enable
);
260 m_migrate_button
->Enable(enable
);
261 //m_wipe_check->Enable(enable);
264 void MigrateDlg::DoSafeClose()
266 // if migrate thread is running, try to close it down first...
267 // do not exit the dialog until the thread is properly stopped!
268 if( m_thread_running
) {
271 m_status
->SetLabel(_T("Waiting for thread to close..."));
273 if( m_migrate_thread_created
) {
275 pthread_join(m_migrate_thread
, &junk
);
276 m_migrate_thread_created
= false;
280 // all activity is stopped, so close dialog
281 EndModal(wxID_CANCEL
);
284 void MigrateDlg::SendEvent(int event_type
)
286 wxCommandEvent
event(event_type
, wxID_ANY
);
287 event
.SetEventObject(this);
288 AddPendingEvent(event
);
291 void MigrateDlg::SendStatusEvent(const wxString
&msg
, int pos
, int max
)
293 wxCommandEvent
event(MET_SET_STATUS_MSG
, wxID_ANY
);
294 event
.SetEventObject(this);
296 event
.SetString(msg
);
310 AddPendingEvent(event
);
313 void MigrateDlg::SendErrorMsgEvent(const wxString
&msg
)
315 wxCommandEvent
event(MET_ERROR_MSG
, wxID_ANY
);
316 event
.SetEventObject(this);
317 event
.SetString(msg
);
318 AddPendingEvent(event
);
321 void MigrateDlg::OnMigrateNow(wxCommandEvent
&event
)
323 // gather info from dialog
324 int source_index
= m_source_combo
->GetSelection();
325 int dest_index
= m_dest_combo
->GetSelection();
326 int write_mode_index
= m_write_mode_combo
->GetSelection();
329 if( source_index
== wxNOT_FOUND
|| dest_index
== wxNOT_FOUND
||
330 write_mode_index
== wxNOT_FOUND
)
332 wxMessageBox(_T("Please select a source and destination device, as well as the write mode."),
333 _T("Migration Options Needed"), wxOK
| wxICON_ERROR
);
337 // do not migrate from one PIN to the same PIN
338 if( source_index
== (dest_index
- 1) ) {
339 wxMessageBox(_T("Cannot migrate from and to the same PIN."),
340 _T("Migration Options Error"), wxOK
| wxICON_ERROR
);
344 // set the migration arguments
345 m_source_device
= &m_results
[source_index
];
346 if( dest_index
> 0 ) {
347 m_dest_device
= &m_results
[dest_index
-1];
350 m_dest_device
= 0; // an invalid dest causes a prompt
352 switch( write_mode_index
)
355 m_write_mode
= Barry::DeviceParser::ERASE_ALL_WRITE_ALL
;
358 m_write_mode
= Barry::DeviceParser::INDIVIDUAL_OVERWRITE
;
361 m_write_mode
= Barry::DeviceParser::ADD_BUT_NO_OVERWRITE
;
364 m_write_mode
= Barry::DeviceParser::ADD_WITH_NEW_ID
;
367 wxMessageBox(_T("Invalid write mode. This should never happen. Contact the developers."),
368 _T("Internal Logic Error"), wxOK
| wxICON_ERROR
);
372 // disable all buttons and controls except cancel
373 EnableControls(false);
375 // turn off the stop flag
376 m_abort_flag
= false;
377 m_thread_running
= false;
379 // fire up migrate thread, and let the thread close the dialog when
380 // done (can we EndModal() from a thread?)
381 int ret
= pthread_create(&m_migrate_thread
, NULL
,
382 &MigrateDlg::MigrateThread
, this);
385 EnableControls(true);
389 m_migrate_thread_created
= true;
392 // thread started... let it finish
395 void MigrateDlg::OnCancel(wxCommandEvent
&event
)
400 void MigrateDlg::OnCloseWindow(wxCloseEvent
&event
)
405 void MigrateDlg::OnThreadFinished(wxCommandEvent
&event
)
407 if( m_migrate_thread_created
) {
408 m_status
->SetLabel(_T("Waiting for thread..."));
410 pthread_join(m_migrate_thread
, &junk
);
411 m_migrate_thread_created
= false;
415 // user cancelled in some way, restore GUI
416 EnableControls(true);
417 m_status
->SetLabel(_T("Cancelled by user..."));
420 // if we were not aborted, then this is success, and we
421 // can close the original dialog
422 EndModal(wxID_CANCEL
);
426 void MigrateDlg::OnCheckDestPin(wxCommandEvent
&event
)
428 ScopeSignaler
done(m_waiter
);
430 if( m_dest_device
&& m_dest_device
->m_pin
.Valid() )
431 return; // nothing to do
433 // no destination pin was available, so user may need to plugin
434 // the new device right now, before continuing
435 int response
= wxMessageBox(_T("Please plug in the target device now."),
436 _T("Ready for Writing"), wxOK
| wxCANCEL
| wxICON_INFORMATION
,
438 if( response
!= wxOK
) {
446 m_status
->SetLabel(_T("Scanning USB for devices..."));
448 // pause for 2 seconds to let any new devices settle down
452 // rescan the USB and try to find the new device
454 m_new_results
= probe
.GetResults();
457 // now prompt the user... create a list of PIN display names
458 wxArrayString devices
;
459 for( Barry::Probe::Results::const_iterator i
= m_new_results
.begin();
460 i
!= m_new_results
.end(); ++i
)
462 devices
.Add(wxString(i
->GetDisplayName().c_str(), wxConvUTF8
));
465 m_status
->SetLabel(_T("User input..."));
469 // prompt the user with this list
470 int choice
= wxGetSingleChoiceIndex(_T("Please select the target device to write to:"),
471 _T("Destination PIN"), devices
, this);
478 // found the new PIN to use!
479 m_dest_device
= &m_new_results
[choice
];
481 // check if user needs to choose again
482 if( m_dest_device
->m_pin
== m_source_device
->m_pin
) {
483 wxMessageBox(_T("Cannot use the same device PIN as migration destination."),
484 _T("Invalid Device Selection"),
485 wxOK
| wxICON_ERROR
, this);
488 } while( m_dest_device
->m_pin
== m_source_device
->m_pin
);
491 void MigrateDlg::OnSetStatusMsg(wxCommandEvent
&event
)
493 if( event
.GetString().size() ) {
494 m_status
->SetLabel(event
.GetString());
497 if( (unsigned int)event
.GetInt() != 0xffff ) {
498 unsigned int value
= (unsigned int) event
.GetInt();
499 unsigned int pos
= (value
& 0xff00) >> 8;
500 unsigned int max
= (value
& 0xff);
503 m_progress
->SetValue(pos
);
507 m_progress
->SetRange(max
);
512 void MigrateDlg::OnPromptPassword(wxCommandEvent
&event
)
514 ScopeSignaler
done(m_waiter
);
516 // create prompt based on exception data
518 oss
<< "Please enter device password: ("
520 << " tries remaining)";
521 wxString
prompt(oss
.str().c_str(), wxConvUTF8
);
523 // ask user for device password
524 m_password
= wxGetPasswordFromUser(prompt
,
525 _T("Device Password"), _T(""), this);
528 void MigrateDlg::OnErrorMsg(wxCommandEvent
&event
)
530 ScopeSignaler
done(m_waiter
);
531 wxMessageBox(event
.GetString(), _T("Migration Error"),
532 wxOK
| wxICON_ERROR
, this);
535 void* MigrateDlg::MigrateThread(void *arg
)
537 MigrateDlg
*us
= (MigrateDlg
*) arg
;
540 us
->m_thread_running
= true;
546 // make sure we have a destination PIN
547 if( !us
->m_abort_flag
)
550 // restore to dest PIN
551 if( !us
->m_abort_flag
)
554 catch( std::exception
&e
) {
555 us
->SendErrorMsgEvent(wxString(e
.what(), wxConvUTF8
));
559 // invalidate the device selection pointers, since
560 // m_new_results may not always exist
561 us
->m_source_device
= us
->m_dest_device
= 0;
564 us
->m_thread_running
= false;
566 // send event to let main GUI thread we're finished
567 us
->SendEvent(MET_THREAD_FINISHED
);
572 // This is called from the thread
573 void MigrateDlg::BackupSource()
575 // connect to the source device
576 SendStatusEvent(_T("Connecting..."));
577 EventDesktopConnector
connect(this, "", "utf-8", *m_source_device
);
578 if( !connect
.Reconnect(2) ) {
584 // calculate the default backup path location, based on user name
585 // (see backup GUI for code?)
587 // fetch DBDB, for list of databases to backup... back them all up
588 // remember to save this DBDB into the class, so it is available
589 // for the restore stage
591 // cycle through all databases
592 // status message "Backing up database: XXXXX..."
593 // calculate 1 to 100 percentage, based on number of
594 // databases being backed up, and update status bar too
596 // backup this database
597 // on each record (pump cycle?), as often as possible,
598 // check the m_abort_flag, and abort if necessary,
599 // updating the status message
601 // close all files, etc.
602 // save backup filename, for restore stage
605 // This is called from the thread
606 void MigrateDlg::CheckDestPin()
608 SendEvent(MET_CHECK_DEST_PIN
);
612 // This is called from the thread
613 void MigrateDlg::RestoreToDest()
615 // connect to the dest device
617 // fetch DBDB of dest device, for list of databases we can restore
618 // to... compare with the backup DBDB to create a list of similarly
619 // named databases which we can restore....
621 // cycle through all databases
622 // status message "Writing database: XXXXX..."
623 // calculate 1 to 100 percentage, based on number of
624 // databases being restored up, and update status bar too
626 // restore this database
627 // on each record (pump cycle?), as often as possible,
628 // check the m_abort_flag, and abort if necessary,
629 // updating the status message
631 // close all files, etc.