lib: added implicit ctor converter from DatabaseDatabase to DBListType
[barry/progweb.git] / desktop / src / GroupCfgDlg.cc
blob2078237e550a4664586e3fc869d78069a28ef5c8
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-2012, 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>
30 using namespace std;
31 using namespace OpenSync;
33 BEGIN_EVENT_TABLE(GroupCfgDlg, wxDialog)
34 EVT_BUTTON (Dialog_GroupCfg_AppConfigButton,
35 GroupCfgDlg::OnConfigureApp)
36 EVT_CHECKBOX (Dialog_GroupCfg_ContactsCheck,
37 GroupCfgDlg::OnSyncTypeCheck)
38 EVT_CHECKBOX (Dialog_GroupCfg_EventsCheck,
39 GroupCfgDlg::OnSyncTypeCheck)
40 EVT_CHECKBOX (Dialog_GroupCfg_NotesCheck,
41 GroupCfgDlg::OnSyncTypeCheck)
42 EVT_CHECKBOX (Dialog_GroupCfg_TodosCheck,
43 GroupCfgDlg::OnSyncTypeCheck)
44 EVT_TEXT (Dialog_GroupCfg_EngineCombo,
45 GroupCfgDlg::OnEngineComboChange)
46 EVT_TEXT (Dialog_GroupCfg_AppCombo,
47 GroupCfgDlg::OnAppComboChange)
48 END_EVENT_TABLE()
50 //////////////////////////////////////////////////////////////////////////////
51 // GroupCfgDlg class
53 GroupCfgDlg::GroupCfgDlg(wxWindow *parent,
54 const DeviceEntry &device,
55 OpenSync::APISet &apiset)
56 : wxDialog(parent, Dialog_GroupCfg, _T("Device Sync Configuration"))
57 , m_device(device)
58 , m_apiset(apiset)
59 , m_app_count(0)
60 , m_engine(0)
61 , m_barry_plugin(m_device.GetPin())
62 , m_topsizer(0)
63 , m_appsizer(0)
64 , m_engine_combo(0)
65 , m_app_combo(0)
66 , m_password_edit(0)
67 , m_name_edit(0)
68 , m_debug_check(0)
69 , m_sync_contacts_check(0)
70 , m_sync_events_check(0)
71 , m_sync_notes_check(0)
72 , m_sync_todos_check(0)
73 , m_favour_radios(0)
75 std::string appname;
77 // make sure there is at least one engine
78 if( !apiset.os22() && !apiset.os40() )
79 throw std::logic_error("Must have at least one engine in GroupCfgDlg");
81 // setup the raw GUI
82 CreateLayout();
84 // set window title to device PIN and name
85 string label = "Configure Device - ";
86 label += m_device.GetPin().Str();
87 if( m_device.GetDeviceName().size() )
88 label += " (" + m_device.GetDeviceName() + ")";
89 SetTitle(wxString(label.c_str(), wxConvUTF8));
91 // copy over the extras
92 // and initialize the engine / sync type map with config data
93 // if available
94 if( m_device.GetExtras() ) {
95 const DeviceExtras *extras = m_device.GetExtras();
96 m_favour_plugin_name = extras->m_favour_plugin_name;
98 m_sync_types[apiset.os22()] = extras->m_sync_types;
99 m_sync_types[apiset.os40()] = extras->m_sync_types;
101 else {
102 // default to all on, in worst case scenario
103 m_sync_types[apiset.os22()] = PST_ALL;
104 m_sync_types[apiset.os40()] = PST_ALL;
107 // initialize current engine pointer
108 if( m_device.GetEngine() ) {
109 m_engine = const_cast<OpenSync::API*> (m_device.GetEngine());
112 // initialize local group and plugin data
113 if( m_engine && m_device.GetConfigGroup() ) {
114 const Config::Group *group = m_device.GetConfigGroup();
115 // use existing group name, if available
116 m_group_name = group->GetGroupName();
118 // copy Barry plugin config, if available
119 if( group->HasBarryPlugins() )
120 m_barry_plugin = group->GetBarryPlugin();
122 // copy non-Barry plugin config, if available
123 const Config::Plugin *plugin = group->GetNonBarryPlugin();
124 if( plugin ) {
125 appname = plugin->GetAppName();
126 m_plugins[m_engine][appname].reset( plugin->Clone() );
129 else {
130 m_group_name = "barrydesktop_" + m_device.GetPin().Str();
133 SelectCurrentEngine();
134 LoadBarryConfig();
135 SelectApplication(appname);
136 SelectFavour();
138 if( m_app_count == 0 ) {
139 wxMessageBox(_T("No supported applications found. You may need to install some opensync plugins."),
140 _T("No App Found"), wxOK | wxICON_ERROR, this);
144 void GroupCfgDlg::CreateLayout()
146 m_topsizer = new wxBoxSizer(wxVERTICAL);
147 AddEngineSizer(m_topsizer);
148 AddConfigSizer(m_topsizer);
149 AddSyncTypeSizer(m_topsizer);
150 AddFavourSizer(m_topsizer);
151 AddButtonSizer(m_topsizer);
153 SetSizer(m_topsizer);
154 m_topsizer->SetSizeHints(this);
155 m_topsizer->Layout();
158 void GroupCfgDlg::AddEngineSizer(wxSizer *sizer)
160 wxSizer *engine = new wxStaticBoxSizer(
161 new wxStaticBox(this, wxID_ANY, _T("OpenSync Engine")),
162 wxHORIZONTAL
165 wxArrayString engines;
166 if( m_apiset.os22() )
167 engines.Add(wxString(m_apiset.os22()->GetVersion(),wxConvUTF8));
168 if( m_apiset.os40() )
169 engines.Add(wxString(m_apiset.os40()->GetVersion(),wxConvUTF8));
171 engine->Add(
172 m_engine_combo = new wxComboBox(this,
173 Dialog_GroupCfg_EngineCombo, _T(""),
174 wxDefaultPosition, wxSize(100, -1), engines,
175 wxCB_READONLY),
176 1, wxALL, 5);
178 sizer->Add(engine, 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
180 // if only one engine is available, don't bother showing the combo
181 if( !m_apiset.os22() || !m_apiset.os40() ) {
182 sizer->Hide(engine, true);
186 void GroupCfgDlg::AddConfigSizer(wxSizer *sizer)
188 wxSizer *config = new wxBoxSizer(wxHORIZONTAL);
189 AddBarrySizer(config);
190 AddAppSizer(config);
192 sizer->Add(config, 1, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
195 void GroupCfgDlg::AddBarrySizer(wxSizer *sizer)
197 wxSizer *barry = new wxStaticBoxSizer(
198 new wxStaticBox(this, wxID_ANY, _T("Barry Config")),
199 wxVERTICAL
202 wxSizer *dname = new wxBoxSizer(wxHORIZONTAL);
203 dname->Add(
204 new wxStaticText(this, wxID_ANY, _T("Name:")),
205 0, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL, 2);
206 dname->Add(
207 m_name_edit = new wxTextCtrl(this, wxID_ANY, _T("")),
208 1, wxALIGN_LEFT, 0);
209 barry->Add(dname, 0, wxALL | wxEXPAND, 5);
211 wxSizer *password = new wxBoxSizer(wxHORIZONTAL);
212 password->Add(
213 new wxStaticText(this, wxID_ANY, _T("Password:")),
214 0, wxALIGN_RIGHT | wxALIGN_CENTRE_VERTICAL, 2);
215 password->Add(
216 m_password_edit = new wxTextCtrl(this, wxID_ANY, _T(""),
217 wxDefaultPosition, wxDefaultSize, wxTE_PASSWORD),
218 1, wxALIGN_LEFT, 0);
219 barry->Add(password, 0, wxALL | wxEXPAND, 5);
221 barry->Add(
222 m_debug_check = new wxCheckBox(this, wxID_ANY,
223 _T("Debug output during sync")),
224 0, wxALIGN_LEFT, 5);
226 sizer->Add(barry, 0, wxRIGHT | wxEXPAND, 5);
229 void GroupCfgDlg::AddAppSizer(wxSizer *sizer)
231 m_appsizer = new wxStaticBoxSizer(
232 new wxStaticBox(this, wxID_ANY, _T("Application")),
233 wxVERTICAL
236 UpdateAppSizer(false);
238 sizer->Add(m_appsizer, 0, wxLEFT | wxEXPAND, 5);
241 void GroupCfgDlg::UpdateAppSizer(bool relayout)
243 // start fresh
244 m_appsizer->Clear(true);
246 wxArrayString appnames;
247 LoadAppNames(appnames);
248 appnames.Sort();
250 // FIXME - make size of combobox the size of the longest
251 // string in apps? using textextent calcs
252 m_appsizer->Add(
253 m_app_combo = new wxComboBox(this,
254 Dialog_GroupCfg_AppCombo, _T(""),
255 wxDefaultPosition, wxSize(200, -1), appnames,
256 wxCB_READONLY),
257 0, wxALL | wxALIGN_CENTRE, 5);
258 m_appsizer->Add(
259 new wxButton(this, Dialog_GroupCfg_AppConfigButton,
260 _T("&Configure...")),
261 0, wxALL | wxALIGN_CENTRE, 5);
263 // in case this is called after the dialog is already displayed,
264 // we need to readjust everything
265 if( relayout )
266 m_topsizer->Layout();
269 void GroupCfgDlg::LoadAppNames(wxArrayString &appnames)
271 // need to load app names based on engine plugin availability
272 // NOTE - do not load the Barry plugin, since that's already assumed
274 if( !m_engine ) {
275 // no engine available
276 appnames.Add(_T("No engine selected"));
277 return;
280 string_list_type plugins;
281 try {
282 m_engine->GetPluginNames(plugins);
284 catch( std::exception &e ) {
285 barrylog("Exception caught in LoadAppNames: " << e.what());
286 return;
289 // cycle through all available plugins, and add the ones
290 // that we support
291 int added = 0;
292 string_list_type::const_iterator i = plugins.begin();
293 for( ; i != plugins.end(); ++i ) {
294 string appname;
295 if( m_engine->GetConverter().IsPluginSupported(*i, &appname) ) {
296 // found a supported plugin...
298 // skip Barry
299 if( appname == Config::Barry::AppName() )
300 continue;
302 appnames.Add( wxString(appname.c_str(), wxConvUTF8) );
303 added++;
307 m_app_count = added;
309 if( m_app_count == 0 ) {
310 appnames.Add(_T("No supported plugins available"));
311 return;
315 void GroupCfgDlg::AddSyncTypeSizer(wxSizer *sizer)
317 wxStaticBoxSizer *checks = new wxStaticBoxSizer(wxHORIZONTAL, this,
318 _T("Sync:"));
320 checks->Add( m_sync_contacts_check = new wxCheckBox(this,
321 Dialog_GroupCfg_ContactsCheck, _T("Contacts")),
322 0, wxRIGHT | wxEXPAND, 10);
323 checks->Add( m_sync_events_check = new wxCheckBox(this,
324 Dialog_GroupCfg_EventsCheck, _T("Events")),
325 0, wxRIGHT | wxEXPAND, 10);
326 checks->Add( m_sync_notes_check = new wxCheckBox(this,
327 Dialog_GroupCfg_NotesCheck, _T("Notes")),
328 0, wxRIGHT | wxEXPAND, 10);
329 checks->Add( m_sync_todos_check = new wxCheckBox(this,
330 Dialog_GroupCfg_TodosCheck, _T("To-dos")),
331 0, wxRIGHT | wxEXPAND, 10);
333 sizer->Add( checks,
334 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
337 void GroupCfgDlg::AddFavourSizer(wxSizer *sizer)
339 wxArrayString labels;
340 labels.Add( _T("Favour device") );
341 labels.Add( _T("Favour application") );
342 labels.Add( _T("Ask me") );
344 sizer->Add( m_favour_radios = new wxRadioBox(this, wxID_ANY,
345 _T("To Resolve Conflicts:"),
346 wxDefaultPosition, wxDefaultSize,
347 labels, 1, wxRA_SPECIFY_ROWS),
348 0, wxTOP | wxLEFT | wxRIGHT | wxEXPAND, 10);
351 void GroupCfgDlg::AddButtonSizer(wxSizer *sizer)
353 wxSizer *button = CreateSeparatedButtonSizer(wxOK | wxCANCEL);
354 sizer->Add(button, 0, wxALL | wxEXPAND | wxALIGN_RIGHT, 10);
357 void GroupCfgDlg::SelectCurrentEngine()
359 if( m_engine_combo ) {
360 if( !m_engine ) {
361 m_engine = m_apiset.os22() ?
362 m_apiset.os22() : m_apiset.os40();
365 if( m_engine )
366 m_engine_combo->SetValue(
367 wxString(m_engine->GetVersion(), wxConvUTF8));
369 UpdateAppSizer();
373 void GroupCfgDlg::LoadBarryConfig()
375 Config::Barry &bp = m_barry_plugin;
377 wxString dname(wxGetApp().GetDeviceName(bp.GetPin()).c_str(), wxConvUTF8);
379 m_name_edit->SetValue(dname);
380 m_password_edit->SetValue(wxString(bp.GetPassword().c_str(), wxConvUTF8));
381 m_debug_check->SetValue( bp.IsDebugMode() );
384 void GroupCfgDlg::SelectApplication(const std::string appname)
386 m_app_combo->SetValue(wxString(appname.c_str(), wxConvUTF8));
387 SelectSyncTypes();
390 void GroupCfgDlg::SelectSyncTypes()
392 if( !m_engine ) {
393 SetSyncTypeChecks(PST_NONE);
394 EnableSyncTypeChecks(PST_NONE);
395 return;
398 string app = GetCurrentAppName();
399 plugin_ptr ap = GetCurrentPlugin();
401 // calculate the supported sync types
402 // Note: we could also take the Barry plugin config into
403 // consideration here, but so far, we just use the
404 // opensync group config
405 pst_type supported = PST_NONE;
406 if( ap.get() ) {
407 supported = m_barry_plugin.GetSupportedSyncTypes(*m_engine)
408 & ap->GetSupportedSyncTypes(*m_engine);
411 // make sure our current selection is limited by our new
412 // set of supported plugins
413 m_sync_types[m_engine] &= supported;
415 // enable the checkboxes according to our ability
416 EnableSyncTypeChecks(supported);
418 // set the checkboxes according to our choices
419 SetSyncTypeChecks(m_sync_types[m_engine]);
422 void GroupCfgDlg::SelectFavour()
424 if( m_engine &&
425 m_favour_plugin_name == Config::Barry::PluginName(*m_engine) )
427 m_favour_radios->SetSelection(0);
429 else if( m_engine && m_favour_plugin_name.size() ) {
430 m_favour_radios->SetSelection(1);
432 else {
433 // ask the user
434 m_favour_radios->SetSelection(2);
438 void GroupCfgDlg::EnableSyncTypeChecks(pst_type types)
440 m_sync_contacts_check->Enable( types & PST_CONTACTS );
441 m_sync_events_check ->Enable( types & PST_EVENTS );
442 m_sync_notes_check ->Enable( types & PST_NOTES );
443 m_sync_todos_check ->Enable( types & PST_TODOS );
446 void GroupCfgDlg::SetSyncTypeChecks(pst_type types)
448 m_sync_contacts_check->SetValue( types & PST_CONTACTS );
449 m_sync_events_check ->SetValue( types & PST_EVENTS );
450 m_sync_notes_check ->SetValue( types & PST_NOTES );
451 m_sync_todos_check ->SetValue( types & PST_TODOS );
454 GroupCfgDlg::pst_type GroupCfgDlg::GetSyncTypeChecks()
456 pst_type types = PST_NONE;
457 if( m_sync_contacts_check->GetValue() ) types |= PST_CONTACTS;
458 if( m_sync_events_check ->GetValue() ) types |= PST_EVENTS;
459 if( m_sync_notes_check ->GetValue() ) types |= PST_NOTES;
460 if( m_sync_todos_check ->GetValue() ) types |= PST_TODOS;
461 return types;
464 std::string GroupCfgDlg::GetCurrentAppName() const
466 wxString app = m_app_combo->GetValue();
467 return std::string(app.utf8_str());
470 GroupCfgDlg::plugin_ptr GroupCfgDlg::GetCurrentPlugin()
472 string appname = GetCurrentAppName();
473 appcfg_map &cfgs = m_plugins[m_engine];
474 appcfg_map::iterator pi = cfgs.find(appname);
475 if( pi != cfgs.end() )
476 return pi->second;
477 else
478 return plugin_ptr(); // not found, return empty ptr
481 void GroupCfgDlg::OnConfigureApp(wxCommandEvent &event)
483 string app = GetCurrentAppName();
484 if( app.size() == 0 ) {
485 wxMessageBox(_T("Please select an application."),
486 _T("Application Config"), wxOK | wxICON_ERROR, this);
487 return;
490 ConfigUI::ptr ui = ConfigUI::CreateConfigUI(app);
492 if( !ui.get() ) {
493 wxMessageBox(_T("No configuration interface available for this Application."),
494 _T("Application Config"),
495 wxOK | wxICON_ERROR, this);
496 return;
499 if( ui->Configure(this, GetCurrentPlugin()) ) {
500 ConfigUI::plugin_ptr plugin = ui->GetPlugin();
501 if( plugin.get() ) {
502 m_plugins[m_engine][app] = plugin;
504 // if this is the first time, default to all
505 if( m_sync_types[m_engine] == PST_NONE )
506 m_sync_types[m_engine] = PST_ALL;
508 // update the types checkboxes
509 SelectSyncTypes();
514 void GroupCfgDlg::OnEngineComboChange(wxCommandEvent &event)
516 // remember what plugin we're using
517 plugin_ptr old_app = GetCurrentPlugin();
519 // update engine pointer
520 wxString newEngine = m_engine_combo->GetValue();
521 if( m_apiset.os22() && newEngine == wxString(m_apiset.os22()->GetVersion(), wxConvUTF8) ) {
522 m_engine = m_apiset.os22();
524 else if( m_apiset.os40() && newEngine == wxString(m_apiset.os40()->GetVersion(), wxConvUTF8) ) {
525 m_engine = m_apiset.os40();
528 // update the application list
529 UpdateAppSizer();
531 // if plugin can be configured in new engine, keep our current
532 // config, otherwise, reset
533 if( old_app.get() && m_engine->GetConverter().IsPluginSupported(old_app->GetPluginName(*m_engine)) ) {
534 // update the app list
535 SelectApplication(old_app->GetAppName());
537 else {
538 // leave GUI as is, and zap our plugin data
539 SelectApplication("");
543 void GroupCfgDlg::OnAppComboChange(wxCommandEvent &event)
545 SelectSyncTypes();
548 void GroupCfgDlg::OnSyncTypeCheck(wxCommandEvent &event)
550 if( !m_engine )
551 return;
553 m_sync_types[m_engine] = GetSyncTypeChecks();
556 bool GroupCfgDlg::TransferDataFromWindow()
558 // engine must be set!
559 if( !m_engine ) {
560 wxMessageBox(_T("Please select an engine."),
561 _T("Device Config"), wxOK | wxICON_ERROR, this);
562 return false;
565 // make sure the Barry plugin is configured
566 if( !m_barry_plugin.IsConfigured(*m_engine) ) {
567 wxMessageBox(_T("Barry doesn't have a PIN number. This should never happen."),
568 _T("Device Config"), wxOK | wxICON_ERROR, this);
569 return false;
572 // make sure the application plugin is configured
573 plugin_ptr app = GetCurrentPlugin();
574 if( !app.get() || !app->IsConfigured(*m_engine) ) {
575 // the app hasn't been configured yet, do it automatically
576 wxCommandEvent event;
577 OnConfigureApp(event);
579 app = GetCurrentPlugin();
580 if( !app.get() || !app->IsConfigured(*m_engine) ) {
581 wxMessageBox(_T("The application plugin is not fully configured."),
582 _T("Application Config"), wxOK | wxICON_ERROR, this);
583 return false;
587 // copy over barry specific settings
588 m_device_name = string(m_name_edit->GetValue().utf8_str());
589 m_barry_plugin.SetPassword(string(m_password_edit->GetValue().utf8_str()));
590 m_barry_plugin.DebugMode(m_debug_check->GetValue());
592 // make sure conflict resolution is known
593 int findex = m_favour_radios->GetSelection();
594 switch( findex )
596 case 0: // Favour device
597 m_favour_plugin_name = Config::Barry::PluginName(*m_engine);
598 break;
600 case 1: // Favour application
601 m_favour_plugin_name = app->GetPluginName(*m_engine);
602 break;
604 case 2: // Ask me
605 m_favour_plugin_name.clear();
606 break;
608 default: // borked
609 wxMessageBox(_T("Please select conflict resolution method."),
610 _T("Conflict Resolution"), wxOK | wxICON_ERROR, this);
611 return false;
614 // save the new device name
615 wxGetApp().SetDeviceName(m_barry_plugin.GetPin(), m_device_name);
617 // save the sync type checkboxes
618 m_sync_types[m_engine] = GetSyncTypeChecks();
620 return true;
623 int GroupCfgDlg::ShowModal()
625 int status = wxDialog::ShowModal();
626 plugin_ptr app = GetCurrentPlugin();
627 if( status == wxID_OK && app.get() ) {
628 // construct a new group from user's results
629 m_group.reset( new Config::Group(m_group_name) );
630 m_group->AddPlugin( m_barry_plugin.Clone() );
631 m_group->AddPlugin( app->Clone() );
633 // don't forget the extras
634 m_extras.reset( new DeviceExtras(m_barry_plugin.GetPin()) );
635 m_extras->m_favour_plugin_name = m_favour_plugin_name;
636 m_extras->m_sync_types = m_sync_types[m_engine];
638 else {
639 m_group.reset();
640 m_extras.reset();
642 return status;