Bumped copyright dates for 2013
[barry.git] / desktop / src / GroupCfgDlg.cc
blobf856a683c4f548579d826752f9b803bf0c702fb7
1 ///
2 /// \file GroupCfgDlg.cc
3 /// The configuration dialog used when a user double clicks
4 /// on a device in the device list. It lets the user choose
5 /// the app to sync with Barry, as well as the engine to use.
6 ///
8 /*
9 Copyright (C) 2009-2013, Net Direct Inc. (http://www.netdirect.ca/)
11 This program is free software; you can redistribute it and/or modify
12 it under the terms of the GNU General Public License as published by
13 the Free Software Foundation; either version 2 of the License, or
14 (at your option) any later version.
16 This program is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
20 See the GNU General Public License in the COPYING file at the
21 root directory of this project for more details.
24 #include "GroupCfgDlg.h"
25 #include "windowids.h"
26 #include "configui.h"
27 #include "barrydesktop.h"
28 #include <string>
29 #include "wxi18n.h"
31 using namespace std;
32 using namespace OpenSync;
34 BEGIN_EVENT_TABLE(GroupCfgDlg, wxDialog)
35 EVT_BUTTON (Dialog_GroupCfg_AppConfigButton,
36 GroupCfgDlg::OnConfigureApp)
37 EVT_CHECKBOX (Dialog_GroupCfg_ContactsCheck,
38 GroupCfgDlg::OnSyncTypeCheck)
39 EVT_CHECKBOX (Dialog_GroupCfg_EventsCheck,
40 GroupCfgDlg::OnSyncTypeCheck)
41 EVT_CHECKBOX (Dialog_GroupCfg_NotesCheck,
42 GroupCfgDlg::OnSyncTypeCheck)
43 EVT_CHECKBOX (Dialog_GroupCfg_TodosCheck,
44 GroupCfgDlg::OnSyncTypeCheck)
45 EVT_TEXT (Dialog_GroupCfg_EngineCombo,
46 GroupCfgDlg::OnEngineComboChange)
47 EVT_TEXT (Dialog_GroupCfg_AppCombo,
48 GroupCfgDlg::OnAppComboChange)
49 END_EVENT_TABLE()
51 //////////////////////////////////////////////////////////////////////////////
52 // GroupCfgDlg class
54 GroupCfgDlg::GroupCfgDlg(wxWindow *parent,
55 const DeviceEntry &device,
56 OpenSync::APISet &apiset)
57 : wxDialog(parent, Dialog_GroupCfg, _W("Device Sync Configuration"))
58 , m_device(device)
59 , m_apiset(apiset)
60 , m_app_count(0)
61 , m_engine(0)
62 , m_barry_plugin(m_device.GetPin())
63 , m_topsizer(0)
64 , m_appsizer(0)
65 , m_engine_combo(0)
66 , m_app_combo(0)
67 , m_password_edit(0)
68 , m_name_edit(0)
69 , m_debug_check(0)
70 , m_sync_contacts_check(0)
71 , m_sync_events_check(0)
72 , m_sync_notes_check(0)
73 , m_sync_todos_check(0)
74 , m_favour_radios(0)
76 std::string appname;
78 // make sure there is at least one engine
79 if( !apiset.os22() && !apiset.os40() )
80 throw std::logic_error(_C("Must have at least one engine in GroupCfgDlg"));
82 // setup the raw GUI
83 CreateLayout();
85 // set window title to device PIN and name
86 string label = _C("Configure Device - ");
87 label += m_device.GetPin().Str();
88 if( m_device.GetDeviceName().size() )
89 label += " (" + m_device.GetDeviceName() + ")";
90 SetTitle(wxString(label.c_str(), wxConvUTF8));
92 // copy over the extras
93 // and initialize the engine / sync type map with config data
94 // if available
95 if( m_device.GetExtras() ) {
96 const DeviceExtras *extras = m_device.GetExtras();
97 m_favour_plugin_name = extras->m_favour_plugin_name;
99 m_sync_types[apiset.os22()] = extras->m_sync_types;
100 m_sync_types[apiset.os40()] = extras->m_sync_types;
102 else {
103 // default to all on, in worst case scenario
104 m_sync_types[apiset.os22()] = PST_ALL;
105 m_sync_types[apiset.os40()] = PST_ALL;
108 // initialize current engine pointer
109 if( m_device.GetEngine() ) {
110 m_engine = const_cast<OpenSync::API*> (m_device.GetEngine());
113 // initialize local group and plugin data
114 if( m_engine && m_device.GetConfigGroup() ) {
115 const Config::Group *group = m_device.GetConfigGroup();
116 // use existing group name, if available
117 m_group_name = group->GetGroupName();
119 // copy Barry plugin config, if available
120 if( group->HasBarryPlugins() )
121 m_barry_plugin = group->GetBarryPlugin();
123 // copy non-Barry plugin config, if available
124 const Config::Plugin *plugin = group->GetNonBarryPlugin();
125 if( plugin ) {
126 appname = plugin->GetAppName();
127 m_plugins[m_engine][appname].reset( plugin->Clone() );
130 else {
131 m_group_name = "barrydesktop_" + m_device.GetPin().Str();
134 SelectCurrentEngine();
135 LoadBarryConfig();
136 SelectApplication(appname);
137 SelectFavour();
139 if( m_app_count == 0 ) {
140 wxMessageBox(_W("No supported applications found. You may need to install some opensync plugins."),
141 _W("No App Found"), wxOK | wxICON_ERROR, this);
145 void GroupCfgDlg::CreateLayout()
147 m_topsizer = new wxBoxSizer(wxVERTICAL);
148 AddEngineSizer(m_topsizer);
149 AddConfigSizer(m_topsizer);
150 AddSyncTypeSizer(m_topsizer);
151 AddFavourSizer(m_topsizer);
152 AddButtonSizer(m_topsizer);
154 SetSizer(m_topsizer);
155 m_topsizer->SetSizeHints(this);
156 m_topsizer->Layout();
159 void GroupCfgDlg::AddEngineSizer(wxSizer *sizer)
161 wxSizer *engine = new wxStaticBoxSizer(
162 new wxStaticBox(this, wxID_ANY, _W("OpenSync Engine")),
163 wxHORIZONTAL
166 wxArrayString engines;
167 if( m_apiset.os22() )
168 engines.Add(wxString(m_apiset.os22()->GetVersion(),wxConvUTF8));
169 if( m_apiset.os40() )
170 engines.Add(wxString(m_apiset.os40()->GetVersion(),wxConvUTF8));
172 engine->Add(
173 m_engine_combo = new wxComboBox(this,
174 Dialog_GroupCfg_EngineCombo, _T(""),
175 wxDefaultPosition, wxSize(100, -1), engines,
176 wxCB_READONLY),
177 1, wxALL, 5);
179 sizer->Add(engine, 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
181 // if only one engine is available, don't bother showing the combo
182 if( !m_apiset.os22() || !m_apiset.os40() ) {
183 sizer->Hide(engine, true);
187 void GroupCfgDlg::AddConfigSizer(wxSizer *sizer)
189 wxSizer *config = new wxBoxSizer(wxHORIZONTAL);
190 AddBarrySizer(config);
191 AddAppSizer(config);
193 sizer->Add(config, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
196 void GroupCfgDlg::AddBarrySizer(wxSizer *sizer)
198 wxSizer *barry = new wxStaticBoxSizer(
199 new wxStaticBox(this, wxID_ANY, _W("Barry Config")),
200 wxVERTICAL
203 wxSizer *dname = new wxBoxSizer(wxHORIZONTAL);
204 dname->Add(
205 new wxStaticText(this, wxID_ANY, _W("Name:")),
206 0, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL, 2);
207 dname->Add(
208 m_name_edit = new wxTextCtrl(this, wxID_ANY, _T("")),
209 1, wxALIGN_LEFT, 0);
210 barry->Add(dname, 0, wxALL | wxEXPAND, 5);
212 wxSizer *password = new wxBoxSizer(wxHORIZONTAL);
213 password->Add(
214 new wxStaticText(this, wxID_ANY, _W("Password:")),
215 0, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL, 2);
216 password->Add(
217 m_password_edit = new wxTextCtrl(this, wxID_ANY, _T(""),
218 wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD),
219 1, wxALIGN_LEFT, 0);
220 barry->Add(password, 0, wxALL | wxEXPAND, 5);
222 barry->Add(
223 m_debug_check = new wxCheckBox(this, wxID_ANY,
224 _W("Debug output during sync")),
225 0, wxALIGN_LEFT, 5);
227 sizer->Add(barry, 0, wxRIGHT | wxEXPAND, 5);
230 void GroupCfgDlg::AddAppSizer(wxSizer *sizer)
232 m_appsizer = new wxStaticBoxSizer(
233 new wxStaticBox(this, wxID_ANY, _W("Application")),
234 wxVERTICAL
237 UpdateAppSizer(false);
239 sizer->Add(m_appsizer, 0, wxLEFT | wxEXPAND, 5);
242 void GroupCfgDlg::UpdateAppSizer(bool relayout)
244 // start fresh
245 m_appsizer->Clear(true);
247 wxArrayString appnames;
248 LoadAppNames(appnames);
249 appnames.Sort();
251 // FIXME - make size of combobox the size of the longest
252 // string in apps? using textextent calcs
253 m_appsizer->Add(
254 m_app_combo = new wxComboBox(this,
255 Dialog_GroupCfg_AppCombo, _T(""),
256 wxDefaultPosition, wxSize(200, -1), appnames,
257 wxCB_READONLY),
258 0, wxALL | wxALIGN_CENTRE, 5);
259 m_appsizer->Add(
260 new wxButton(this, Dialog_GroupCfg_AppConfigButton,
261 _W("&Configure...")),
262 0, wxALL | wxALIGN_CENTRE, 5);
264 // in case this is called after the dialog is already displayed,
265 // we need to readjust everything
266 if( relayout )
267 m_topsizer->Layout();
270 void GroupCfgDlg::LoadAppNames(wxArrayString &appnames)
272 // need to load app names based on engine plugin availability
273 // NOTE - do not load the Barry plugin, since that's already assumed
275 if( !m_engine ) {
276 // no engine available
277 appnames.Add(_W("No engine selected"));
278 return;
281 string_list_type plugins;
282 try {
283 m_engine->GetPluginNames(plugins);
285 catch( std::exception &e ) {
286 barrylog(_C("Exception caught in LoadAppNames: ") << e.what());
287 return;
290 // cycle through all available plugins, and add the ones
291 // that we support
292 int added = 0;
293 string_list_type::const_iterator i = plugins.begin();
294 for( ; i != plugins.end(); ++i ) {
295 string appname;
296 if( m_engine->GetConverter().IsPluginSupported(*i, &appname) ) {
297 // found a supported plugin...
299 // skip Barry
300 if( appname == Config::Barry::AppName() )
301 continue;
303 appnames.Add( wxString(appname.c_str(), wxConvUTF8) );
304 added++;
308 m_app_count = added;
310 if( m_app_count == 0 ) {
311 appnames.Add(_W("No supported plugins available"));
312 return;
316 void GroupCfgDlg::AddSyncTypeSizer(wxSizer *sizer)
318 wxStaticBoxSizer *checks = new wxStaticBoxSizer(wxHORIZONTAL, this,
319 _W("Sync:"));
321 checks->Add( m_sync_contacts_check = new wxCheckBox(this,
322 Dialog_GroupCfg_ContactsCheck, _W("Contacts")),
323 0, wxRIGHT | wxEXPAND, 10);
324 checks->Add( m_sync_events_check = new wxCheckBox(this,
325 Dialog_GroupCfg_EventsCheck, _W("Events")),
326 0, wxRIGHT | wxEXPAND, 10);
327 checks->Add( m_sync_notes_check = new wxCheckBox(this,
328 Dialog_GroupCfg_NotesCheck, _W("Notes")),
329 0, wxRIGHT | wxEXPAND, 10);
330 checks->Add( m_sync_todos_check = new wxCheckBox(this,
331 Dialog_GroupCfg_TodosCheck, _W("To-dos")),
332 0, wxRIGHT | wxEXPAND, 10);
334 sizer->Add( checks,
335 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
338 void GroupCfgDlg::AddFavourSizer(wxSizer *sizer)
340 wxArrayString labels;
341 // TRANSLATORS: these 3 strings are options for default
342 // conflict resolution during syncing
343 labels.Add( _W("Favour device") );
344 labels.Add( _W("Favour application") );
345 labels.Add( _W("Ask me") );
347 sizer->Add( m_favour_radios = new wxRadioBox(this, wxID_ANY,
348 _W("To Resolve Conflicts:"),
349 wxDefaultPosition, wxDefaultSize,
350 labels, 1, wxRA_SPECIFY_ROWS),
351 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
354 void GroupCfgDlg::AddButtonSizer(wxSizer *sizer)
356 wxSizer *button = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
357 sizer->Add(button, 0, wxALL | wxEXPAND | wxALIGN_RIGHT, 10);
360 void GroupCfgDlg::SelectCurrentEngine()
362 if( m_engine_combo ) {
363 if( !m_engine ) {
364 m_engine = m_apiset.os22() ?
365 m_apiset.os22() : m_apiset.os40();
368 if( m_engine )
369 m_engine_combo->SetValue(
370 wxString(m_engine->GetVersion(), wxConvUTF8));
372 UpdateAppSizer();
376 void GroupCfgDlg::LoadBarryConfig()
378 Config::Barry &bp = m_barry_plugin;
380 // use m_device here, since BarryDesktopApp::GetDeviceName()
381 // may return an empty string if the device is not currently
382 // plugged in
383 wxString dname(m_device.GetDeviceName().c_str(), wxConvUTF8);
385 m_name_edit->SetValue(dname);
386 m_password_edit->SetValue(wxString(bp.GetPassword().c_str(), wxConvUTF8));
387 m_debug_check->SetValue( bp.IsDebugMode() );
390 void GroupCfgDlg::SelectApplication(const std::string appname)
392 m_app_combo->SetValue(wxString(appname.c_str(), wxConvUTF8));
393 SelectSyncTypes();
396 void GroupCfgDlg::SelectSyncTypes()
398 if( !m_engine ) {
399 SetSyncTypeChecks(PST_NONE);
400 EnableSyncTypeChecks(PST_NONE);
401 return;
404 string app = GetCurrentAppName();
405 plugin_ptr ap = GetCurrentPlugin();
407 // calculate the supported sync types
408 // Note: we could also take the Barry plugin config into
409 // consideration here, but so far, we just use the
410 // opensync group config
411 pst_type supported = PST_NONE;
412 if( ap.get() ) {
413 supported = m_barry_plugin.GetSupportedSyncTypes(*m_engine)
414 & ap->GetSupportedSyncTypes(*m_engine);
417 // make sure our current selection is limited by our new
418 // set of supported plugins
419 m_sync_types[m_engine] &= supported;
421 // enable the checkboxes according to our ability
422 EnableSyncTypeChecks(supported);
424 // set the checkboxes according to our choices
425 SetSyncTypeChecks(m_sync_types[m_engine]);
428 void GroupCfgDlg::SelectFavour()
430 if( m_engine &&
431 m_favour_plugin_name == Config::Barry::PluginName(*m_engine) )
433 m_favour_radios->SetSelection(0);
435 else if( m_engine && m_favour_plugin_name.size() ) {
436 m_favour_radios->SetSelection(1);
438 else {
439 // ask the user
440 m_favour_radios->SetSelection(2);
444 void GroupCfgDlg::EnableSyncTypeChecks(pst_type types)
446 m_sync_contacts_check->Enable( types & PST_CONTACTS );
447 m_sync_events_check ->Enable( types & PST_EVENTS );
448 m_sync_notes_check ->Enable( types & PST_NOTES );
449 m_sync_todos_check ->Enable( types & PST_TODOS );
452 void GroupCfgDlg::SetSyncTypeChecks(pst_type types)
454 m_sync_contacts_check->SetValue( types & PST_CONTACTS );
455 m_sync_events_check ->SetValue( types & PST_EVENTS );
456 m_sync_notes_check ->SetValue( types & PST_NOTES );
457 m_sync_todos_check ->SetValue( types & PST_TODOS );
460 GroupCfgDlg::pst_type GroupCfgDlg::GetSyncTypeChecks()
462 pst_type types = PST_NONE;
463 if( m_sync_contacts_check->GetValue() ) types |= PST_CONTACTS;
464 if( m_sync_events_check ->GetValue() ) types |= PST_EVENTS;
465 if( m_sync_notes_check ->GetValue() ) types |= PST_NOTES;
466 if( m_sync_todos_check ->GetValue() ) types |= PST_TODOS;
467 return types;
470 std::string GroupCfgDlg::GetCurrentAppName() const
472 wxString app = m_app_combo->GetValue();
473 return std::string(app.utf8_str());
476 GroupCfgDlg::plugin_ptr GroupCfgDlg::GetCurrentPlugin()
478 string appname = GetCurrentAppName();
479 appcfg_map &cfgs = m_plugins[m_engine];
480 appcfg_map::iterator pi = cfgs.find(appname);
481 if( pi != cfgs.end() )
482 return pi->second;
483 else
484 return plugin_ptr(); // not found, return empty ptr
487 void GroupCfgDlg::OnConfigureApp(wxCommandEvent &event)
489 string app = GetCurrentAppName();
490 if( app.size() == 0 ) {
491 wxMessageBox(_W("Please select an application."),
492 _W("Application Config"), wxOK | wxICON_ERROR, this);
493 return;
496 ConfigUI::ptr ui = ConfigUI::CreateConfigUI(app);
498 if( !ui.get() ) {
499 wxMessageBox(_W("No configuration interface available for this Application."),
500 _W("Application Config"),
501 wxOK | wxICON_ERROR, this);
502 return;
505 if( ui->Configure(this, GetCurrentPlugin()) ) {
506 ConfigUI::plugin_ptr plugin = ui->GetPlugin();
507 if( plugin.get() ) {
508 m_plugins[m_engine][app] = plugin;
510 // if this is the first time, default to all
511 if( m_sync_types[m_engine] == PST_NONE )
512 m_sync_types[m_engine] = PST_ALL;
514 // update the types checkboxes
515 SelectSyncTypes();
520 void GroupCfgDlg::OnEngineComboChange(wxCommandEvent &event)
522 // remember what plugin we're using
523 plugin_ptr old_app = GetCurrentPlugin();
525 // update engine pointer
526 wxString newEngine = m_engine_combo->GetValue();
527 if( m_apiset.os22() && newEngine == wxString(m_apiset.os22()->GetVersion(), wxConvUTF8) ) {
528 m_engine = m_apiset.os22();
530 else if( m_apiset.os40() && newEngine == wxString(m_apiset.os40()->GetVersion(), wxConvUTF8) ) {
531 m_engine = m_apiset.os40();
534 // update the application list
535 UpdateAppSizer();
537 // if plugin can be configured in new engine, keep our current
538 // config, otherwise, reset
539 if( old_app.get() && m_engine->GetConverter().IsPluginSupported(old_app->GetPluginName(*m_engine)) ) {
540 // update the app list
541 SelectApplication(old_app->GetAppName());
543 else {
544 // leave GUI as is, and zap our plugin data
545 SelectApplication("");
549 void GroupCfgDlg::OnAppComboChange(wxCommandEvent &event)
551 SelectSyncTypes();
554 void GroupCfgDlg::OnSyncTypeCheck(wxCommandEvent &event)
556 if( !m_engine )
557 return;
559 m_sync_types[m_engine] = GetSyncTypeChecks();
562 bool GroupCfgDlg::TransferDataFromWindow()
564 // engine must be set!
565 if( !m_engine ) {
566 wxMessageBox(_W("Please select an engine."),
567 _W("Device Config"), wxOK | wxICON_ERROR, this);
568 return false;
571 // make sure the Barry plugin is configured
572 if( !m_barry_plugin.IsConfigured(*m_engine) ) {
573 wxMessageBox(_W("Barry doesn't have a PIN number. This should never happen."),
574 _W("Device Config"), wxOK | wxICON_ERROR, this);
575 return false;
578 // make sure the application plugin is configured
579 plugin_ptr app = GetCurrentPlugin();
580 if( !app.get() || !app->IsConfigured(*m_engine) ) {
581 // the app hasn't been configured yet, do it automatically
582 wxCommandEvent event;
583 OnConfigureApp(event);
585 app = GetCurrentPlugin();
586 if( !app.get() || !app->IsConfigured(*m_engine) ) {
587 wxMessageBox(_W("The application plugin is not fully configured."),
588 _W("Application Config"), wxOK | wxICON_ERROR, this);
589 return false;
593 // copy over barry specific settings
594 m_device_name = string(m_name_edit->GetValue().utf8_str());
595 m_barry_plugin.SetPassword(string(m_password_edit->GetValue().utf8_str()));
596 m_barry_plugin.DebugMode(m_debug_check->GetValue());
598 // make sure conflict resolution is known
599 int findex = m_favour_radios->GetSelection();
600 switch( findex )
602 case 0: // Favour device
603 m_favour_plugin_name = Config::Barry::PluginName(*m_engine);
604 break;
606 case 1: // Favour application
607 m_favour_plugin_name = app->GetPluginName(*m_engine);
608 break;
610 case 2: // Ask me
611 m_favour_plugin_name.clear();
612 break;
614 default: // borked
615 wxMessageBox(_W("Please select conflict resolution method."),
616 _W("Conflict Resolution"), wxOK | wxICON_ERROR, this);
617 return false;
620 // save the new device name
621 wxGetApp().SetDeviceName(m_barry_plugin.GetPin(), m_device_name);
623 // save the sync type checkboxes
624 m_sync_types[m_engine] = GetSyncTypeChecks();
626 return true;
629 int GroupCfgDlg::ShowModal()
631 int status = wxDialog::ShowModal();
632 plugin_ptr app = GetCurrentPlugin();
633 if( status == wxID_OK && app.get() ) {
634 // construct a new group from user's results
635 m_group.reset( new Config::Group(m_group_name) );
636 m_group->AddPlugin( m_barry_plugin.Clone() );
637 m_group->AddPlugin( app->Clone() );
639 // don't forget the extras
640 m_extras.reset( new DeviceExtras(m_barry_plugin.GetPin()) );
641 m_extras->m_favour_plugin_name = m_favour_plugin_name;
642 m_extras->m_sync_types = m_sync_types[m_engine];
644 else {
645 m_group.reset();
646 m_extras.reset();
648 return status;