desktop: added timezone list to CalendarEditDlg
[barry.git] / desktop / src / Mode_Browse.cc
blobbd1c1d0869a7fc6ba3312cefbd3ef47555ed26e5
1 ///
2 /// \file Mode_Browse.cc
3 /// Mode derived class for database browsing
4 ///
6 /*
7 Copyright (C) 2011-2012, 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_Browse.h"
23 #include "BaseFrame.h"
24 #include "ContactEditDlg.h"
25 #include "CalendarEditDlg.h"
26 #include "MemoEditDlg.h"
27 #include "windowids.h"
28 #include <iostream>
29 #include <sstream>
30 #include <iomanip>
32 using namespace std;
33 using namespace Barry;
35 DEFINE_EVENT_TYPE(BMET_LOAD_STATUS)
37 BEGIN_EVENT_TABLE(BrowseMode, wxEvtHandler)
38 EVT_LIST_ITEM_SELECTED(BrowseMode_DBDBList,
39 BrowseMode::OnDBDBListSelChange)
40 EVT_LIST_ITEM_SELECTED(BrowseMode_RecordList,
41 BrowseMode::OnRecordListSelChange)
42 EVT_LIST_ITEM_ACTIVATED(BrowseMode_RecordList,
43 BrowseMode::OnRecordListActivated)
44 EVT_CHECKBOX (BrowseMode_ShowAllCheckbox,
45 BrowseMode::OnShowAll)
46 EVT_BUTTON (BrowseMode_AddRecordButton,
47 BrowseMode::OnAddRecord)
48 EVT_BUTTON (BrowseMode_CopyRecordButton,
49 BrowseMode::OnCopyRecord)
50 EVT_BUTTON (BrowseMode_EditRecordButton,
51 BrowseMode::OnEditRecord)
52 EVT_BUTTON (BrowseMode_DeleteRecordButton,
53 BrowseMode::OnDeleteRecord)
54 EVT_COMMAND (wxID_ANY, BMET_LOAD_STATUS,
55 BrowseMode::OnStatusEvent)
56 END_EVENT_TABLE()
59 //////////////////////////////////////////////////////////////////////////////
60 // Standalone functions
62 bool IsEditable(const std::string &dbname)
64 // add entry here for each edit dialog available
65 return dbname == Contact::GetDBName() ||
66 dbname == Calendar::GetDBName() ||
67 dbname == Memo::GetDBName();
70 bool EditRecord(wxWindow *parent,
71 bool editable,
72 const Barry::TimeZones &zones,
73 Barry::Contact &rec)
75 ContactEditDlg edit(parent, rec, editable);
76 return edit.ShowModal() == wxID_OK;
79 bool EditRecord(wxWindow *parent,
80 bool editable,
81 const Barry::TimeZones &zones,
82 Barry::Bookmark &rec)
84 return false;
87 bool EditRecord(wxWindow *parent,
88 bool editable,
89 const Barry::TimeZones &zones,
90 Barry::Calendar &rec)
92 CalendarEditDlg edit(parent, rec, editable, &zones);
93 return edit.ShowModal() == wxID_OK;
96 bool EditRecord(wxWindow *parent,
97 bool editable,
98 const Barry::TimeZones &zones,
99 Barry::CalendarAll &rec)
101 return false;
104 bool EditRecord(wxWindow *parent,
105 bool editable,
106 const Barry::TimeZones &zones,
107 Barry::ContentStore &rec)
109 return false;
112 bool EditRecord(wxWindow *parent,
113 bool editable,
114 const Barry::TimeZones &zones,
115 Barry::Folder &rec)
117 return false;
120 bool EditRecord(wxWindow *parent,
121 bool editable,
122 const Barry::TimeZones &zones,
123 Barry::Memo &rec)
125 MemoEditDlg edit(parent, rec, editable);
126 return edit.ShowModal() == wxID_OK;
129 bool EditRecord(wxWindow *parent,
130 bool editable,
131 const Barry::TimeZones &zones,
132 Barry::Message &rec)
134 return false;
137 bool EditRecord(wxWindow *parent,
138 bool editable,
139 const Barry::TimeZones &zones,
140 Barry::CallLog &rec)
142 return false;
145 bool EditRecord(wxWindow *parent,
146 bool editable,
147 const Barry::TimeZones &zones,
148 Barry::PINMessage &rec)
150 return false;
153 bool EditRecord(wxWindow *parent,
154 bool editable,
155 const Barry::TimeZones &zones,
156 Barry::SavedMessage &rec)
158 return false;
161 bool EditRecord(wxWindow *parent,
162 bool editable,
163 const Barry::TimeZones &zones,
164 Barry::ServiceBook &rec)
166 return false;
169 bool EditRecord(wxWindow *parent,
170 bool editable,
171 const Barry::TimeZones &zones,
172 Barry::Sms &rec)
174 return false;
177 bool EditRecord(wxWindow *parent,
178 bool editable,
179 const Barry::TimeZones &zones,
180 Barry::Task &rec)
182 return false;
185 bool EditRecord(wxWindow *parent,
186 bool editable,
187 const Barry::TimeZones &zones,
188 Barry::TimeZone &rec)
190 return false;
193 bool EditRecord(wxWindow *parent,
194 bool editable,
195 const Barry::TimeZones &zones,
196 Barry::HandheldAgent &rec)
198 return false;
202 //////////////////////////////////////////////////////////////////////////////
203 // GUIDesktopConnector
205 bool GUIDesktopConnector::PasswordPrompt(const Barry::BadPassword &bp,
206 std::string &password_result)
208 // create prompt based on exception data
209 ostringstream oss;
210 oss << "Please enter device password: ("
211 << bp.remaining_tries()
212 << " tries remaining)";
213 wxString prompt(oss.str().c_str(), wxConvUTF8);
215 // ask user for device password
216 wxString pass = wxGetPasswordFromUser(prompt,
217 _T("Device Password"), _T(""), m_parent);
219 password_result = pass.utf8_str();
221 // assume that a blank password means the user wishes to quit...
222 // wxWidgets doesn't seem to handle this very well?
223 return password_result.size() > 0;
226 //////////////////////////////////////////////////////////////////////////////
227 // DBDataCache
229 DBDataCache::DBDataCache(DataCache::IndexType index, const Barry::DBData &raw)
230 : DataCache(index, raw.GetUniqueId())
231 , m_raw(raw)
235 bool DBDataCache::Edit(wxWindow *parent,
236 bool editable,
237 const Barry::TimeZones &zones)
239 return false;
242 std::string DBDataCache::GetDescription() const
244 return "raw data";
248 //////////////////////////////////////////////////////////////////////////////
249 // DBCache
251 DBCache::DBCache(ThreadableDesktop &tdesktop, const std::string &dbname)
252 : m_tdesktop(tdesktop)
253 , m_dbname(dbname)
255 // lock the desktop
256 DesktopInstancePtr dip = m_tdesktop.Get();
258 // grab the DBID
259 m_dbid = dip->Desktop().GetDBID(m_dbname);
261 // load the record state table
262 dip->Desktop().GetRecordStateTable(m_dbid, m_state);
264 // load all records
265 AllRecordParser ap(*this, *this);
266 RecordStateTable::StateMapType::iterator i = m_state.StateMap.begin();
267 for( ; i != m_state.StateMap.end(); ++i ) {
268 m_index = i->second.Index; // save for the callback
269 dip->Desktop().GetRecord(m_dbid, m_index, ap);
272 // sort the list of records by description
273 m_records.sort();
276 DBCache::~DBCache()
280 DBCache::iterator DBCache::Get(int list_offset)
282 iterator i = begin();
283 for( ; i != end() && list_offset; ++i, list_offset-- )
285 return i;
288 DBCache::const_iterator DBCache::Get(int list_offset) const
290 const_iterator i = begin();
291 for( ; i != end() && list_offset; ++i, list_offset-- )
293 return i;
296 int DBCache::GetIndex(iterator record) const
298 iterator i = const_cast<DBCache*> (this)->begin();
299 iterator e = const_cast<DBCache*> (this)->end();
300 for( int index = 0; i != e; ++i, index++ ) {
301 if( i == record )
302 return index;
304 return -1;
307 int DBCache::GetIndex(const_iterator record) const
309 const_iterator i = begin();
310 for( int index = 0; i != end(); ++i, index++ ) {
311 if( i == record )
312 return index;
314 return -1;
317 DBCache::iterator DBCache::Add(wxWindow *parent,
318 const Barry::TimeZones &zones,
319 iterator copy_record)
321 DataCachePtr p;
323 #undef HANDLE_BUILDER
324 #define HANDLE_BUILDER(tname) \
325 if( m_dbname == Barry::tname::GetDBName() ) { \
326 Barry::tname rec; \
327 if( copy_record != end() ) { \
328 RecordCache<Barry::tname> *rc = dynamic_cast<RecordCache<Barry::tname>* > (copy_record->get()); \
329 if( rc ) { \
330 rec = rc->GetRecord(); \
333 p.reset( new RecordCache<Barry::tname>(0, rec) ); \
335 ALL_KNOWN_BUILDER_TYPES
337 // anything else is not addable or buildable
338 if( !p.get() ) {
339 return end();
342 if( p->Edit(parent, true, zones) ) {
343 // see if this record has a builder
344 Barry::Builder *bp = dynamic_cast<Barry::Builder*> (p.get());
345 if( !bp ) {
346 return end();
349 // give record a new UniqueID
350 uint32_t record_id = m_state.MakeNewRecordId();
351 cout << "New recordID generated: 0x" << hex << record_id << endl;
352 p->SetIds(p->GetStateIndex(), record_id);
354 // add record to device
355 DesktopInstancePtr dip = m_tdesktop.Get();
356 Barry::Mode::Desktop &desktop = dip->Desktop();
357 desktop.AddRecord(m_dbid, *bp);
359 // update our copy of the record state table from device
360 desktop.GetRecordStateTable(m_dbid, m_state);
361 cout << m_state << endl;
363 // find our new record_id in list, to find the state index
364 IndexType new_index;
365 if( !m_state.GetIndex(record_id, &new_index) ) {
366 throw std::logic_error("Need to reconnect for adding a record?");
369 // update new state_index in the data cache record
370 p->SetIds(new_index, record_id);
372 // add DataCachePtr to our own cache list
373 m_records.push_front(p);
375 // return iterator pointing to new record
376 return begin();
378 else {
379 return end();
383 bool DBCache::Edit(wxWindow *parent,
384 const Barry::TimeZones &zones,
385 iterator record)
387 if( record == end() )
388 return false;
390 if( (*record)->Edit(parent, true, zones) && (*record)->IsBuildable() ) {
391 // see if this record has a builder
392 Barry::Builder *bp = dynamic_cast<Barry::Builder*> ((*record).get());
393 if( !bp )
394 return false;
396 cout << "Changing device record with index: 0x" << hex << (*record)->GetStateIndex() << endl;
397 cout << m_state << endl;
398 // update the device with new record data
399 DesktopInstancePtr dip = m_tdesktop.Get();
400 Barry::Mode::Desktop &desktop = dip->Desktop();
401 desktop.SetRecord(m_dbid, (*record)->GetStateIndex(), *bp);
403 return true;
405 else {
406 return false;
410 bool DBCache::Delete(wxWindow *parent, iterator record)
412 if( record == end() )
413 return false;
415 // prompt user with Yes / No message
416 wxString desc((*record)->GetDescription().c_str(), wxConvUTF8);
417 int choice = wxMessageBox(_T("Delete record: ") + desc + _T("?"),
418 _T("Record Delete"), wxYES_NO | wxICON_QUESTION, parent);
420 // if no, return false
421 if( choice != wxYES )
422 return false;
424 cout << "Deleting device record with index: 0x" << hex << (*record)->GetStateIndex() << endl;
425 cout << m_state << endl;
426 // delete record from device
427 DesktopInstancePtr dip = m_tdesktop.Get();
428 Barry::Mode::Desktop &desktop = dip->Desktop();
429 desktop.DeleteRecord(m_dbid, (*record)->GetStateIndex());
431 // remove record from cache list
432 m_records.erase(record);
433 return true;
436 // For Barry::AllRecordStore
437 #undef HANDLE_PARSER
438 #define HANDLE_PARSER(tname) \
439 void DBCache::operator() (const Barry::tname &rec) \
441 DataCachePtr p( new RecordCache<Barry::tname>(m_index, rec) ); \
442 m_records.push_front(p); \
444 ALL_KNOWN_PARSER_TYPES
446 // For Barry::Parser
447 void DBCache::ParseRecord(const Barry::DBData &data,
448 const Barry::IConverter *ic)
450 DataCachePtr p( new DBDataCache(m_index, data) );
451 m_records.push_front(p);
455 //////////////////////////////////////////////////////////////////////////////
456 // DBMap
458 DBMap::DBMap(ThreadableDesktop &tdesktop)
459 : m_tdesktop(tdesktop)
461 if( pthread_mutex_init(&m_map_mutex, NULL) ) {
462 throw Barry::Error("Failed to create map mutex");
465 if( pthread_mutex_init(&m_load_mutex, NULL) ) {
466 throw Barry::Error("Failed to create load mutex");
470 DBMap::DBCachePtr DBMap::LoadDBCache(const std::string &dbname)
472 // first, check for pre-loaded data, before the load lock,
473 // to make sure we return pre-loaded data with utmost haste
475 scoped_lock map_lock(m_map_mutex);
477 MapType::iterator i = m_map.find(dbname);
478 if( i != m_map.end() )
479 return i->second;
482 // if not found, lock and load, but be careful, since we
483 // don't want to open a window here for loading a db twice
484 scoped_lock load_lock(m_load_mutex);
486 // check again for pre-loaded data, since between
487 // map.unlock and load.lock there could have been
488 // another successful load
490 scoped_lock map_lock(m_map_mutex);
492 MapType::iterator i = m_map.find(dbname);
493 if( i != m_map.end() )
494 return i->second;
497 // do the load, without map.lock, since this can take a
498 // significant amount of time
499 DBCachePtr p( new DBCache(m_tdesktop, dbname) );
501 // lock once more to update the map, and then done
502 scoped_lock map_lock(m_map_mutex);
503 m_map[dbname] = p;
504 return p;
507 DBMap::DBCachePtr DBMap::GetDBCache(const std::string &dbname)
509 scoped_lock lock(m_map_mutex);
511 MapType::iterator i = m_map.find(dbname);
512 if( i != m_map.end() )
513 return i->second;
515 return DBCachePtr();
518 //////////////////////////////////////////////////////////////////////////////
519 // BrowseMode
521 BrowseMode::BrowseMode(wxWindow *parent, const ProbeResult &device)
522 : m_parent(parent)
523 , m_buildable(false)
524 , m_editable(false)
525 , m_show_all(false)
527 // create device identifying string
528 m_device_id_str = wxString(device.GetDisplayName().c_str(), wxConvUTF8);
531 // connect to the device
533 m_con.reset( new GUIDesktopConnector(m_parent, "", "utf-8", device) );
534 m_con->Reconnect(2);
535 m_tdesktop.reset( new ThreadableDesktop(*m_con) );
537 // keep our own copy, and sort by name for later
538 m_dbdb = m_con->GetDesktop().GetDBDB();
539 m_dbdb.SortByName();
541 // store a copy of the time zone set for record editing
542 if( TimeZones::IsLoadable(m_con->GetDesktop()) ) {
543 // load time zones from device itself
544 m_zones.reset( new TimeZones(m_con->GetDesktop()) );
546 else {
547 // use static time zone table from Barry library
548 m_zones.reset( new TimeZones );
551 CreateControls();
553 // create our DBMap and give it the threadable desktop,
554 // now that we're finished doing any desktop USB work
555 m_dbmap.reset( new DBMap(*m_tdesktop) );
558 // From here down, we assume that our constructor succeeds, with
559 // no exceptions!
562 // fire off a background thread to cache database records
563 // in advance... if it fails, don't worry about it
564 m_abort_flag = false;
565 int ret = pthread_create(&m_cache_thread, NULL,
566 &BrowseMode::FillCacheThread, this);
567 if( ret != 0 )
568 m_abort_flag = true; // no need to join later
570 // connect ourselves to the parent's event handling chain
571 // do this last, so that we are guaranteed our destructor
572 // will run, in case of exceptions
573 m_parent->PushEventHandler(this);
576 BrowseMode::~BrowseMode()
578 // unhook that event handler!
579 m_parent->PopEventHandler();
581 // make sure the cache thread is finished before we destroy it :-)
582 if( !m_abort_flag ) {
583 m_abort_flag = true;
584 void *junk;
585 pthread_join(m_cache_thread, &junk);
589 std::string& GetDBName(Barry::DatabaseDatabase::Database &db)
591 return db.Name;
594 void BrowseMode::SendStatusEvent(const std::string &dbname)
596 wxCommandEvent event(BMET_LOAD_STATUS, wxID_ANY);
597 event.SetEventObject(this);
599 if( dbname.size() ) {
600 wxString msg(_T("Loading: "));
601 msg += wxString(dbname.c_str(), wxConvUTF8);
602 event.SetString(msg);
604 else {
605 event.SetString(_T(""));
608 AddPendingEvent(event);
611 void BrowseMode::CreateControls()
613 m_top_sizer.reset( new wxBoxSizer(wxVERTICAL) );
615 // make space for the main header, which is not part of our
616 // work area
617 m_top_sizer->AddSpacer(MAIN_HEADER_OFFSET);
621 // add list boxes to main area, the list_sizer
624 wxStaticBoxSizer *list_sizer = new wxStaticBoxSizer(wxHORIZONTAL,
625 m_parent, m_device_id_str);
627 // add database listctrl
628 m_dbdb_list.reset (new wxListCtrl(m_parent, BrowseMode_DBDBList,
629 wxDefaultPosition, wxDefaultSize,
630 wxLC_REPORT | wxLC_SINGLE_SEL) ); //| wxLC_VRULES
631 // int max_db_width = GetMaxWidth(m_dbdb_list.get(),
632 // m_dbdb.Databases.begin(), m_dbdb.Databases.end(),
633 // &GetDBName);
634 list_sizer->Add( m_dbdb_list.get(), 4, wxEXPAND | wxALL, 4 );
636 // add the record listctrl
637 m_record_list.reset(new wxListCtrl(m_parent, BrowseMode_RecordList,
638 wxDefaultPosition, wxDefaultSize,
639 wxLC_REPORT | wxLC_SINGLE_SEL) ); //| wxLC_VRULES
640 list_sizer->Add( m_record_list.get(), 5, wxEXPAND | wxALL, 4 );
642 // add list sizer to top sizer
643 m_top_sizer->Add( list_sizer, 1, wxEXPAND | wxALL, 4 );
646 // add "show all" checkbox and load status static textbox, inside sizer
649 wxBoxSizer *status_sizer = new wxBoxSizer(wxHORIZONTAL);
651 m_show_all_checkbox.reset( new wxCheckBox(m_parent,
652 BrowseMode_ShowAllCheckbox,
653 _T("Show All Databases"),
654 wxDefaultPosition, wxDefaultSize,
655 wxCHK_2STATE) );
656 status_sizer->Add( m_show_all_checkbox.get(), 0, wxEXPAND, 0 );
657 m_show_all_checkbox->SetValue(m_show_all);
659 status_sizer->AddStretchSpacer();
661 m_load_status_text.reset( new wxStaticText(m_parent,
662 BrowseMode_LoadStatusText,
663 _T(""),
664 wxDefaultPosition, wxSize(200, -1),
665 wxST_NO_AUTORESIZE) );
666 status_sizer->Add( m_load_status_text.get(), 0,
667 wxEXPAND | wxALIGN_CENTRE_VERTICAL, 0 );
669 m_top_sizer->Add( status_sizer, 0, wxEXPAND | wxALL, 4 );
674 // bottom buttons
677 // add bottom buttons - these go in the bottom FOOTER area
678 // so their heights must be fixed to MAIN_HEADER_OFFSET
679 // minus a border of 5px top and bottom
680 wxSize footer(-1, MAIN_HEADER_OFFSET - 5 - 5);
681 wxBoxSizer *buttons = new wxBoxSizer(wxHORIZONTAL);
682 m_add_record_button.reset( new wxButton(m_parent,
683 BrowseMode_AddRecordButton, _T("Add..."),
684 wxDefaultPosition, footer) );
685 m_copy_record_button.reset( new wxButton(m_parent,
686 BrowseMode_CopyRecordButton, _T("Copy..."),
687 wxDefaultPosition, footer) );
688 m_edit_record_button.reset( new wxButton(m_parent,
689 BrowseMode_EditRecordButton, _T("Edit..."),
690 wxDefaultPosition, footer));
691 m_delete_record_button.reset( new wxButton(m_parent,
692 BrowseMode_DeleteRecordButton, _T("Delete..."),
693 wxDefaultPosition, footer) );
694 buttons->Add(m_add_record_button.get(), 0, wxRIGHT, 5);
695 buttons->Add(m_copy_record_button.get(), 0, wxRIGHT, 5);
696 buttons->Add(m_edit_record_button.get(), 0, wxRIGHT, 5);
697 buttons->Add(m_delete_record_button.get(), 0, wxRIGHT, 5);
698 m_top_sizer->Add(buttons, 0, wxALL | wxALIGN_RIGHT, 5);
701 // recalc size of children and add columns
703 wxSize client_size = m_parent->GetClientSize();
704 m_top_sizer->SetDimension(0, 0,
705 client_size.GetWidth(), client_size.GetHeight());
707 // m_dbdb_list
708 wxSize dbdb_size = m_dbdb_list->GetClientSize();
709 int scroll_width = wxSystemSettings::GetMetric(wxSYS_VSCROLL_X);
710 int size = dbdb_size.GetWidth() - scroll_width;
711 m_dbdb_list->InsertColumn(0, _T("Databases"), wxLIST_FORMAT_LEFT,
712 size * 0.80);
713 m_dbdb_list->InsertColumn(1, _T("Count"), wxLIST_FORMAT_LEFT,
714 size * 0.20 + scroll_width); // add back the scroll width
715 // so it doesn't look half-baked when
716 // there is no scroll bar
719 // m_record_list
720 wxSize record_size = m_record_list->GetClientSize();
721 m_record_list->InsertColumn(0, _T("Record Description"),
722 wxLIST_FORMAT_LEFT, record_size.GetWidth());
726 // add data
728 FillDBDBList();
731 // attempt to re-select the devices as we last saw them
732 ReselectDevices(m_device_set->String2Subset(wxGetApp().GetGlobalConfig().GetKey("SelectedDevices")));
733 UpdateButtons();
738 int BrowseMode::GUItoDBDBIndex(int gui_index)
740 if( m_show_all )
741 return gui_index;
743 DatabaseDatabase::DatabaseArrayType::const_iterator
744 i = m_dbdb.Databases.begin(), e = m_dbdb.Databases.end();
745 for( int index = 0; i != e; ++i, index++ ) {
746 // only bump index on the parsable databases
747 if( !m_show_all && !IsParsable(i->Name) )
748 continue;
750 if( !gui_index )
751 return index;
753 gui_index--;
756 // error
757 return -1;
760 void BrowseMode::FillDBDBList()
762 // start fresh
763 m_dbdb_list->DeleteAllItems();
765 DatabaseDatabase::DatabaseArrayType::const_iterator
766 i = m_dbdb.Databases.begin(), e = m_dbdb.Databases.end();
767 for( int index = 0; i != e; ++i, index++ ) {
768 // Only show parsable databases, depending on GUI
769 if( !m_show_all && !IsParsable(i->Name) )
770 continue;
772 // Database Name
773 wxString text(i->Name.c_str(), wxConvUTF8);
774 long item = m_dbdb_list->InsertItem(index, text);
776 // Record Count
777 ostringstream oss;
778 oss << dec << i->RecordCount;
779 text = wxString(oss.str().c_str(), wxConvUTF8);
780 m_dbdb_list->SetItem(item, 1, text);
783 UpdateButtons();
786 void BrowseMode::FillRecordList(const std::string &dbname)
788 try {
790 // start fresh
791 m_record_list->DeleteAllItems();
793 // grab our DB
794 DBMap::DBCachePtr db = m_dbmap->LoadDBCache(dbname);
796 // cycle through the cache, and insert the descriptions
797 // given for each record
798 DBCache::const_iterator b = db->begin(), e = db->end();
799 for( int index = 0; b != e; ++b, index++ ) {
800 wxString text((*b)->GetDescription().c_str(), wxConvUTF8);
801 //long item =
802 m_record_list->InsertItem(index, text);
805 } catch( Barry::Error &be ) {
806 cerr << be.what() << endl;
810 void BrowseMode::UpdateButtons()
812 int selected_count = m_record_list->GetSelectedItemCount();
814 // can only add if we have a builder and dialog for this record type
815 m_add_record_button->Enable(m_buildable && m_editable);
817 // can only copy or edit if we have a builder, a dialog, and
818 // only 1 is selected
819 m_copy_record_button->Enable(
820 m_buildable && m_editable && selected_count == 1);
821 m_edit_record_button->Enable(
822 m_buildable && m_editable && selected_count == 1);
824 // can only delete if something is selected
825 m_delete_record_button->Enable(selected_count > 0);
828 void BrowseMode::FillCache()
830 // cycle through the dbdb and load all Parsable databases
831 DatabaseDatabase::DatabaseArrayType::const_iterator
832 i = m_dbdb.Databases.begin(), e = m_dbdb.Databases.end();
833 for( ; i != e; ++i ) {
834 if( IsParsable(i->Name) ) try {
835 SendStatusEvent(i->Name);
836 m_dbmap->LoadDBCache(i->Name);
837 } catch( Barry::Error &be ) {
838 cerr << be.what() << endl;
841 if( m_abort_flag )
842 break;
845 SendStatusEvent("");
847 // finished
848 m_abort_flag = true;
851 void* BrowseMode::FillCacheThread(void *bobj)
853 BrowseMode *bm = (BrowseMode*) bobj;
854 bm->FillCache();
855 return NULL;
858 void BrowseMode::OnDBDBListSelChange(wxListEvent &event)
860 wxBusyCursor wait;
861 int index = GUItoDBDBIndex(event.GetIndex());
862 m_current_dbname = m_dbdb.Databases.at(index).Name;
863 m_buildable = ::IsBuildable(m_current_dbname);
864 m_editable = ::IsEditable(m_current_dbname);
865 m_current_record_item = -1;
867 FillRecordList(m_current_dbname);
868 UpdateButtons();
871 void BrowseMode::OnRecordListSelChange(wxListEvent &event)
873 // grab the cache for the current database... Get is ok here,
874 // since the cache is already loaded by the main db list
875 DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname);
876 if( !p.get() )
877 return;
879 // grab the record list index
880 m_current_record_item = event.GetIndex();
881 // m_current_record_item = m_record_list->GetNextItem(
882 // m_current_record_item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
884 UpdateButtons();
887 void BrowseMode::OnRecordListActivated(wxListEvent &event)
889 wxCommandEvent ce;
890 OnEditRecord(ce);
893 void BrowseMode::OnShowAll(wxCommandEvent &event)
895 m_show_all = !m_show_all;
896 FillDBDBList();
899 void BrowseMode::OnAddRecord(wxCommandEvent &event)
901 // grab the cache for the current database... Get is ok here,
902 // since the cache is already loaded by the main db list
903 DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname);
904 if( !p.get() )
905 return;
907 DBCache::iterator i = p->Add(m_parent, *m_zones, p->end());
908 if( i != p->end() ) {
909 wxString text((*i)->GetDescription().c_str(), wxConvUTF8);
911 // insert new record in same spot as DBCache has it
912 m_current_record_item = p->GetIndex(i);
913 m_record_list->InsertItem(m_current_record_item, text);
917 void BrowseMode::OnCopyRecord(wxCommandEvent &event)
919 // grab the cache for the current database... Get is ok here,
920 // since the cache is already loaded by the main db list
921 DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname);
922 if( !p.get() )
923 return;
925 DBCache::iterator source = p->Get(m_current_record_item);
926 DBCache::iterator i = p->Add(m_parent, *m_zones, source);
927 if( i != p->end() ) {
928 wxString text((*i)->GetDescription().c_str(), wxConvUTF8);
930 // insert new record in same spot as DBCache has it
931 m_current_record_item = p->GetIndex(i);
932 m_record_list->InsertItem(m_current_record_item, text);
936 void BrowseMode::OnEditRecord(wxCommandEvent &event)
938 // grab the cache for the current database... Get is ok here,
939 // since the cache is already loaded by the main db list
940 DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname);
941 if( !p.get() )
942 return;
944 DBCache::iterator i = p->Get(m_current_record_item);
945 if( p->Edit(m_parent, *m_zones, i) ) {
946 wxString text((*i)->GetDescription().c_str(), wxConvUTF8);
947 m_record_list->SetItem(m_current_record_item, 0, text);
951 void BrowseMode::OnDeleteRecord(wxCommandEvent &event)
953 // grab the cache for the current database... Get is ok here,
954 // since the cache is already loaded by the main db list
955 DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname);
956 if( !p.get() )
957 return;
959 DBCache::iterator i = p->Get(m_current_record_item);
960 if( p->Delete(m_parent, i) ) {
961 m_record_list->DeleteItem(m_current_record_item);
965 void BrowseMode::OnStatusEvent(wxCommandEvent &event)
967 m_load_status_text->SetLabel(event.GetString());