debian: made the changelog more verbose as per intrigeri's feedback
[barry.git] / desktop / src / GroupCfgDlg.cc
blob38d48fc1fabbb7c3ae598221a23981e54d2ae6d7
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 // use m_device here, since BarryDesktopApp::GetDeviceName()
378 // may return an empty string if the device is not currently
379 // plugged in
380 wxString dname(m_device.GetDeviceName().c_str(), wxConvUTF8);
382 m_name_edit->SetValue(dname);
383 m_password_edit->SetValue(wxString(bp.GetPassword().c_str(), wxConvUTF8));
384 m_debug_check->SetValue( bp.IsDebugMode() );
387 void GroupCfgDlg::SelectApplication(const std::string appname)
389 m_app_combo->SetValue(wxString(appname.c_str(), wxConvUTF8));
390 SelectSyncTypes();
393 void GroupCfgDlg::SelectSyncTypes()
395 if( !m_engine ) {
396 SetSyncTypeChecks(PST_NONE);
397 EnableSyncTypeChecks(PST_NONE);
398 return;
401 string app = GetCurrentAppName();
402 plugin_ptr ap = GetCurrentPlugin();
404 // calculate the supported sync types
405 // Note: we could also take the Barry plugin config into
406 // consideration here, but so far, we just use the
407 // opensync group config
408 pst_type supported = PST_NONE;
409 if( ap.get() ) {
410 supported = m_barry_plugin.GetSupportedSyncTypes(*m_engine)
411 & ap->GetSupportedSyncTypes(*m_engine);
414 // make sure our current selection is limited by our new
415 // set of supported plugins
416 m_sync_types[m_engine] &= supported;
418 // enable the checkboxes according to our ability
419 EnableSyncTypeChecks(supported);
421 // set the checkboxes according to our choices
422 SetSyncTypeChecks(m_sync_types[m_engine]);
425 void GroupCfgDlg::SelectFavour()
427 if( m_engine &&
428 m_favour_plugin_name == Config::Barry::PluginName(*m_engine) )
430 m_favour_radios->SetSelection(0);
432 else if( m_engine && m_favour_plugin_name.size() ) {
433 m_favour_radios->SetSelection(1);
435 else {
436 // ask the user
437 m_favour_radios->SetSelection(2);
441 void GroupCfgDlg::EnableSyncTypeChecks(pst_type types)
443 m_sync_contacts_check->Enable( types & PST_CONTACTS );
444 m_sync_events_check ->Enable( types & PST_EVENTS );
445 m_sync_notes_check ->Enable( types & PST_NOTES );
446 m_sync_todos_check ->Enable( types & PST_TODOS );
449 void GroupCfgDlg::SetSyncTypeChecks(pst_type types)
451 m_sync_contacts_check->SetValue( types & PST_CONTACTS );
452 m_sync_events_check ->SetValue( types & PST_EVENTS );
453 m_sync_notes_check ->SetValue( types & PST_NOTES );
454 m_sync_todos_check ->SetValue( types & PST_TODOS );
457 GroupCfgDlg::pst_type GroupCfgDlg::GetSyncTypeChecks()
459 pst_type types = PST_NONE;
460 if( m_sync_contacts_check->GetValue() ) types |= PST_CONTACTS;
461 if( m_sync_events_check ->GetValue() ) types |= PST_EVENTS;
462 if( m_sync_notes_check ->GetValue() ) types |= PST_NOTES;
463 if( m_sync_todos_check ->GetValue() ) types |= PST_TODOS;
464 return types;
467 std::string GroupCfgDlg::GetCurrentAppName() const
469 wxString app = m_app_combo->GetValue();
470 return std::string(app.utf8_str());
473 GroupCfgDlg::plugin_ptr GroupCfgDlg::GetCurrentPlugin()
475 string appname = GetCurrentAppName();
476 appcfg_map &cfgs = m_plugins[m_engine];
477 appcfg_map::iterator pi = cfgs.find(appname);
478 if( pi != cfgs.end() )
479 return pi->second;
480 else
481 return plugin_ptr(); // not found, return empty ptr
484 void GroupCfgDlg::OnConfigureApp(wxCommandEvent &event)
486 string app = GetCurrentAppName();
487 if( app.size() == 0 ) {
488 wxMessageBox(_T("Please select an application."),
489 _T("Application Config"), wxOK | wxICON_ERROR, this);
490 return;
493 ConfigUI::ptr ui = ConfigUI::CreateConfigUI(app);
495 if( !ui.get() ) {
496 wxMessageBox(_T("No configuration interface available for this Application."),
497 _T("Application Config"),
498 wxOK | wxICON_ERROR, this);
499 return;
502 if( ui->Configure(this, GetCurrentPlugin()) ) {
503 ConfigUI::plugin_ptr plugin = ui->GetPlugin();
504 if( plugin.get() ) {
505 m_plugins[m_engine][app] = plugin;
507 // if this is the first time, default to all
508 if( m_sync_types[m_engine] == PST_NONE )
509 m_sync_types[m_engine] = PST_ALL;
511 // update the types checkboxes
512 SelectSyncTypes();
517 void GroupCfgDlg::OnEngineComboChange(wxCommandEvent &event)
519 // remember what plugin we're using
520 plugin_ptr old_app = GetCurrentPlugin();
522 // update engine pointer
523 wxString newEngine = m_engine_combo->GetValue();
524 if( m_apiset.os22() && newEngine == wxString(m_apiset.os22()->GetVersion(), wxConvUTF8) ) {
525 m_engine = m_apiset.os22();
527 else if( m_apiset.os40() && newEngine == wxString(m_apiset.os40()->GetVersion(), wxConvUTF8) ) {
528 m_engine = m_apiset.os40();
531 // update the application list
532 UpdateAppSizer();
534 // if plugin can be configured in new engine, keep our current
535 // config, otherwise, reset
536 if( old_app.get() && m_engine->GetConverter().IsPluginSupported(old_app->GetPluginName(*m_engine)) ) {
537 // update the app list
538 SelectApplication(old_app->GetAppName());
540 else {
541 // leave GUI as is, and zap our plugin data
542 SelectApplication("");
546 void GroupCfgDlg::OnAppComboChange(wxCommandEvent &event)
548 SelectSyncTypes();
551 void GroupCfgDlg::OnSyncTypeCheck(wxCommandEvent &event)
553 if( !m_engine )
554 return;
556 m_sync_types[m_engine] = GetSyncTypeChecks();
559 bool GroupCfgDlg::TransferDataFromWindow()
561 // engine must be set!
562 if( !m_engine ) {
563 wxMessageBox(_T("Please select an engine."),
564 _T("Device Config"), wxOK | wxICON_ERROR, this);
565 return false;
568 // make sure the Barry plugin is configured
569 if( !m_barry_plugin.IsConfigured(*m_engine) ) {
570 wxMessageBox(_T("Barry doesn't have a PIN number. This should never happen."),
571 _T("Device Config"), wxOK | wxICON_ERROR, this);
572 return false;
575 // make sure the application plugin is configured
576 plugin_ptr app = GetCurrentPlugin();
577 if( !app.get() || !app->IsConfigured(*m_engine) ) {
578 // the app hasn't been configured yet, do it automatically
579 wxCommandEvent event;
580 OnConfigureApp(event);
582 app = GetCurrentPlugin();
583 if( !app.get() || !app->IsConfigured(*m_engine) ) {
584 wxMessageBox(_T("The application plugin is not fully configured."),
585 _T("Application Config"), wxOK | wxICON_ERROR, this);
586 return false;
590 // copy over barry specific settings
591 m_device_name = string(m_name_edit->GetValue().utf8_str());
592 m_barry_plugin.SetPassword(string(m_password_edit->GetValue().utf8_str()));
593 m_barry_plugin.DebugMode(m_debug_check->GetValue());
595 // make sure conflict resolution is known
596 int findex = m_favour_radios->GetSelection();
597 switch( findex )
599 case 0: // Favour device
600 m_favour_plugin_name = Config::Barry::PluginName(*m_engine);
601 break;
603 case 1: // Favour application
604 m_favour_plugin_name = app->GetPluginName(*m_engine);
605 break;
607 case 2: // Ask me
608 m_favour_plugin_name.clear();
609 break;
611 default: // borked
612 wxMessageBox(_T("Please select conflict resolution method."),
613 _T("Conflict Resolution"), wxOK | wxICON_ERROR, this);
614 return false;
617 // save the new device name
618 wxGetApp().SetDeviceName(m_barry_plugin.GetPin(), m_device_name);
620 // save the sync type checkboxes
621 m_sync_types[m_engine] = GetSyncTypeChecks();
623 return true;
626 int GroupCfgDlg::ShowModal()
628 int status = wxDialog::ShowModal();
629 plugin_ptr app = GetCurrentPlugin();
630 if( status == wxID_OK && app.get() ) {
631 // construct a new group from user's results
632 m_group.reset( new Config::Group(m_group_name) );
633 m_group->AddPlugin( m_barry_plugin.Clone() );
634 m_group->AddPlugin( app->Clone() );
636 // don't forget the extras
637 m_extras.reset( new DeviceExtras(m_barry_plugin.GetPin()) );
638 m_extras->m_favour_plugin_name = m_favour_plugin_name;
639 m_extras->m_sync_types = m_sync_types[m_engine];
641 else {
642 m_group.reset();
643 m_extras.reset();
645 return status;