Bumped copyright dates for 2013
[barry.git] / desktop / src / Mode_Sync.cc
blob182c4f2f3a7e6d088a610f7e48da9d74ea192dab
1 ///
2 /// \file Mode_Sync.cc
3 /// Mode derived class for syncing
4 ///
6 /*
7 Copyright (C) 2009-2013, 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 "Mode_Sync.h"
23 #include "BaseFrame.h"
24 #include "GroupCfgDlg.h"
25 #include "SyncStatusDlg.h"
26 #include "barrydesktop.h"
27 #include "windowids.h"
28 #include <string>
30 using namespace std;
32 BEGIN_EVENT_TABLE(SyncMode, wxEvtHandler)
33 EVT_BUTTON (SyncMode_SyncNowButton, SyncMode::OnSyncNow)
34 EVT_BUTTON (SyncMode_ConfigureButton, SyncMode::OnConfigure)
35 EVT_BUTTON (SyncMode_RunAppButton, SyncMode::OnRunApp)
36 EVT_BUTTON (SyncMode_1WayResetButton, SyncMode::On1WayReset)
37 EVT_LIST_ITEM_SELECTED(SyncMode_DeviceList, SyncMode::OnListSelChange)
38 EVT_LIST_ITEM_DESELECTED(SyncMode_DeviceList, SyncMode::OnListSelChange)
39 EVT_LIST_ITEM_ACTIVATED(SyncMode_DeviceList, SyncMode::OnConfigureDevice)
40 END_EVENT_TABLE()
42 //////////////////////////////////////////////////////////////////////////////
43 // SyncMode
45 SyncMode::SyncMode(wxWindow *parent)
46 : m_parent(parent)
48 wxBusyCursor wait;
50 wxSize client_size = parent->GetClientSize();
52 // create our list of devices
53 m_device_set.reset( new DeviceSet(wxGetApp().GetGlobalConfig(),
54 wxGetApp().GetOpenSync(),
55 wxGetApp().GetResults()) );
56 barryverbose(*m_device_set);
58 // eliminate all duplicate device entries
59 DeviceSet::subset_type subset;
60 do {
61 subset = m_device_set->FindDuplicates();
62 if( subset.size() ) {
63 // build list of choices
64 wxArrayString choices;
65 DeviceSet::subset_type::iterator i = subset.begin();
66 for( ; i != subset.end(); ++i ) {
67 string desc = (*i)->GetIdentifyingString();
68 choices.Add( wxString(desc.c_str(), wxConvUTF8) );
71 // let the user choose
72 // FIXME - the width of the choice dialog is
73 // determined by the length of the string...
74 // which is less than ideal
75 int choice = wxGetSingleChoiceIndex(_W("Multiple configurations have been found with the same PIN. Please select\nthe configuration that Barry Desktop should work with."),
76 _T("Duplicate PIN"),
77 choices, parent);
79 // remove everything except keep
80 if( choice != -1 ) {
81 subset.erase(subset.begin() + choice);
84 m_device_set->KillDuplicates(subset);
86 barryverbose(*m_device_set);
88 } while( subset.size() );
91 // create the window controls we need
94 m_topsizer.reset( new wxBoxSizer(wxVERTICAL) );
96 // make space for the main header, which is not part of our
97 // work area
98 m_topsizer->AddSpacer(MAIN_HEADER_OFFSET);
100 // add status area
102 // Select the device(s) you want to sync and press Sync Now
103 // Press Configure... to configure the currently selected device
104 // Press Run App to start the application that the device syncs with
105 // Press 1-Way Reset to recover from a broken sync, copying all
106 // device data to application, or vice versa.
108 m_topsizer->AddSpacer(5);
110 m_sync_now_button.reset( new wxButton(parent,
111 SyncMode_SyncNowButton, _T("Sync Now")));
112 wxSize sync_button_size = m_sync_now_button->GetClientSize();
113 int wrapwidth = client_size.GetWidth() - 20 - sync_button_size.GetWidth();
115 wxBoxSizer *infosizer = new wxBoxSizer(wxHORIZONTAL);
116 wxBoxSizer *linesizer = new wxBoxSizer(wxVERTICAL);
118 // info lines
119 #define MAKE_INFO_LABEL(a, b) \
120 m_label[a].reset( new wxStaticText(parent, -1, b, \
121 wxPoint(15, 100)) ); \
122 m_label[a]->Wrap(wrapwidth); \
123 linesizer->Add(m_label[a].get(), 0, wxEXPAND, 0); \
124 linesizer->AddSpacer(4);
125 MAKE_INFO_LABEL(0, _W("Select the device(s) you want to sync and press Sync Now."));
126 MAKE_INFO_LABEL(1, _W("Use Configure to configure the currently selected device."));
127 MAKE_INFO_LABEL(2, _W("Use Run App to start the application that the device syncs with."));
128 MAKE_INFO_LABEL(3, _W("Use 1-Way Reset to recover from a broken sync, copying all device data to application, or vice versa."));
130 infosizer->Add( linesizer, 1, wxALIGN_LEFT, 0 );
131 infosizer->Add( m_sync_now_button.get(), 0, wxALIGN_RIGHT, 0 );
132 m_topsizer->Add( infosizer,
133 0, wxEXPAND | wxLEFT | wxBOTTOM | wxRIGHT, 10 );
135 // status, final spacing
136 m_topsizer->AddSpacer(10);
138 // add device list
139 wxStaticBoxSizer *box = new wxStaticBoxSizer(wxHORIZONTAL, parent,
140 _W("Device List"));
141 m_device_list.reset (new wxListCtrl(parent, SyncMode_DeviceList,
142 wxDefaultPosition, wxDefaultSize,
143 wxLC_REPORT /*| wxLC_VRULES*/) );
144 box->Add( m_device_list.get(), 1, wxEXPAND | wxALL, 4 );
145 m_topsizer->Add(box, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, 10 );
147 // add bottom buttons - these go in the bottom FOOTER area
148 // so their heights must be fixed to MAIN_HEADER_OFFSET
149 // minus a border of 5px top and bottom
150 wxSize footer(-1, MAIN_HEADER_OFFSET - 5 - 5);
151 wxBoxSizer *buttons = new wxBoxSizer(wxHORIZONTAL);
152 m_run_app_button.reset( new wxButton(parent,
153 SyncMode_RunAppButton, _W("Run App"),
154 wxDefaultPosition, footer));
155 m_configure_button.reset( new wxButton(parent,
156 SyncMode_ConfigureButton, _W("Configure..."),
157 wxDefaultPosition, footer) );
158 m_1way_reset_button.reset( new wxButton(parent,
159 SyncMode_1WayResetButton, _W("1 Way Reset..."),
160 wxDefaultPosition, footer) );
161 buttons->Add(m_run_app_button.get(), 0, wxRIGHT, 5 );
162 buttons->Add(m_configure_button.get(), 0, wxRIGHT, 5 );
163 buttons->Add(m_1way_reset_button.get(), 0, wxRIGHT, 5 );
164 m_topsizer->Add(buttons, 0, wxALL | wxALIGN_RIGHT, 5 );
166 // recalc size of children
167 m_topsizer->SetDimension(0, 0,
168 client_size.GetWidth(), client_size.GetHeight());
170 // insert list columns based on the new list size
171 wxSize list_size = m_device_list->GetSize();
172 int timestamp_width = GetMaxTimestampWidth(m_device_list.get());
173 // FIXME - for some reason, even with the width calculated,
174 // when displayed in the listctrl, there's not enough space...
175 // possibly because there's space between columns... I don't
176 // know how to calculate the column inter-space size, so add
177 // a constant here :-(
178 timestamp_width += 8;
179 int usable_width = list_size.GetWidth() - timestamp_width;
180 m_device_list->InsertColumn(0, _W("PIN"),
181 wxLIST_FORMAT_LEFT, usable_width * 0.16);
182 m_device_list->InsertColumn(1, _W("Name"),
183 wxLIST_FORMAT_LEFT, usable_width * 0.33);
184 m_device_list->InsertColumn(2, _W("Connected"),
185 wxLIST_FORMAT_CENTRE, usable_width * 0.16);
186 m_device_list->InsertColumn(3, _W("Application"),
187 wxLIST_FORMAT_CENTRE, usable_width * 0.18);
188 m_device_list->InsertColumn(4, _W("Engine"),
189 wxLIST_FORMAT_CENTRE, usable_width * 0.17);
190 m_device_list->InsertColumn(5, _W("Last Sync"),
191 wxLIST_FORMAT_CENTRE, timestamp_width);
193 FillDeviceList();
195 // attempt to re-select the devices as we last saw them
196 ReselectDevices(m_device_set->String2Subset(wxGetApp().GetGlobalConfig().GetKey("SelectedDevices")));
197 UpdateButtons();
199 // connect ourselves to the parent's event handling chain
200 // do this last, so that we are guaranteed our destructor
201 // will run, in case of exceptions
202 m_parent->PushEventHandler(this);
205 SyncMode::~SyncMode()
207 m_parent->PopEventHandler();
209 // save selected devices for later
210 wxGetApp().GetGlobalConfig().SetKey("SelectedDevices",
211 DeviceSet::Subset2String(GetSelectedDevices()));
214 std::string SyncMode::Timestamp(time_t last_sync)
216 string ret;
217 struct tm local;
218 if( localtime_r(&last_sync, &local) != NULL ) {
219 char timestamp[20];
220 strftime(timestamp, sizeof(timestamp), "%b %d, %H:%M", &local);
221 ret = timestamp;
223 return ret;
226 int SyncMode::GetMaxTimestampWidth(wxWindow *win)
228 int max_width = 0;
229 DeviceSet::const_iterator i = m_device_set->begin();
230 for( ; i != m_device_set->end(); ++i ) {
231 int this_width = 0;
232 int this_height = 0;
233 if( i->GetExtras() ) {
234 time_t last_sync = i->GetExtras()->m_last_sync_time;
235 if( last_sync ) {
236 win->GetTextExtent(wxString(Timestamp(last_sync).c_str(), wxConvUTF8), &this_width, &this_height);
240 max_width = max(max_width, this_width);
243 return max_width;
246 void SyncMode::FillDeviceList()
248 // start fresh
249 m_device_list->DeleteAllItems();
251 DeviceSet::const_iterator i = m_device_set->begin();
252 for( int index = 0; i != m_device_set->end(); ++i, index++ ) {
253 // PIN number
254 wxString text(i->GetPin().Str().c_str(), wxConvUTF8);
255 long item = m_device_list->InsertItem(index, text);
257 // Device name
258 text = wxString(i->GetDeviceName().c_str(), wxConvUTF8);
259 m_device_list->SetItem(item, 1, text);
261 // Connected?
262 text = i->IsConnected() ? _W("Yes") : _W("No");
263 m_device_list->SetItem(item, 2, text);
265 // Configured?
266 if( i->IsConfigured() ) {
267 text = wxString(i->GetAppNames().c_str(), wxConvUTF8);
269 else {
270 text = _W("(No config)");
272 m_device_list->SetItem(item, 3, text);
274 // Engine
275 if( i->GetEngine() )
276 text = wxString(i->GetEngine()->GetVersion(), wxConvUTF8);
277 else
278 text = _T("");
279 m_device_list->SetItem(item, 4, text);
281 // Last Sync
282 if( i->GetExtras() ) {
283 time_t last_sync = i->GetExtras()->m_last_sync_time;
284 if( last_sync ) {
285 wxString ts(Timestamp(last_sync).c_str(), wxConvUTF8);
286 m_device_list->SetItem(item, 5, ts);
291 UpdateButtons();
294 void SyncMode::UpdateButtons()
296 int selected_count = m_device_list->GetSelectedItemCount();
298 // update the SyncNow button (only on if anything is selected)
299 m_sync_now_button->Enable(selected_count > 0);
300 m_configure_button->Enable(selected_count == 1);
301 m_run_app_button->Enable(selected_count == 1);
302 m_1way_reset_button->Enable(selected_count == 1);
304 // if only one item is selected, enable RunApp button
305 bool enable_run_app = false;
306 if( selected_count == 1 ) {
307 // good, only one is selected, find out which one
308 long item = -1;
309 item = m_device_list->GetNextItem(item, wxLIST_NEXT_ALL,
310 wxLIST_STATE_SELECTED);
312 if( item != -1 ) {
313 DeviceEntry &entry = (*m_device_set)[item];
314 if( entry.GetAppNames().size() ) {
315 // has application configured!
316 enable_run_app = true;
320 m_run_app_button->Enable(enable_run_app);
323 DeviceSet::subset_type SyncMode::GetSelectedDevices()
325 DeviceSet::subset_type subset;
327 long item = -1;
328 do {
329 item = m_device_list->GetNextItem(item, wxLIST_NEXT_ALL,
330 wxLIST_STATE_SELECTED);
332 if( item != -1 ) {
333 subset.push_back(m_device_set->begin() + item);
335 } while( item != -1 );
337 return subset;
340 void SyncMode::ReselectDevices(const DeviceSet::subset_type &set)
342 for( long item = m_device_list->GetNextItem(-1); item != -1;
343 item = m_device_list->GetNextItem(item) )
345 bool selected = DeviceSet::FindPin(set, (*m_device_set)[item].GetPin()) != set.end();
347 m_device_list->SetItemState(item,
348 selected ? wxLIST_STATE_SELECTED : 0,
349 wxLIST_STATE_SELECTED);
353 void SyncMode::ConfigureDevice(int device_index)
355 DeviceEntry &entry = (*m_device_set)[device_index];
356 ConfigureDevice(entry);
359 void SyncMode::ConfigureDevice(DeviceEntry &entry)
361 // make sure it's not already running
362 if( m_cui.get() && m_cui->IsAppRunning() ) {
363 wxMessageBox(_W("An application is currently running."),
364 _W("Run App Error"), wxOK | wxICON_ERROR, m_parent);
365 return;
368 GroupCfgDlg dlg(m_parent, entry, wxGetApp().GetOpenSync());
369 if( dlg.ShowModal() == wxID_OK &&
370 dlg.GetEngine() &&
371 dlg.GetGroup().get() &&
372 dlg.GetExtras().get() )
374 bool skip_rewrite = false;
375 bool delete_old = false;
377 // does old group exist?
378 if( entry.GetEngine() &&
379 entry.GetConfigGroup() &&
380 entry.GetConfigGroup()->GroupExists(*entry.GetEngine()) )
382 // yes, is the new group equal?
383 string v1 = entry.GetEngine()->GetVersion();
384 string v2 = dlg.GetEngine()->GetVersion();
385 skip_rewrite = (v1 == v2 &&
386 dlg.GetGroup()->Compare(*entry.GetConfigGroup()));
388 if( skip_rewrite ) {
389 // config is the same, don't bother saving again
390 barryverbose(_C("Config is the same, skipping save"));
392 else {
393 // clean up after ourselves... if the new
394 // config uses a different engine, delete
395 // the config on the old engine
396 if( entry.GetEngine() != dlg.GetEngine() ) {
397 delete_old = true;
402 if( !skip_rewrite ) try {
404 OpenSync::API *eng = dlg.GetEngine();
405 DeviceEntry::group_ptr grp = dlg.GetGroup();
407 // make sure that the save will be successful
408 if( grp->GroupExists(*eng) ) {
409 if( !grp->GroupMatchesExistingConfig(*eng) ) {
410 if( WarnAbout1WayReset() ) {
411 eng->DeleteGroup(grp->GetGroupName());
412 grp->DisconnectMembers();
414 else {
415 // skip save
416 return;
419 else {
420 // group we want to save has the
421 // same set of plugins and member IDs
422 // as the one already there...
423 // so do not disconnect members
426 else {
427 // we are saving a brand new group, so make
428 // sure that all members are new
429 grp->DisconnectMembers();
432 // save the new one
433 grp->Save(*dlg.GetEngine());
435 // clean up the old engine's group, so we don't leave
436 // garbage behind... do this after a successful
437 // save, so that we don't delete existing knowledge
438 // before we've crossed over
439 if( delete_old ) {
440 barryverbose("Engine change detected in config: deleting old config '" << entry.GetConfigGroup()->GetGroupName() << "' from engine " << entry.GetEngine()->GetVersion() << " in order to save it to engine " << dlg.GetEngine()->GetVersion());
441 entry.GetEngine()->DeleteGroup(entry.GetConfigGroup()->GetGroupName());
445 catch( OpenSync::Config::SaveError &se ) {
446 barryverbose("Exception during save: " << se.what());
447 wxString msg = _W("Unable to save configuration for this device.\nError: ");
448 msg += wxString(se.what(), wxConvUTF8);
449 wxMessageBox(msg, _W("OpenSync Save Error"),
450 wxOK | wxICON_ERROR, m_parent);
451 return;
454 // save the extras... this is cheap, so no need to check
455 // skip_rewrite
456 dlg.GetExtras()->Save(wxGetApp().GetGlobalConfig(),
457 dlg.GetGroup()->GetGroupName());
459 // update the device set
460 entry.SetConfigGroup(dlg.GetGroup(), dlg.GetEngine(),
461 dlg.GetExtras());
462 entry.SetDeviceName(dlg.GetDeviceName());
464 // update!
465 RefillList();
469 void SyncMode::CheckConfigured(DeviceSet::subset_type &subset)
471 for( DeviceSet::subset_type::iterator i = subset.begin();
472 i != subset.end();
473 ++i )
475 DeviceEntry &device = *(*i);
476 if( !device.IsConnected() )
477 continue;
478 if( device.IsConfigured() )
479 continue;
481 wxString msg = wxString::Format(
482 // TRANSLATORS: first %s is the PIN number, and
483 // the second (in parentheses) is the device name.
484 _W("Selected device %s (%s) is not yet configured. Configure now?"),
485 wxString(device.GetPin().Str().c_str(), wxConvUTF8).c_str(),
486 wxString(device.GetDeviceName().c_str(), wxConvUTF8).c_str());
488 int response = wxMessageBox(msg,
489 _W("Configure Now?"), wxYES_NO, m_parent);
490 if( response == wxYES ) {
491 ConfigureDevice(device);
496 void SyncMode::RefillList()
498 DeviceSet::subset_type subset = GetSelectedDevices();
499 FillDeviceList();
500 ReselectDevices(subset);
503 int SyncMode::GetSelectedDevice()
505 if( m_device_list->GetSelectedItemCount() != 1 ) {
506 wxMessageBox(_W("Please select one device from the list."),
507 _W("Device List"), wxOK | wxICON_ERROR, m_parent);
508 return -1;
511 // find selected device
512 long item = -1;
513 item = m_device_list->GetNextItem(item, wxLIST_NEXT_ALL,
514 wxLIST_STATE_SELECTED);
515 return item;
519 // Returns an index into the config group for the given device index
520 // that represents the authoritative side of the sync. This means
521 // that during the 1-Way Reset, the plugin at this index must NOT
522 // be zapped! All the others must be zapped.
524 // Returns -1 if the user cancels, or if not enough data to decide.
526 int SyncMode::GetAuthoritativeSide(int device_index)
528 // grab the device entry and group config
529 DeviceEntry &entry = (*m_device_set)[device_index];
530 OpenSync::Config::Group *group = entry.GetConfigGroup();
531 if( !group )
532 return -1;
534 // build message
535 wxString intro(_W(
536 "Which device / application should be considered\n"
537 "authoritative?\n"
538 "\n"
539 "All data in non-authoritative devices / applications\n"
540 "will be deleted in order to setup a straight copy on\n"
541 "the next sync."));
543 // build list of devices / applications
544 wxArrayString list;
545 OpenSync::Config::Group::iterator gi = group->begin();
546 for( ; gi != group->end(); ++gi ) {
547 // the Barry plugin is special
548 if( dynamic_cast<OpenSync::Config::Barry*>( (*gi).get() ) ) {
549 // this is a Barry plugin, so display the
550 // device name, not the plugin name
551 string device_name = entry.GetPin().Str();
552 if( entry.GetDeviceName().size() )
553 device_name += " (" + entry.GetDeviceName() + ")";
555 list.Add( wxString(device_name.c_str(), wxConvUTF8) );
557 else {
558 // add the application name
559 list.Add( wxString((*gi)->GetAppName().c_str(),
560 wxConvUTF8) );
564 // ask the user
565 int choice = wxGetSingleChoiceIndex(intro,
566 _W("Select Authoritative Device / Application"),
567 list, m_parent);
568 return choice;
571 bool SyncMode::ZapConflicts(int device_index, int authoritative_side)
573 // grab the device entry and group config
574 DeviceEntry &entry = (*m_device_set)[device_index];
575 OpenSync::Config::Group *group = entry.GetConfigGroup();
576 if( !group )
577 return false;
579 // cycle through list of sync plugins, zapping each
580 // non-authoritative one
581 OpenSync::Config::Group::iterator gi = group->begin();
582 for( int i = 0; gi != group->end(); ++gi, ++i ) {
584 // skip the authoritative plugin!
585 if( i == authoritative_side )
586 continue;
588 OpenSync::Config::Plugin &plugin = *(*gi);
590 // create a configUI object, for zapping
591 ConfigUI::ptr ui = ConfigUI::CreateConfigUI(plugin.GetAppName());
592 if( !ui.get() ) {
593 // no possibility for zapping here... so just
594 // assume that it doesn't need it for now...
595 // worst that can happen, should be it slow-syncs
596 // all the time
597 continue;
600 bool success = ui->ZapData(m_parent, *gi, entry.GetEngine());
601 if( !success ) {
602 // if the user cancels one, cancel all the rest
603 return false;
607 // if we reach this, we succeeded
608 return true;
611 void SyncMode::RewriteConfig(int device_index)
613 // grab the device entry and group config
614 DeviceEntry &entry = (*m_device_set)[device_index];
615 OpenSync::Config::Group *group = entry.GetConfigGroup();
616 OpenSync::API *engine = entry.GetEngine();
617 if( !group || !engine )
618 return;
620 string group_name = group->GetGroupName();
622 try {
623 // delete the existing group name
624 engine->DeleteGroup(group_name);
626 // disconnect the plugins from the group, to
627 // make them "new" again
628 group->DisconnectMembers();
630 // save
631 group->Save(*engine);
633 // success!
634 barryverbose(group_name << " group config rewritten");
635 return;
637 catch( std::runtime_error &re ) {
638 ostringstream oss;
639 oss << _C("Unable to rewrite config! Start over manually. "
640 "Error: ") << re.what();
641 wxString msg(oss.str().c_str(), wxConvUTF8);
643 wxMessageBox(msg, _W("Error Rewriting Config"),
644 wxOK | wxICON_ERROR, m_parent);
647 // if we get here, the group is in an undefined state,
648 // so delete it to make sure nothing odd is left behind
649 try {
650 engine->DeleteGroup(group_name);
652 catch( std::runtime_error &re ) {
653 barryverbose("Error while deleting undefined group '" << group_name << "': " << re.what());
657 bool SyncMode::WarnAbout1WayReset()
659 int answer = wxMessageBox( _W("The sync config you are about to save "
660 "is sufficiently different from the existing one that "
661 "a 1-Way Reset will be required. You will need to "
662 "perform the reset at your earliest convenience, before "
663 "your next sync.\n\n"
664 "Continue anyway?"),
665 _W("1-Way Reset Warning"),
666 wxYES_NO | wxICON_QUESTION, m_parent);
667 return answer == wxYES;
670 void SyncMode::OnSyncNow(wxCommandEvent &event)
672 DeviceSet::subset_type subset = GetSelectedDevices();
673 if( subset.size() == 0 )
674 return; // nothing to do
676 // make sure an app is not running
677 if( m_cui.get() && m_cui->IsAppRunning() ) {
678 wxMessageBox(_W("An application is currently running."),
679 _W("Sync Error"), wxOK | wxICON_ERROR, m_parent);
680 return;
683 // check that all selections are configured
684 CheckConfigured(subset);
686 // do the sync
687 SyncStatusDlg dlg(m_parent, subset);
688 dlg.ShowModal();
690 // update the timestamps
691 RefillList();
694 void SyncMode::OnConfigure(wxCommandEvent &event)
696 int item = GetSelectedDevice();
697 if( item != -1 )
698 ConfigureDevice(item);
701 void SyncMode::OnRunApp(wxCommandEvent &event)
703 // make sure it's not already running
704 if( m_cui.get() && m_cui->IsAppRunning() ) {
705 wxMessageBox(_W("An application is already running."),
706 _W("Run App Error"), wxOK | wxICON_ERROR, m_parent);
707 return;
710 // find selected device
711 int item = GetSelectedDevice();
712 if( item == -1 )
713 return;
715 // retrieve device's group config
716 DeviceEntry &entry = (*m_device_set)[item];
717 OpenSync::Config::Plugin *plugin = 0;
718 if( entry.GetConfigGroup() )
719 plugin = entry.GetConfigGroup()->GetNonBarryPlugin();
720 if( !plugin )
721 return;
723 // run the app
724 m_cui = ConfigUI::CreateConfigUI(plugin->GetAppName());
725 if( m_cui.get() )
726 m_cui->RunApp(m_parent);
729 void SyncMode::On1WayReset(wxCommandEvent &event)
731 int item = GetSelectedDevice();
732 if( item == -1 )
733 return;
735 // let user pick the authoritative side of the sync
736 int side = GetAuthoritativeSide(item);
737 if( side == -1 )
738 return;
740 // zap the data of all remaining sync elements
741 if( !ZapConflicts(item, side) )
742 return;
744 // rewrite the config
745 RewriteConfig(item);
747 // reload the deviceset
748 RefillList();
750 // tell the user all's well
751 wxMessageBox(_W("1-Way Reset is complete, and ready to sync."),
752 _W("Reset Complete"), wxOK | wxICON_INFORMATION, m_parent);
755 void SyncMode::OnListSelChange(wxListEvent &event)
757 UpdateButtons();
760 void SyncMode::OnConfigureDevice(wxListEvent &event)
762 ConfigureDevice(event.GetIndex());