From e112e18d8ba4423bd04d6dcb7aa6c809a8548719 Mon Sep 17 00:00:00 2001 From: Chris Frey Date: Mon, 7 Feb 2011 01:07:14 -0500 Subject: [PATCH] desktop: added Add/Copy/Edit/Delete support for Address Book records --- desktop/src/Mode_Browse.cc | 283 +++++++++++++++++++++++++++++++++++++++++++-- desktop/src/Mode_Browse.h | 93 +++++++++++---- desktop/src/windowids.h | 1 + 3 files changed, 345 insertions(+), 32 deletions(-) diff --git a/desktop/src/Mode_Browse.cc b/desktop/src/Mode_Browse.cc index 2f1a38a3..3edae8d9 100644 --- a/desktop/src/Mode_Browse.cc +++ b/desktop/src/Mode_Browse.cc @@ -21,7 +21,9 @@ #include "Mode_Browse.h" #include "BaseFrame.h" +#include "ContactEditDlg.h" #include "windowids.h" +#include #include #include @@ -31,10 +33,16 @@ using namespace Barry; BEGIN_EVENT_TABLE(BrowseMode, wxEvtHandler) EVT_LIST_ITEM_SELECTED(BrowseMode_DBDBList, BrowseMode::OnDBDBListSelChange) + EVT_LIST_ITEM_SELECTED(BrowseMode_RecordList, + BrowseMode::OnRecordListSelChange) + EVT_LIST_ITEM_ACTIVATED(BrowseMode_RecordList, + BrowseMode::OnRecordListActivated) EVT_CHECKBOX (BrowseMode_ShowAllCheckbox, BrowseMode::OnShowAll) EVT_BUTTON (BrowseMode_AddRecordButton, BrowseMode::OnAddRecord) + EVT_BUTTON (BrowseMode_CopyRecordButton, + BrowseMode::OnCopyRecord) EVT_BUTTON (BrowseMode_EditRecordButton, BrowseMode::OnEditRecord) EVT_BUTTON (BrowseMode_DeleteRecordButton, @@ -47,7 +55,8 @@ END_EVENT_TABLE() bool EditRecord(wxWindow *parent, bool editable, Barry::Contact &rec) { - return false; + ContactEditDlg edit(parent, rec, editable); + return edit.ShowModal() == wxID_OK; } bool EditRecord(wxWindow *parent, bool editable, Barry::Bookmark &rec) @@ -125,13 +134,14 @@ bool EditRecord(wxWindow *parent, bool editable, Barry::Timezone &rec) // DBDataCache DBDataCache::DBDataCache(DataCache::IndexType index, const Barry::DBData &raw) - : DataCache(index) + : DataCache(index, raw.GetUniqueId()) , m_raw(raw) { } -void DBDataCache::Edit(wxWindow *parent, bool editable) +bool DBDataCache::Edit(wxWindow *parent, bool editable) { + return false; } std::string DBDataCache::GetDescription() const @@ -171,6 +181,159 @@ DBCache::~DBCache() { } +DBCache::iterator DBCache::Get(int list_offset) +{ + iterator i = begin(); + for( ; i != end() && list_offset; ++i, list_offset-- ) + ; + return i; +} + +DBCache::const_iterator DBCache::Get(int list_offset) const +{ + const_iterator i = begin(); + for( ; i != end() && list_offset; ++i, list_offset-- ) + ; + return i; +} + +int DBCache::GetIndex(iterator record) const +{ + iterator i = const_cast (this)->begin(); + iterator e = const_cast (this)->end(); + for( int index = 0; i != e; ++i, index++ ) { + if( i == record ) + return index; + } + return -1; +} + +int DBCache::GetIndex(const_iterator record) const +{ + const_iterator i = begin(); + for( int index = 0; i != end(); ++i, index++ ) { + if( i == record ) + return index; + } + return -1; +} + +DBCache::iterator DBCache::Add(wxWindow *parent, iterator copy_record) +{ + DataCachePtr p; + +#undef HANDLE_BUILDER +#define HANDLE_BUILDER(tname) \ + if( m_dbname == Barry::tname::GetDBName() ) { \ + Barry::tname rec; \ + if( copy_record != end() ) { \ + RecordCache *rc = dynamic_cast* > (copy_record->get()); \ + if( rc ) { \ + rec = rc->GetRecord(); \ + } \ + } \ + p.reset( new RecordCache(0, rec) ); \ + } + ALL_KNOWN_BUILDER_TYPES + + // anything else is not addable or buildable + if( !p.get() ) { + return end(); + } + + if( p->Edit(parent, true) ) { + // see if this record has a builder + Barry::Builder *bp = dynamic_cast (p.get()); + if( !bp ) { + return end(); + } + + // give record a new UniqueID + uint32_t record_id = m_state.MakeNewRecordId(); +cout << "New recordID generated: 0x" << hex << record_id << endl; + p->SetIds(p->GetStateIndex(), record_id); + + // add record to device + DesktopInstancePtr dip = m_tdesktop.Get(); + Barry::Mode::Desktop &desktop = dip->Desktop(); + desktop.AddRecord(m_dbid, *bp); + + // update our copy of the record state table from device + desktop.GetRecordStateTable(m_dbid, m_state); +cout << m_state << endl; + + // find our new record_id in list, to find the state index + IndexType new_index; + if( !m_state.GetIndex(record_id, &new_index) ) { + throw std::logic_error("Need to reconnect for adding a record?"); + } + + // update new state_index in the data cache record + p->SetIds(new_index, record_id); + + // add DataCachePtr to our own cache list + m_records.push_front(p); + + // return iterator pointing to new record + return begin(); + } + else { + return end(); + } +} + +bool DBCache::Edit(wxWindow *parent, iterator record) +{ + if( record == end() ) + return false; + + if( (*record)->Edit(parent, true) && (*record)->IsBuildable() ) { + // see if this record has a builder + Barry::Builder *bp = dynamic_cast ((*record).get()); + if( !bp ) + return false; + +cout << "Changing device record with index: 0x" << hex << (*record)->GetStateIndex() << endl; +cout << m_state << endl; + // update the device with new record data + DesktopInstancePtr dip = m_tdesktop.Get(); + Barry::Mode::Desktop &desktop = dip->Desktop(); + desktop.SetRecord(m_dbid, (*record)->GetStateIndex(), *bp); + desktop.ClearDirty(m_dbid, (*record)->GetStateIndex()); + + return true; + } + else { + return false; + } +} + +bool DBCache::Delete(wxWindow *parent, iterator record) +{ + if( record == end() ) + return false; + + // prompt user with Yes / No message + wxString desc((*record)->GetDescription().c_str(), wxConvUTF8); + int choice = wxMessageBox(_T("Delete record: ") + desc + _T("?"), + _T("Record Delete"), wxYES_NO | wxICON_QUESTION, parent); + + // if no, return false + if( choice != wxYES ) + return false; + +cout << "Deleting device record with index: 0x" << hex << (*record)->GetStateIndex() << endl; +cout << m_state << endl; + // delete record from device + DesktopInstancePtr dip = m_tdesktop.Get(); + Barry::Mode::Desktop &desktop = dip->Desktop(); + desktop.DeleteRecord(m_dbid, (*record)->GetStateIndex()); + + // remove record from cache list + m_records.erase(record); + return true; +} + // For Barry::AllRecordStore #undef HANDLE_PARSER #define HANDLE_PARSER(tname) \ @@ -201,7 +364,7 @@ DBMap::DBMap(ThreadableDesktop &tdesktop) } } -DBMap::DBCachePtr DBMap::GetDBCache(const std::string &dbname) +DBMap::DBCachePtr DBMap::LoadDBCache(const std::string &dbname) { scoped_lock lock(m_map_mutex); @@ -218,6 +381,17 @@ DBMap::DBCachePtr DBMap::GetDBCache(const std::string &dbname) return p; } +DBMap::DBCachePtr DBMap::GetDBCache(const std::string &dbname) +{ + scoped_lock lock(m_map_mutex); + + MapType::iterator i = m_map.find(dbname); + if( i != m_map.end() ) + return i->second; + + return DBCachePtr(); +} + ////////////////////////////////////////////////////////////////////////////// // BrowseMode @@ -386,16 +560,20 @@ void BrowseMode::CreateControls() m_add_record_button.reset( new wxButton(m_parent, BrowseMode_AddRecordButton, _T("Add..."), wxDefaultPosition, footer) ); + m_copy_record_button.reset( new wxButton(m_parent, + BrowseMode_CopyRecordButton, _T("Copy..."), + wxDefaultPosition, footer) ); m_edit_record_button.reset( new wxButton(m_parent, BrowseMode_EditRecordButton, _T("Edit..."), wxDefaultPosition, footer)); m_delete_record_button.reset( new wxButton(m_parent, BrowseMode_DeleteRecordButton, _T("Delete..."), wxDefaultPosition, footer) ); - buttons->Add(m_add_record_button.get(), 0, wxRIGHT, 5 ); - buttons->Add(m_edit_record_button.get(), 0, wxRIGHT, 5 ); - buttons->Add(m_delete_record_button.get(), 0, wxRIGHT, 5 ); - m_top_sizer->Add(buttons, 0, wxALL | wxALIGN_RIGHT, 5 ); + buttons->Add(m_add_record_button.get(), 0, wxRIGHT, 5); + buttons->Add(m_copy_record_button.get(), 0, wxRIGHT, 5); + buttons->Add(m_edit_record_button.get(), 0, wxRIGHT, 5); + buttons->Add(m_delete_record_button.get(), 0, wxRIGHT, 5); + m_top_sizer->Add(buttons, 0, wxALL | wxALIGN_RIGHT, 5); // // recalc size of children and add columns @@ -491,7 +669,7 @@ void BrowseMode::FillRecordList(const std::string &dbname) m_record_list->DeleteAllItems(); // grab our DB - DBMap::DBCachePtr db = m_dbmap->GetDBCache(dbname); + DBMap::DBCachePtr db = m_dbmap->LoadDBCache(dbname); // cycle through the cache, and insert the descriptions // given for each record @@ -513,7 +691,8 @@ void BrowseMode::UpdateButtons() // can only add if we have a builder for this record type m_add_record_button->Enable(m_buildable); - // can only edit if we have a builder, and if only 1 is selected + // can only copy or edit if we have a builder, and only 1 is selected + m_copy_record_button->Enable(m_buildable && selected_count == 1); m_edit_record_button->Enable(m_buildable && selected_count == 1); // can only delete if something is selected m_delete_record_button->Enable(selected_count > 0); @@ -526,7 +705,7 @@ void BrowseMode::FillCache() i = m_dbdb.Databases.begin(), e = m_dbdb.Databases.end(); for( ; i != e; ++i ) { if( IsParsable(i->Name) ) try { - m_dbmap->GetDBCache(i->Name); + m_dbmap->LoadDBCache(i->Name); } catch( Barry::Error &be ) { cerr << be.what() << endl; } @@ -550,10 +729,36 @@ void BrowseMode::OnDBDBListSelChange(wxListEvent &event) { wxBusyCursor wait; int index = GUItoDBDBIndex(event.GetIndex()); - FillRecordList(m_dbdb.Databases.at(index).Name); + m_current_dbname = m_dbdb.Databases.at(index).Name; + m_buildable = ::IsBuildable(m_current_dbname); + m_current_record_item = -1; + + FillRecordList(m_current_dbname); UpdateButtons(); } +void BrowseMode::OnRecordListSelChange(wxListEvent &event) +{ + // grab the cache for the current database... Get is ok here, + // since the cache is already loaded by the main db list + DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname); + if( !p.get() ) + return; + + // grab the record list index + m_current_record_item = event.GetIndex(); +// m_current_record_item = m_record_list->GetNextItem( +// m_current_record_item, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + + UpdateButtons(); +} + +void BrowseMode::OnRecordListActivated(wxListEvent &event) +{ + wxCommandEvent ce; + OnEditRecord(ce); +} + void BrowseMode::OnShowAll(wxCommandEvent &event) { m_show_all = !m_show_all; @@ -562,13 +767,67 @@ void BrowseMode::OnShowAll(wxCommandEvent &event) void BrowseMode::OnAddRecord(wxCommandEvent &event) { + // grab the cache for the current database... Get is ok here, + // since the cache is already loaded by the main db list + DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname); + if( !p.get() ) + return; + + DBCache::iterator i = p->Add(m_parent, p->end()); + if( i != p->end() ) { + wxString text((*i)->GetDescription().c_str(), wxConvUTF8); + + // insert new record in same spot as DBCache has it + m_current_record_item = p->GetIndex(i); + m_record_list->InsertItem(m_current_record_item, text); + } +} + +void BrowseMode::OnCopyRecord(wxCommandEvent &event) +{ + // grab the cache for the current database... Get is ok here, + // since the cache is already loaded by the main db list + DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname); + if( !p.get() ) + return; + + DBCache::iterator source = p->Get(m_current_record_item); + DBCache::iterator i = p->Add(m_parent, source); + if( i != p->end() ) { + wxString text((*i)->GetDescription().c_str(), wxConvUTF8); + + // insert new record in same spot as DBCache has it + m_current_record_item = p->GetIndex(i); + m_record_list->InsertItem(m_current_record_item, text); + } } void BrowseMode::OnEditRecord(wxCommandEvent &event) { + // grab the cache for the current database... Get is ok here, + // since the cache is already loaded by the main db list + DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname); + if( !p.get() ) + return; + + DBCache::iterator i = p->Get(m_current_record_item); + if( p->Edit(m_parent, i) ) { + wxString text((*i)->GetDescription().c_str(), wxConvUTF8); + m_record_list->SetItem(m_current_record_item, 0, text); + } } void BrowseMode::OnDeleteRecord(wxCommandEvent &event) { + // grab the cache for the current database... Get is ok here, + // since the cache is already loaded by the main db list + DBMap::DBCachePtr p = m_dbmap->GetDBCache(m_current_dbname); + if( !p.get() ) + return; + + DBCache::iterator i = p->Get(m_current_record_item); + if( p->Delete(m_parent, i) ) { + m_record_list->DeleteItem(m_current_record_item); + } } diff --git a/desktop/src/Mode_Browse.h b/desktop/src/Mode_Browse.h index d8034f46..dc5ac1c6 100644 --- a/desktop/src/Mode_Browse.h +++ b/desktop/src/Mode_Browse.h @@ -90,33 +90,38 @@ public: private: IndexType m_state_index; + uint32_t m_record_id; public: - explicit DataCache(IndexType state_index) + DataCache(IndexType state_index, uint32_t record_id) : m_state_index(state_index) + , m_record_id(record_id) { } virtual ~DataCache() {} - IndexType GetStateIndex() const { return m_state_index; } + virtual IndexType GetStateIndex() const { return m_state_index; } + virtual uint32_t GetRecordId() const { return m_record_id; } + virtual void SetIds(IndexType state_index, uint32_t record_id) + { + std::cout << "DataCache::SetIds(" << state_index << ", " << record_id << ");" << std::endl; + m_state_index = state_index; + m_record_id = record_id; + } // set editable to false if you just want to view record // non-buildable records will always be non-editable regardless - virtual void Edit(wxWindow *parent, bool editable) = 0; + virtual bool Edit(wxWindow *parent, bool editable) = 0; virtual std::string GetDescription() const = 0; virtual bool IsBuildable() const { return false; } - virtual void Build(Barry::DBData &data, size_t offset, - const Barry::IConverter *ic) const - { - // not buildable - } bool operator< (const DataCache &other) { return GetDescription() < other.GetDescription(); } + }; typedef std::tr1::shared_ptr DataCachePtr; @@ -128,7 +133,7 @@ class DBDataCache : public DataCache public: DBDataCache(DataCache::IndexType index, const Barry::DBData &raw); - virtual void Edit(wxWindow *parent, bool editable); + virtual bool Edit(wxWindow *parent, bool editable); virtual std::string GetDescription() const; }; @@ -139,25 +144,42 @@ public: ALL_KNOWN_PARSER_TYPES template -class RecordCache : public DataCache +class RecordCache + : public DataCache + , public Barry::Builder { private: RecordT m_rec; public: RecordCache(DataCache::IndexType index, const RecordT &rec) - : DataCache(index) + : DataCache(index, rec.GetUniqueId()) , m_rec(rec) { + // if copying from another record, don't copy what + // we don't understand + m_rec.Unknowns.clear(); + } + + const RecordT& GetRecord() const { return m_rec; } + + // hook SetIds() to grab any new record_ids / UniqueIDs + virtual void SetIds(IndexType state_index, uint32_t record_id) + { + std::cout << "RecordCache::SetIds(" << state_index << ", " << record_id << ");" << std::endl; + DataCache::SetIds(state_index, record_id); + m_rec.SetIds(RecordT::GetDefaultRecType(), record_id); } - virtual void Edit(wxWindow *parent, bool editable) + virtual bool Edit(wxWindow *parent, bool editable) { RecordT copy = m_rec; bool changed = EditRecord(parent, editable, copy); if( changed && editable ) { m_rec = copy; + return true; } + return false; } virtual std::string GetDescription() const @@ -170,11 +192,27 @@ public: return ::IsBuildable(); } - virtual void Build(Barry::DBData &data, size_t offset, + // + // Barry::Builder overrides + // + virtual bool BuildRecord(Barry::DBData &data, size_t &offset, const Barry::IConverter *ic) { -// FIXME -// Barry::SetDBData(m_rec, data, 0, ic); + Barry::SetDBData(m_rec, data, offset, ic); + return true; + } + + virtual bool FetchRecord(Barry::DBData &data, + const Barry::IConverter *ic) + { + size_t offset = 0; + Barry::SetDBData(m_rec, data, offset, ic); + return true; + } + + virtual bool EndOfFile() const + { + return true; } }; @@ -203,12 +241,20 @@ public: const std::string& GetDBName() { return m_dbname; } -// virtual void Add(wxWindow *parent); -// virtual void Edit(wxWindow *parent, int list_offset); -// virtual void Delete(wxWindow *parent, int list_offset); + iterator Get(int list_offset); + const_iterator Get(int list_offset) const; + // returns the numeric index of the record, to keep with GUI + int GetIndex(iterator record) const; + int GetIndex(const_iterator record) const; + + iterator Add(wxWindow *parent, iterator copy_record); + bool Edit(wxWindow *parent, iterator record); + bool Delete(wxWindow *parent, iterator record); - DataList::const_iterator begin() const { return m_records.begin(); } - DataList::const_iterator end() const { return m_records.end(); } + iterator begin() { return m_records.begin(); } + iterator end() { return m_records.end(); } + const_iterator begin() const { return m_records.begin(); } + const_iterator end() const { return m_records.end(); } // For Barry::AllRecordStore #undef HANDLE_PARSER @@ -235,6 +281,7 @@ private: public: DBMap(ThreadableDesktop &tdesktop); + DBCachePtr LoadDBCache(const std::string &dbname); DBCachePtr GetDBCache(const std::string &dbname); }; @@ -260,6 +307,7 @@ private: std::auto_ptr m_record_list; std::auto_ptr m_show_all_checkbox; std::auto_ptr m_add_record_button; + std::auto_ptr m_copy_record_button; std::auto_ptr m_edit_record_button; std::auto_ptr m_delete_record_button; @@ -271,6 +319,8 @@ private: // a Builder available for it bool m_show_all; // if true, show all databases in list // instead of just the parsable ones + std::string m_current_dbname; + long m_current_record_item; // thread state pthread_t m_cache_thread; @@ -296,8 +346,11 @@ public: // window events void OnDBDBListSelChange(wxListEvent &event); + void OnRecordListSelChange(wxListEvent &event); + void OnRecordListActivated(wxListEvent &event); void OnShowAll(wxCommandEvent &event); void OnAddRecord(wxCommandEvent &event); + void OnCopyRecord(wxCommandEvent &event); void OnEditRecord(wxCommandEvent &event); void OnDeleteRecord(wxCommandEvent &event); }; diff --git a/desktop/src/windowids.h b/desktop/src/windowids.h index 93652305..0fff0595 100644 --- a/desktop/src/windowids.h +++ b/desktop/src/windowids.h @@ -66,6 +66,7 @@ enum { BrowseMode_RecordList, BrowseMode_ShowAllCheckbox, BrowseMode_AddRecordButton, + BrowseMode_CopyRecordButton, BrowseMode_EditRecordButton, BrowseMode_DeleteRecordButton, -- 2.11.4.GIT