2 /// \file Mode_Browse.cc
3 /// Mode derived class for database browsing
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 "TaskEditDlg.h"
28 #include "windowids.h"
34 using namespace Barry
;
36 DEFINE_EVENT_TYPE(BMET_LOAD_STATUS
)
38 BEGIN_EVENT_TABLE(BrowseMode
, wxEvtHandler
)
39 EVT_LIST_ITEM_SELECTED(BrowseMode_DBDBList
,
40 BrowseMode::OnDBDBListSelChange
)
41 EVT_LIST_ITEM_SELECTED(BrowseMode_RecordList
,
42 BrowseMode::OnRecordListSelChange
)
43 EVT_LIST_ITEM_ACTIVATED(BrowseMode_RecordList
,
44 BrowseMode::OnRecordListActivated
)
45 EVT_CHECKBOX (BrowseMode_ShowAllCheckbox
,
46 BrowseMode::OnShowAll
)
47 EVT_BUTTON (BrowseMode_AddRecordButton
,
48 BrowseMode::OnAddRecord
)
49 EVT_BUTTON (BrowseMode_CopyRecordButton
,
50 BrowseMode::OnCopyRecord
)
51 EVT_BUTTON (BrowseMode_EditRecordButton
,
52 BrowseMode::OnEditRecord
)
53 EVT_BUTTON (BrowseMode_DeleteRecordButton
,
54 BrowseMode::OnDeleteRecord
)
55 EVT_COMMAND (wxID_ANY
, BMET_LOAD_STATUS
,
56 BrowseMode::OnStatusEvent
)
60 //////////////////////////////////////////////////////////////////////////////
61 // Standalone functions
63 void ShowReadOnlyMsg(wxWindow
*parent
, const Barry::ReturnCodeError
&rce
)
65 wxString
what(rce
.what(), wxConvUTF8
);
66 wxMessageBox(_T("This database is apparently read-only. If this device is connected to a BES, you cannot edit records via USB. (Error: ") + what
+ _T(")"),
67 _T("Device Error"), wxOK
| wxICON_ERROR
, parent
);
70 bool IsEditable(const std::string
&dbname
)
72 // add entry here for each edit dialog available
73 return dbname
== Contact::GetDBName() ||
74 dbname
== Calendar::GetDBName() ||
75 dbname
== Memo::GetDBName() ||
76 dbname
== Task::GetDBName();
79 bool EditRecord(wxWindow
*parent
,
81 const Barry::TimeZones
&zones
,
84 ContactEditDlg
edit(parent
, rec
, editable
);
85 return edit
.ShowModal() == wxID_OK
;
88 bool EditRecord(wxWindow
*parent
,
90 const Barry::TimeZones
&zones
,
96 bool EditRecord(wxWindow
*parent
,
98 const Barry::TimeZones
&zones
,
101 CalendarEditDlg
edit(parent
, rec
, editable
, &zones
);
102 return edit
.ShowModal() == wxID_OK
;
105 bool EditRecord(wxWindow
*parent
,
107 const Barry::TimeZones
&zones
,
108 Barry::CalendarAll
&rec
)
113 bool EditRecord(wxWindow
*parent
,
115 const Barry::TimeZones
&zones
,
116 Barry::ContentStore
&rec
)
121 bool EditRecord(wxWindow
*parent
,
123 const Barry::TimeZones
&zones
,
129 bool EditRecord(wxWindow
*parent
,
131 const Barry::TimeZones
&zones
,
134 MemoEditDlg
edit(parent
, rec
, editable
);
135 return edit
.ShowModal() == wxID_OK
;
138 bool EditRecord(wxWindow
*parent
,
140 const Barry::TimeZones
&zones
,
146 bool EditRecord(wxWindow
*parent
,
148 const Barry::TimeZones
&zones
,
154 bool EditRecord(wxWindow
*parent
,
156 const Barry::TimeZones
&zones
,
157 Barry::PINMessage
&rec
)
162 bool EditRecord(wxWindow
*parent
,
164 const Barry::TimeZones
&zones
,
165 Barry::SavedMessage
&rec
)
170 bool EditRecord(wxWindow
*parent
,
172 const Barry::TimeZones
&zones
,
173 Barry::ServiceBook
&rec
)
178 bool EditRecord(wxWindow
*parent
,
180 const Barry::TimeZones
&zones
,
186 bool EditRecord(wxWindow
*parent
,
188 const Barry::TimeZones
&zones
,
191 TaskEditDlg
edit(parent
, rec
, editable
, &zones
);
192 return edit
.ShowModal() == wxID_OK
;
195 bool EditRecord(wxWindow
*parent
,
197 const Barry::TimeZones
&zones
,
198 Barry::TimeZone
&rec
)
203 bool EditRecord(wxWindow
*parent
,
205 const Barry::TimeZones
&zones
,
206 Barry::HandheldAgent
&rec
)
212 //////////////////////////////////////////////////////////////////////////////
213 // GUIDesktopConnector
215 bool GUIDesktopConnector::PasswordPrompt(const Barry::BadPassword
&bp
,
216 std::string
&password_result
)
218 // create prompt based on exception data
220 oss
<< "Please enter device password: ("
221 << bp
.remaining_tries()
222 << " tries remaining)";
223 wxString
prompt(oss
.str().c_str(), wxConvUTF8
);
225 // ask user for device password
226 wxString pass
= wxGetPasswordFromUser(prompt
,
227 _T("Device Password"), _T(""), m_parent
);
229 password_result
= pass
.utf8_str();
231 // assume that a blank password means the user wishes to quit...
232 // wxWidgets doesn't seem to handle this very well?
233 return password_result
.size() > 0;
236 //////////////////////////////////////////////////////////////////////////////
239 DBDataCache::DBDataCache(DataCache::IndexType index
, const Barry::DBData
&raw
)
240 : DataCache(index
, raw
.GetUniqueId())
245 bool DBDataCache::Edit(wxWindow
*parent
,
247 const Barry::TimeZones
&zones
)
252 std::string
DBDataCache::GetDescription() const
258 //////////////////////////////////////////////////////////////////////////////
261 DBCache::DBCache(ThreadableDesktop
&tdesktop
, const std::string
&dbname
)
262 : m_tdesktop(tdesktop
)
266 DesktopInstancePtr dip
= m_tdesktop
.Get();
269 m_dbid
= dip
->Desktop().GetDBID(m_dbname
);
271 // load the record state table
272 dip
->Desktop().GetRecordStateTable(m_dbid
, m_state
);
275 AllRecordParser
ap(*this, *this);
276 RecordStateTable::StateMapType::iterator i
= m_state
.StateMap
.begin();
277 for( ; i
!= m_state
.StateMap
.end(); ++i
) {
278 m_index
= i
->second
.Index
; // save for the callback
279 dip
->Desktop().GetRecord(m_dbid
, m_index
, ap
);
282 // sort the list of records by description
290 DBCache::iterator
DBCache::Get(int list_offset
)
292 iterator i
= begin();
293 for( ; i
!= end() && list_offset
; ++i
, list_offset
-- )
298 DBCache::const_iterator
DBCache::Get(int list_offset
) const
300 const_iterator i
= begin();
301 for( ; i
!= end() && list_offset
; ++i
, list_offset
-- )
306 int DBCache::GetIndex(iterator record
) const
308 iterator i
= const_cast<DBCache
*> (this)->begin();
309 iterator e
= const_cast<DBCache
*> (this)->end();
310 for( int index
= 0; i
!= e
; ++i
, index
++ ) {
317 int DBCache::GetIndex(const_iterator record
) const
319 const_iterator i
= begin();
320 for( int index
= 0; i
!= end(); ++i
, index
++ ) {
327 DBCache::iterator
DBCache::Add(wxWindow
*parent
,
328 const Barry::TimeZones
&zones
,
329 iterator copy_record
)
333 #undef HANDLE_BUILDER
334 #define HANDLE_BUILDER(tname) \
335 if( m_dbname == Barry::tname::GetDBName() ) { \
337 if( copy_record != end() ) { \
338 RecordCache<Barry::tname> *rc = dynamic_cast<RecordCache<Barry::tname>* > (copy_record->get()); \
340 rec = rc->GetRecord(); \
343 p.reset( new RecordCache<Barry::tname>(0, rec) ); \
345 ALL_KNOWN_BUILDER_TYPES
347 // anything else is not addable or buildable
352 if( p
->Edit(parent
, true, zones
) ) {
353 // see if this record has a builder
354 Barry::Builder
*bp
= dynamic_cast<Barry::Builder
*> (p
.get());
359 // give record a new UniqueID
360 uint32_t record_id
= m_state
.MakeNewRecordId();
361 cout
<< "New recordID generated: 0x" << hex
<< record_id
<< endl
;
362 p
->SetIds(p
->GetStateIndex(), record_id
);
364 // add record to device
365 DesktopInstancePtr dip
= m_tdesktop
.Get();
366 Barry::Mode::Desktop
&desktop
= dip
->Desktop();
367 bool iv
= Barry::IsVerbose();
368 Barry::Verbose(true);
370 desktop
.AddRecord(m_dbid
, *bp
);
371 } catch( Barry::ReturnCodeError
&rce
) {
372 if( rce
.IsReadOnly() ) {
373 ShowReadOnlyMsg(parent
, rce
);
381 // update our copy of the record state table from device
382 desktop
.GetRecordStateTable(m_dbid
, m_state
);
383 cout
<< m_state
<< endl
;
385 // find our new record_id in list, to find the state index
387 if( !m_state
.GetIndex(record_id
, &new_index
) ) {
388 throw std::logic_error("Need to reconnect for adding a record?");
391 // update new state_index in the data cache record
392 p
->SetIds(new_index
, record_id
);
394 // add DataCachePtr to our own cache list
395 m_records
.push_front(p
);
397 // return iterator pointing to new record
405 bool DBCache::OverwriteRecord(wxWindow
*parent
, iterator record
)
407 // see if this record has a builder
408 Barry::Builder
*bp
= dynamic_cast<Barry::Builder
*> ((*record
).get());
412 cout
<< "Changing device record with index: 0x" << hex
<< (*record
)->GetStateIndex() << endl
;
413 cout
<< m_state
<< endl
;
414 // update the device with new record data
415 DesktopInstancePtr dip
= m_tdesktop
.Get();
416 Barry::Mode::Desktop
&desktop
= dip
->Desktop();
417 bool iv
= Barry::IsVerbose();
418 Barry::Verbose(true);
420 desktop
.SetRecord(m_dbid
, (*record
)->GetStateIndex(), *bp
);
421 } catch( Barry::ReturnCodeError
&rce
) {
422 if( rce
.IsReadOnly() ) {
423 ShowReadOnlyMsg(parent
, rce
);
434 bool DBCache::DeleteAndAddRecord(wxWindow
*parent
, iterator record
)
436 // see if this record has a builder
437 Barry::Builder
*bp
= dynamic_cast<Barry::Builder
*> ((*record
).get());
441 cout
<< "Changing device record with index: 0x" << hex
<< (*record
)->GetStateIndex() << endl
;
442 cout
<< m_state
<< endl
;
443 // update the device with new record data
444 DesktopInstancePtr dip
= m_tdesktop
.Get();
445 Barry::Mode::Desktop
&desktop
= dip
->Desktop();
446 bool iv
= Barry::IsVerbose();
447 Barry::Verbose(true);
449 desktop
.DeleteRecord(m_dbid
, (*record
)->GetStateIndex());
450 desktop
.AddRecord(m_dbid
, *bp
);
451 } catch( Barry::ReturnCodeError
&rce
) {
452 if( rce
.IsReadOnly() ) {
453 ShowReadOnlyMsg(parent
, rce
);
461 // update our copy of the record state table from device
462 desktop
.GetRecordStateTable(m_dbid
, m_state
);
463 cout
<< m_state
<< endl
;
465 // find our record_id in list, to find the state index
467 if( !m_state
.GetIndex((*record
)->GetRecordId(), &new_index
) ) {
468 throw std::logic_error("DAA: Need to reconnect for adding a record?");
471 // update new state_index in the data cache record
472 (*record
)->SetIds(new_index
, (*record
)->GetRecordId());
477 bool DBCache::Edit(wxWindow
*parent
,
478 const Barry::TimeZones
&zones
,
481 if( record
== end() )
484 if( (*record
)->Edit(parent
, true, zones
) && (*record
)->IsBuildable() ) {
485 // see if this record is part of the Tasks database
486 RecordCache
<Barry::Task
> *tp
= dynamic_cast< RecordCache
<Barry::Task
>*> ((*record
).get());
489 // yes, it is... the Tasks database has a bug
490 // so we need to "edit" by deleting and adding
492 return DeleteAndAddRecord(parent
, record
);
495 // use the normal code for all other records
496 return OverwriteRecord(parent
, record
);
504 bool DBCache::Delete(wxWindow
*parent
, iterator record
)
506 if( record
== end() )
509 // prompt user with Yes / No message
510 wxString
desc((*record
)->GetDescription().c_str(), wxConvUTF8
);
511 int choice
= wxMessageBox(_T("Delete record: ") + desc
+ _T("?"),
512 _T("Record Delete"), wxYES_NO
| wxICON_QUESTION
, parent
);
514 // if no, return false
515 if( choice
!= wxYES
)
518 cout
<< "Deleting device record with index: 0x" << hex
<< (*record
)->GetStateIndex() << endl
;
519 cout
<< m_state
<< endl
;
520 // delete record from device
521 DesktopInstancePtr dip
= m_tdesktop
.Get();
522 Barry::Mode::Desktop
&desktop
= dip
->Desktop();
523 desktop
.DeleteRecord(m_dbid
, (*record
)->GetStateIndex());
525 // remove record from cache list
526 m_records
.erase(record
);
530 // For Barry::AllRecordStore
532 #define HANDLE_PARSER(tname) \
533 void DBCache::operator() (const Barry::tname &rec) \
535 DataCachePtr p( new RecordCache<Barry::tname>(m_index, rec) ); \
536 m_records.push_front(p); \
538 ALL_KNOWN_PARSER_TYPES
541 void DBCache::ParseRecord(const Barry::DBData
&data
,
542 const Barry::IConverter
*ic
)
544 DataCachePtr
p( new DBDataCache(m_index
, data
) );
545 m_records
.push_front(p
);
549 //////////////////////////////////////////////////////////////////////////////
552 DBMap::DBMap(ThreadableDesktop
&tdesktop
)
553 : m_tdesktop(tdesktop
)
555 if( pthread_mutex_init(&m_map_mutex
, NULL
) ) {
556 throw Barry::Error("Failed to create map mutex");
559 if( pthread_mutex_init(&m_load_mutex
, NULL
) ) {
560 throw Barry::Error("Failed to create load mutex");
564 DBMap::DBCachePtr
DBMap::LoadDBCache(const std::string
&dbname
)
566 // first, check for pre-loaded data, before the load lock,
567 // to make sure we return pre-loaded data with utmost haste
569 scoped_lock
map_lock(m_map_mutex
);
571 MapType::iterator i
= m_map
.find(dbname
);
572 if( i
!= m_map
.end() )
576 // if not found, lock and load, but be careful, since we
577 // don't want to open a window here for loading a db twice
578 scoped_lock
load_lock(m_load_mutex
);
580 // check again for pre-loaded data, since between
581 // map.unlock and load.lock there could have been
582 // another successful load
584 scoped_lock
map_lock(m_map_mutex
);
586 MapType::iterator i
= m_map
.find(dbname
);
587 if( i
!= m_map
.end() )
591 // do the load, without map.lock, since this can take a
592 // significant amount of time
593 DBCachePtr
p( new DBCache(m_tdesktop
, dbname
) );
595 // lock once more to update the map, and then done
596 scoped_lock
map_lock(m_map_mutex
);
601 DBMap::DBCachePtr
DBMap::GetDBCache(const std::string
&dbname
)
603 scoped_lock
lock(m_map_mutex
);
605 MapType::iterator i
= m_map
.find(dbname
);
606 if( i
!= m_map
.end() )
612 //////////////////////////////////////////////////////////////////////////////
615 BrowseMode::BrowseMode(wxWindow
*parent
, const ProbeResult
&device
)
621 // create device identifying string
622 m_device_id_str
= wxString(device
.GetDisplayName().c_str(), wxConvUTF8
);
625 // connect to the device
627 m_con
.reset( new GUIDesktopConnector(m_parent
, "", "utf-8", device
) );
629 m_tdesktop
.reset( new ThreadableDesktop(*m_con
) );
631 // keep our own copy, and sort by name for later
632 m_dbdb
= m_con
->GetDesktop().GetDBDB();
635 // store a copy of the time zone set for record editing
636 if( TimeZones::IsLoadable(m_con
->GetDesktop()) ) {
637 // load time zones from device itself
638 m_zones
.reset( new TimeZones(m_con
->GetDesktop()) );
641 // use static time zone table from Barry library
642 m_zones
.reset( new TimeZones
);
647 // create our DBMap and give it the threadable desktop,
648 // now that we're finished doing any desktop USB work
649 m_dbmap
.reset( new DBMap(*m_tdesktop
) );
652 // From here down, we assume that our constructor succeeds, with
656 // fire off a background thread to cache database records
657 // in advance... if it fails, don't worry about it
658 m_abort_flag
= false;
659 int ret
= pthread_create(&m_cache_thread
, NULL
,
660 &BrowseMode::FillCacheThread
, this);
662 m_abort_flag
= true; // no need to join later
664 // connect ourselves to the parent's event handling chain
665 // do this last, so that we are guaranteed our destructor
666 // will run, in case of exceptions
667 m_parent
->PushEventHandler(this);
670 BrowseMode::~BrowseMode()
672 // unhook that event handler!
673 m_parent
->PopEventHandler();
675 // make sure the cache thread is finished before we destroy it :-)
676 if( !m_abort_flag
) {
679 pthread_join(m_cache_thread
, &junk
);
683 std::string
& GetDBName(Barry::DatabaseDatabase::Database
&db
)
688 void BrowseMode::SendStatusEvent(const std::string
&dbname
)
690 wxCommandEvent
event(BMET_LOAD_STATUS
, wxID_ANY
);
691 event
.SetEventObject(this);
693 if( dbname
.size() ) {
694 wxString
msg(_T("Loading: "));
695 msg
+= wxString(dbname
.c_str(), wxConvUTF8
);
696 event
.SetString(msg
);
699 event
.SetString(_T(""));
702 AddPendingEvent(event
);
705 void BrowseMode::CreateControls()
707 m_top_sizer
.reset( new wxBoxSizer(wxVERTICAL
) );
709 // make space for the main header, which is not part of our
711 m_top_sizer
->AddSpacer(MAIN_HEADER_OFFSET
);
715 // add list boxes to main area, the list_sizer
718 wxStaticBoxSizer
*list_sizer
= new wxStaticBoxSizer(wxHORIZONTAL
,
719 m_parent
, m_device_id_str
);
721 // add database listctrl
722 m_dbdb_list
.reset (new wxListCtrl(m_parent
, BrowseMode_DBDBList
,
723 wxDefaultPosition
, wxDefaultSize
,
724 wxLC_REPORT
| wxLC_SINGLE_SEL
) ); //| wxLC_VRULES
725 // int max_db_width = GetMaxWidth(m_dbdb_list.get(),
726 // m_dbdb.Databases.begin(), m_dbdb.Databases.end(),
728 list_sizer
->Add( m_dbdb_list
.get(), 4, wxEXPAND
| wxALL
, 4 );
730 // add the record listctrl
731 m_record_list
.reset(new wxListCtrl(m_parent
, BrowseMode_RecordList
,
732 wxDefaultPosition
, wxDefaultSize
,
733 wxLC_REPORT
| wxLC_SINGLE_SEL
) ); //| wxLC_VRULES
734 list_sizer
->Add( m_record_list
.get(), 5, wxEXPAND
| wxALL
, 4 );
736 // add list sizer to top sizer
737 m_top_sizer
->Add( list_sizer
, 1, wxEXPAND
| wxALL
, 4 );
740 // add "show all" checkbox and load status static textbox, inside sizer
743 wxBoxSizer
*status_sizer
= new wxBoxSizer(wxHORIZONTAL
);
745 m_show_all_checkbox
.reset( new wxCheckBox(m_parent
,
746 BrowseMode_ShowAllCheckbox
,
747 _T("Show All Databases"),
748 wxDefaultPosition
, wxDefaultSize
,
750 status_sizer
->Add( m_show_all_checkbox
.get(), 0, wxEXPAND
, 0 );
751 m_show_all_checkbox
->SetValue(m_show_all
);
753 status_sizer
->AddStretchSpacer();
755 m_load_status_text
.reset( new wxStaticText(m_parent
,
756 BrowseMode_LoadStatusText
,
758 wxDefaultPosition
, wxSize(200, -1),
759 wxST_NO_AUTORESIZE
) );
760 status_sizer
->Add( m_load_status_text
.get(), 0,
761 wxEXPAND
| wxALIGN_CENTRE_VERTICAL
, 0 );
763 m_top_sizer
->Add( status_sizer
, 0, wxEXPAND
| wxALL
, 4 );
771 // add bottom buttons - these go in the bottom FOOTER area
772 // so their heights must be fixed to MAIN_HEADER_OFFSET
773 // minus a border of 5px top and bottom
774 wxSize
footer(-1, MAIN_HEADER_OFFSET
- 5 - 5);
775 wxBoxSizer
*buttons
= new wxBoxSizer(wxHORIZONTAL
);
776 m_add_record_button
.reset( new wxButton(m_parent
,
777 BrowseMode_AddRecordButton
, _T("Add..."),
778 wxDefaultPosition
, footer
) );
779 m_copy_record_button
.reset( new wxButton(m_parent
,
780 BrowseMode_CopyRecordButton
, _T("Copy..."),
781 wxDefaultPosition
, footer
) );
782 m_edit_record_button
.reset( new wxButton(m_parent
,
783 BrowseMode_EditRecordButton
, _T("Edit..."),
784 wxDefaultPosition
, footer
));
785 m_delete_record_button
.reset( new wxButton(m_parent
,
786 BrowseMode_DeleteRecordButton
, _T("Delete..."),
787 wxDefaultPosition
, footer
) );
788 buttons
->Add(m_add_record_button
.get(), 0, wxRIGHT
, 5);
789 buttons
->Add(m_copy_record_button
.get(), 0, wxRIGHT
, 5);
790 buttons
->Add(m_edit_record_button
.get(), 0, wxRIGHT
, 5);
791 buttons
->Add(m_delete_record_button
.get(), 0, wxRIGHT
, 5);
792 m_top_sizer
->Add(buttons
, 0, wxALL
| wxALIGN_RIGHT
, 5);
795 // recalc size of children and add columns
797 wxSize client_size
= m_parent
->GetClientSize();
798 m_top_sizer
->SetDimension(0, 0,
799 client_size
.GetWidth(), client_size
.GetHeight());
802 wxSize dbdb_size
= m_dbdb_list
->GetClientSize();
803 int scroll_width
= wxSystemSettings::GetMetric(wxSYS_VSCROLL_X
);
804 int size
= dbdb_size
.GetWidth() - scroll_width
;
805 m_dbdb_list
->InsertColumn(0, _T("Databases"), wxLIST_FORMAT_LEFT
,
807 m_dbdb_list
->InsertColumn(1, _T("Count"), wxLIST_FORMAT_LEFT
,
808 size
* 0.20 + scroll_width
); // add back the scroll width
809 // so it doesn't look half-baked when
810 // there is no scroll bar
814 wxSize record_size
= m_record_list
->GetClientSize();
815 m_record_list
->InsertColumn(0, _T("Record Description"),
816 wxLIST_FORMAT_LEFT
, record_size
.GetWidth());
825 // attempt to re-select the devices as we last saw them
826 ReselectDevices(m_device_set->String2Subset(wxGetApp().GetGlobalConfig().GetKey("SelectedDevices")));
832 int BrowseMode::GUItoDBDBIndex(int gui_index
)
837 DatabaseDatabase::DatabaseArrayType::const_iterator
838 i
= m_dbdb
.Databases
.begin(), e
= m_dbdb
.Databases
.end();
839 for( int index
= 0; i
!= e
; ++i
, index
++ ) {
840 // only bump index on the parsable databases
841 if( !m_show_all
&& !IsParsable(i
->Name
) )
854 void BrowseMode::FillDBDBList()
857 m_dbdb_list
->DeleteAllItems();
859 DatabaseDatabase::DatabaseArrayType::const_iterator
860 i
= m_dbdb
.Databases
.begin(), e
= m_dbdb
.Databases
.end();
861 for( int index
= 0; i
!= e
; ++i
, index
++ ) {
862 // Only show parsable databases, depending on GUI
863 if( !m_show_all
&& !IsParsable(i
->Name
) )
867 wxString
text(i
->Name
.c_str(), wxConvUTF8
);
868 long item
= m_dbdb_list
->InsertItem(index
, text
);
872 oss
<< dec
<< i
->RecordCount
;
873 text
= wxString(oss
.str().c_str(), wxConvUTF8
);
874 m_dbdb_list
->SetItem(item
, 1, text
);
880 void BrowseMode::FillRecordList(const std::string
&dbname
)
885 m_record_list
->DeleteAllItems();
888 DBMap::DBCachePtr db
= m_dbmap
->LoadDBCache(dbname
);
890 // cycle through the cache, and insert the descriptions
891 // given for each record
892 DBCache::const_iterator b
= db
->begin(), e
= db
->end();
893 for( int index
= 0; b
!= e
; ++b
, index
++ ) {
894 wxString
text((*b
)->GetDescription().c_str(), wxConvUTF8
);
896 m_record_list
->InsertItem(index
, text
);
899 } catch( Barry::Error
&be
) {
900 cerr
<< be
.what() << endl
;
904 void BrowseMode::UpdateButtons()
906 int selected_count
= m_record_list
->GetSelectedItemCount();
908 // can only add if we have a builder and dialog for this record type
909 m_add_record_button
->Enable(m_buildable
&& m_editable
);
911 // can only copy or edit if we have a builder, a dialog, and
912 // only 1 is selected
913 m_copy_record_button
->Enable(
914 m_buildable
&& m_editable
&& selected_count
== 1);
915 m_edit_record_button
->Enable(
916 m_buildable
&& m_editable
&& selected_count
== 1);
918 // can only delete if something is selected
919 m_delete_record_button
->Enable(selected_count
> 0);
922 void BrowseMode::FillCache()
924 // create a copy of the dbdb, and sort in ascending order of
925 // record count, so the last db loaded is the longest...
926 // hopefully this makes the UI more user-friendly and responsive
927 DatabaseDatabase dbdb
= m_dbdb
;
928 dbdb
.SortByRecordCount();
930 // cycle through the dbdb and load all Parsable databases
931 DatabaseDatabase::DatabaseArrayType::const_iterator
932 i
= dbdb
.Databases
.begin(), e
= dbdb
.Databases
.end();
933 for( ; i
!= e
; ++i
) {
934 if( IsParsable(i
->Name
) ) try {
935 SendStatusEvent(i
->Name
);
936 m_dbmap
->LoadDBCache(i
->Name
);
937 } catch( Barry::Error
&be
) {
938 cerr
<< be
.what() << endl
;
951 void* BrowseMode::FillCacheThread(void *bobj
)
953 BrowseMode
*bm
= (BrowseMode
*) bobj
;
958 void BrowseMode::OnDBDBListSelChange(wxListEvent
&event
)
961 int index
= GUItoDBDBIndex(event
.GetIndex());
962 m_current_dbname
= m_dbdb
.Databases
.at(index
).Name
;
963 m_buildable
= ::IsBuildable(m_current_dbname
);
964 m_editable
= ::IsEditable(m_current_dbname
);
965 m_current_record_item
= -1;
967 FillRecordList(m_current_dbname
);
971 void BrowseMode::OnRecordListSelChange(wxListEvent
&event
)
973 // grab the cache for the current database... Get is ok here,
974 // since the cache is already loaded by the main db list
975 DBMap::DBCachePtr p
= m_dbmap
->GetDBCache(m_current_dbname
);
979 // grab the record list index
980 m_current_record_item
= event
.GetIndex();
981 // m_current_record_item = m_record_list->GetNextItem(
982 // m_current_record_item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
987 void BrowseMode::OnRecordListActivated(wxListEvent
&event
)
993 void BrowseMode::OnShowAll(wxCommandEvent
&event
)
995 m_show_all
= !m_show_all
;
999 void BrowseMode::OnAddRecord(wxCommandEvent
&event
)
1001 // grab the cache for the current database... Get is ok here,
1002 // since the cache is already loaded by the main db list
1003 DBMap::DBCachePtr p
= m_dbmap
->GetDBCache(m_current_dbname
);
1007 DBCache::iterator i
= p
->Add(m_parent
, *m_zones
, p
->end());
1008 if( i
!= p
->end() ) {
1009 wxString
text((*i
)->GetDescription().c_str(), wxConvUTF8
);
1011 // insert new record in same spot as DBCache has it
1012 m_current_record_item
= p
->GetIndex(i
);
1013 m_record_list
->InsertItem(m_current_record_item
, text
);
1017 void BrowseMode::OnCopyRecord(wxCommandEvent
&event
)
1019 // grab the cache for the current database... Get is ok here,
1020 // since the cache is already loaded by the main db list
1021 DBMap::DBCachePtr p
= m_dbmap
->GetDBCache(m_current_dbname
);
1025 DBCache::iterator source
= p
->Get(m_current_record_item
);
1026 DBCache::iterator i
= p
->Add(m_parent
, *m_zones
, source
);
1027 if( i
!= p
->end() ) {
1028 wxString
text((*i
)->GetDescription().c_str(), wxConvUTF8
);
1030 // insert new record in same spot as DBCache has it
1031 m_current_record_item
= p
->GetIndex(i
);
1032 m_record_list
->InsertItem(m_current_record_item
, text
);
1036 void BrowseMode::OnEditRecord(wxCommandEvent
&event
)
1038 // grab the cache for the current database... Get is ok here,
1039 // since the cache is already loaded by the main db list
1040 DBMap::DBCachePtr p
= m_dbmap
->GetDBCache(m_current_dbname
);
1044 DBCache::iterator i
= p
->Get(m_current_record_item
);
1045 if( p
->Edit(m_parent
, *m_zones
, i
) ) {
1046 wxString
text((*i
)->GetDescription().c_str(), wxConvUTF8
);
1047 m_record_list
->SetItem(m_current_record_item
, 0, text
);
1051 void BrowseMode::OnDeleteRecord(wxCommandEvent
&event
)
1053 // grab the cache for the current database... Get is ok here,
1054 // since the cache is already loaded by the main db list
1055 DBMap::DBCachePtr p
= m_dbmap
->GetDBCache(m_current_dbname
);
1059 DBCache::iterator i
= p
->Get(m_current_record_item
);
1060 if( p
->Delete(m_parent
, i
) ) {
1061 m_record_list
->DeleteItem(m_current_record_item
);
1065 void BrowseMode::OnStatusEvent(wxCommandEvent
&event
)
1067 m_load_status_text
->SetLabel(event
.GetString());