Folks,

        Sorry about the very rapid changes :) The attached patch augments my
previous patch to add category support for memos as well.

        John.

diff --git a/opensync-plugin-0.4x/src/vbase.cc b/opensync-plugin-0.4x/src/vbase.cc
index b0824ef..cf0f5e2 100644
--- a/opensync-plugin-0.4x/src/vbase.cc
+++ b/opensync-plugin-0.4x/src/vbase.cc
@@ -291,6 +291,38 @@ std::string vBase::GetAttr(const char *attrname, const char *block)
 	return ret;
 }
 
+std::vector<std::string> vBase::GetValueVector(const char * attrname, const char * block)
+{
+	Trace trace("vBase::GetValueVector");
+	trace.logf("getting value vector for: %s", attrname);
+
+	std::vector<std::string> ret;
+	const char *value = 0;
+	bool needs_freeing = false;
+
+	b_VFormatAttribute *attr = b_vformat_find_attribute(m_format, attrname, 0, block);
+	if( attr ) {
+		if( b_vformat_attribute_is_single_valued(attr) ) {
+			value = b_vformat_attribute_get_value(attr);
+			needs_freeing = true;
+		} else {
+			// nasty, but avoids tweaking vformat.
+			int idx=0;
+			do {
+				value=b_vformat_attribute_get_nth_value(attr,idx++);
+				if(value) {
+					ret.push_back(value);
+				}
+			} while(value);
+		}
+	}
+	
+	if( needs_freeing )
+		g_free((char *)value);
+
+	return ret;
+}
+
 vAttr vBase::GetAttrObj(const char *attrname, int nth, const char *block)
 {
 	Trace trace("vBase::GetAttrObj");
@@ -299,3 +331,50 @@ vAttr vBase::GetAttrObj(const char *attrname, int nth, const char *block)
 	return vAttr(b_vformat_find_attribute(m_format, attrname, nth, block));
 }
 
+std::vector<std::string> vBase::Tokenize(const std::string& str, const char delim)
+{
+	std::vector<std::string> tokens;
+	std::string::size_type delimPos = 0, tokenPos = 0, pos = 0;
+
+	if(str.length()<1) {
+		return tokens;
+	}
+	
+	while(1) {
+		delimPos = str.find_first_of(delim, pos);
+		tokenPos = str.find_first_not_of(delim, pos);
+
+		if(std::string::npos != delimPos) {
+			if(std::string::npos != tokenPos) {
+				if(tokenPos<delimPos) {
+					tokens.push_back(str.substr(pos,delimPos-pos));
+				} else {
+					tokens.push_back("");
+				}
+			} else {
+				tokens.push_back("");
+			}
+			pos = delimPos+1;
+		} else {
+			if(std::string::npos != tokenPos){
+				tokens.push_back(str.substr(pos));
+			} else {
+				tokens.push_back("");
+			}
+			break;
+		}
+	}
+	return tokens;
+}
+
+std::string vBase::ToStringList(const std::vector<std::string>& list, const char delim)
+{
+	std::string str;
+	for(unsigned int idx=0;idx<list.size();idx++) {
+		if(idx) {
+			str+=delim;
+		}
+		str += list[idx];
+	}
+	return str;
+}
diff --git a/opensync-plugin-0.4x/src/vbase.h b/opensync-plugin-0.4x/src/vbase.h
index e8cfd6c..3acf32c 100644
--- a/opensync-plugin-0.4x/src/vbase.h
+++ b/opensync-plugin-0.4x/src/vbase.h
@@ -23,6 +23,7 @@
 #define __BARRY_SYNC_VBASE_H__
 
 #include <string>
+#include <vector>
 #include <stdexcept>
 #include "vformat.h"
 
@@ -193,7 +194,12 @@ protected:
 	void AddParam(vAttrPtr &attr, const char *name, const char *value);
 
 	std::string GetAttr(const char *attrname, const char *block = 0);
+	std::vector<std::string> GetValueVector(const char * attrname, const char * block=0);
 	vAttr GetAttrObj(const char *attrname, int nth = 0, const char *block = 0);
+	
+	std::vector<std::string> Tokenize(const std::string& str, const char delim=',');
+	std::string ToStringList(const std::vector<std::string>& list, const char delim=',');
+
 };
 
 #endif
diff --git a/opensync-plugin-0.4x/src/vevent.cc b/opensync-plugin-0.4x/src/vevent.cc
index dba4d0d..96dd29d 100644
--- a/opensync-plugin-0.4x/src/vevent.cc
+++ b/opensync-plugin-0.4x/src/vevent.cc
@@ -59,43 +59,6 @@ unsigned short vCalendar::GetWeekDayIndex(const char *dayname)
 	return 0;
 }
 
-std::vector<std::string> vCalendar::GetDOWList(const std::string& str)
-{
-	std::vector<std::string> tokens;
-	std::string::size_type delimPos = 0, tokenPos = 0, pos = 0;
-
-	if(str.length()<1) {
-		return tokens;
-	}
-
-	while(1) {
-		delimPos = str.find_first_of(',', pos);
-		tokenPos = str.find_first_not_of(',', pos);
-
-		if(std::string::npos != delimPos) {
-			if(std::string::npos != tokenPos) {
-				if(tokenPos<delimPos) {
-					tokens.push_back(str.substr(pos,delimPos-pos));
-				} else {
-					tokens.push_back("");
-				}
-			} else {
-				tokens.push_back("");
-			}
-			pos = delimPos+1;
-		} else {
-			if(std::string::npos != tokenPos){
-				tokens.push_back(str.substr(pos));
-			} else {
-				tokens.push_back("");
-			}
-			break;
-		}
-	}
-	return tokens;
-}
-
-
 unsigned short vCalendar::GetMonthWeekNumFromBYDAY(const std::string& ByDay)
 {
 	return atoi(ByDay.substr(0,ByDay.length()-2).c_str());
@@ -356,7 +319,7 @@ void vCalendar::RecurToBarryCal(vAttr& rrule, time_t starttime)
 		cal.RecurringType=Calendar::Week;
 		// we must have a dayofweek entry
 		if(args.find(string("BYDAY"))!=args.end()) {
-			std::vector<std::string> v=GetDOWList(args["BYDAY"]);
+			std::vector<std::string> v=Tokenize(args["BYDAY"]);
 			// iterate along our vector and convert
 			for(unsigned int idx=0;idx<v.size();idx++) {
 				cal.WeekDays|=pmap[v[idx]];
diff --git a/opensync-plugin-0.4x/src/vevent.h b/opensync-plugin-0.4x/src/vevent.h
index 0e8423e..1ff681d 100644
--- a/opensync-plugin-0.4x/src/vevent.h
+++ b/opensync-plugin-0.4x/src/vevent.h
@@ -48,7 +48,6 @@ class vCalendar : public vBase
 
 	static const char *WeekDays[7];
 
-	std::vector<std::string> GetDOWList(const std::string& str);
 	unsigned short GetMonthWeekNumFromBYDAY(const std::string& ByDay);
 	unsigned short GetWeekDayIndexFromBYDAY(const std::string& ByDay);
 
diff --git a/opensync-plugin-0.4x/src/vjournal.cc b/opensync-plugin-0.4x/src/vjournal.cc
index 405e62d..57edac9 100644
--- a/opensync-plugin-0.4x/src/vjournal.cc
+++ b/opensync-plugin-0.4x/src/vjournal.cc
@@ -89,6 +89,8 @@ const std::string& vJournal::ToMemo(const Barry::Memo &memo)
 	AddAttr(NewAttr("SEQUENCE", "0"));
 	AddAttr(NewAttr("SUMMARY", memo.Title.c_str()));
 	AddAttr(NewAttr("DESCRIPTION", memo.Body.c_str()));
+	AddAttr(NewAttr("CATEGORIES",ToStringList(memo.Categories).c_str()));
+
 
 	// FIXME - add a truly globally unique "UID" string?
 
@@ -147,6 +149,7 @@ const Barry::Memo& vJournal::ToBarry(const char *vjournal, uint32_t RecordId)
 
 	rec.Title = title;
 	rec.Body = body;
+	rec.Categories=GetValueVector("CATEGORIES","/vjournal");
 
 	std::ostringstream oss;
 	m_BarryMemo.Dump(oss);
@@ -321,4 +324,3 @@ bool VJournalConverter::CommitRecordData(BarryEnvironment *env, unsigned int dbI
 
 	return true;
 }
-
diff --git a/opensync-plugin-0.4x/src/vjournal.h b/opensync-plugin-0.4x/src/vjournal.h
index c2c9672..a2b2a7e 100644
--- a/opensync-plugin-0.4x/src/vjournal.h
+++ b/opensync-plugin-0.4x/src/vjournal.h
@@ -33,6 +33,7 @@
 // forward declarations
 class BarryEnvironment;
 
+
 //
 // vJournal
 //
diff --git a/opensync-plugin-0.4x/src/vtodo.cc b/opensync-plugin-0.4x/src/vtodo.cc
index 1cb17a1..8261431 100644
--- a/opensync-plugin-0.4x/src/vtodo.cc
+++ b/opensync-plugin-0.4x/src/vtodo.cc
@@ -101,6 +101,7 @@ const std::string& vTodo::ToTask(const Barry::Task &task)
 	AddAttr(NewAttr("SEQUENCE", "0"));
 	AddAttr(NewAttr("SUMMARY", task.Summary.c_str()));
 	AddAttr(NewAttr("DESCRIPTION", task.Notes.c_str()));
+	AddAttr(NewAttr("CATEGORIES",ToStringList(task.Categories).c_str()));
 
 	// Status
 	if (task.StatusFlag == Barry::Task::InProgress)
@@ -190,6 +191,7 @@ const Barry::Task& vTodo::ToBarry(const char *vtodo, uint32_t RecordId)
 	string due = GetAttr("DUE", "/vtodo");
 	trace.logf("DUE attr retrieved: %s", due.c_str());
 
+
 	//
 	// Now, run checks and convert into Barry object
 	//
@@ -209,6 +211,10 @@ const Barry::Task& vTodo::ToBarry(const char *vtodo, uint32_t RecordId)
 	Barry::Task &rec = m_BarryTask;
 	rec.SetIds(Barry::Task::GetDefaultRecType(), RecordId);
 
+	// Categories
+
+	rec.Categories=GetValueVector("CATEGORIES","/vtodo");
+
 	// SUMMARY & DESCRIPTION fields
 	rec.Summary = summary;
 	rec.Notes = notes;
diff --git a/src/r_memo.cc b/src/r_memo.cc
index 5fe55d1..52fb2fe 100644
--- a/src/r_memo.cc
+++ b/src/r_memo.cc
@@ -47,7 +47,7 @@ namespace Barry {
 static FieldLink<Memo> MemoFieldLinks[] = {
     { MEMFC_TITLE,     "Title",       0, 0, &Memo::Title, 0, 0, 0, 0, true },
     { MEMFC_BODY,      "Body",        0, 0, &Memo::Body, 0, 0, 0, 0, true },
-    { MEMFC_CATEGORY,  "Category",    0, 0, &Memo::Category, 0, 0, 0, 0, true },
+//    { MEMFC_CATEGORY,  "Category",    0, 0, &Memo::Category, 0, 0, 0, 0, true },
     { MEMFC_END,       "End of List", 0, 0, 0, 0, 0, 0, 0, false }
 };
 
@@ -102,6 +102,18 @@ const unsigned char* Memo::ParseField(const unsigned char *begin,
 			}
 		}
 	}
+	// handle special cases
+	switch( field->type )
+	{
+	case MEMFC_CATEGORY:
+		{
+			std::string catstring = ParseFieldString(field);
+			if( ic )
+				catstring = ic->FromBB(catstring);
+			CategoryStr2List(catstring,Categories);
+		}
+		return begin;
+	}
 
 	// if still not handled, add to the Unknowns list
 	UnknownField uf;
@@ -144,6 +156,14 @@ void Memo::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
 	// tack on the 'm' memo type field first
 	BuildField(data, offset, MEMFC_MEMO_TYPE, 'm');
 
+	// Categories
+
+	if(Categories.size()>0) {
+		string store;
+		CategoryList2Str(Categories, store);
+		BuildField(data, offset, MEMFC_CATEGORY, store);
+	}
+
 	// cycle through the type table
 	for(	FieldLink<Memo> *b = MemoFieldLinks;
 		b->type != MEMFC_END;
@@ -184,7 +204,6 @@ void Memo::Dump(std::ostream &os) const
 	   << " (" << (unsigned int)RecType << ")\n";
 	os << "    Title: " << Title << "\n";
 	os << "    Body: " << Body << "\n";
-	os << "    Category: " << Category << "\n";
 
 	os << Unknowns;
 	os << "\n\n";
@@ -194,12 +213,62 @@ void Memo::Clear()
 {
 	Title.clear();
 	Body.clear();
-	Category.clear();
+	Categories.clear();
 
 	MemoType = 0;
 
 	Unknowns.clear();
 }
 
+void Memo::CategoryStr2List(const std::string &str,Barry::CategoryList &list)
+{
+	// start fresh
+	
+	list.clear();
+
+	if( !str.size() )
+		return;
+
+	// parse the comma-delimited string to a list, stripping away
+	// any white space around each category name
+	string::size_type start = 0, end = 0, delim = str.find(',', start);
+	while( start != string::npos ) {
+		if( delim == string::npos )
+			end = str.size() - 1;
+		else
+			end = delim - 1;
+
+		// strip surrounding whitespace
+		while( str[start] == ' ' )
+			start++;
+		while( end && str[end] == ' ' )
+			end--;
+
+		if( start <= end ) {
+			string token = str.substr(start, end-start+1);
+			list.push_back(token);
+		}
+
+		// next
+		start = delim;
+		if( start != string::npos )
+			start++;
+		delim = str.find(',', start);
+	}
+}
+
+void Memo::CategoryList2Str(const Barry::CategoryList &list, std::string &str)
+{
+	str.clear();
+
+	Barry::CategoryList::const_iterator i = list.begin();
+	for( ; i != list.end(); ++i ) {
+		if( str.size() )
+			str += ",";
+		str += *i;
+	}
+}
+
+
 } // namespace Barry
 
diff --git a/src/r_memo.h b/src/r_memo.h
index 0652a38..3b6c2e4 100644
--- a/src/r_memo.h
+++ b/src/r_memo.h
@@ -34,6 +34,8 @@ namespace Barry {
 // forward declarations
 class IConverter;
 
+typedef std::vector<std::string> CategoryList;
+
 class BXEXPORT Memo
 {
 public:
@@ -45,7 +47,7 @@ public:
 	uint8_t MemoType;
 	std::string Title;
 	std::string Body;
-	std::string Category;
+	CategoryList Categories;
 
 	UnknownsType Unknowns;
 
@@ -53,6 +55,14 @@ public:
 	const unsigned char* ParseField(const unsigned char *begin,
 		const unsigned char *end, const IConverter *ic = 0);
 
+protected:
+	// these two are common with Contact and duplicated. TODO: Should really make them
+	// functions, or hive them off up the object hierarchy. But there is no hierarchy 
+
+	static void CategoryStr2List(const std::string &str, Barry::CategoryList &list);
+	static void CategoryList2Str(const Barry::CategoryList &list, std::string &str);
+
+
 public:
 	Memo();
 	~Memo();
diff --git a/src/r_task.cc b/src/r_task.cc
index cc821ae..dffadc1 100644
--- a/src/r_task.cc
+++ b/src/r_task.cc
@@ -105,7 +105,7 @@ static FieldLink<Task> TaskFieldLinks[] = {
    { TSKFC_START_TIME, "Start Time",  0, 0, 0, 0, &Task::StartTime, 0, 0, false },
    { TSKFC_DUE_TIME,   "Due Time",    0, 0, 0, 0, &Task::DueTime, 0, 0, false },
    { TSKFC_ALARM_TIME, "Alarm Time",  0, 0, 0, 0, &Task::AlarmTime, 0, 0, false },
-   { TSKFC_CATEGORIES, "Categories",  0, 0, &Task::Categories, 0, 0, 0, 0, false },
+//   { TSKFC_CATEGORIES, "Categories",  0, 0, &Task::Categories, 0, 0, 0, 0, false },
    { TSKFC_END,        "End of List", 0, 0, 0, 0, 0, 0, 0, false },
 };
 
@@ -202,8 +202,16 @@ const unsigned char* Task::ParseField(const unsigned char *begin,
 			AlarmType = AlarmProto2Rec(field->u.raw[0]);
 		}
 		return begin;
+		
+	case TSKFC_CATEGORIES:
+		{
+			std::string catstring = ParseFieldString(field);
+			if( ic )
+				catstring = ic->FromBB(catstring);
+			CategoryStr2List(catstring,Categories);
+		}
+		return begin;
 	}
-
 	// base class handles recurring data
 	if( RecurBase::ParseField(field->type, field->u.raw, btohs(field->size), ic) )
 		return begin;
@@ -287,6 +295,14 @@ void Task::BuildFields(Data &data, size_t &offset, const IConverter *ic) const
 			}
 		}
 	}
+	
+	// Categories
+
+	if(Categories.size()>0) {
+		string store;
+		CategoryList2Str(Categories, store);
+		BuildField(data, offset, TSKFC_CATEGORIES, store);
+	}
 
 	// and finally save unknowns
 	UnknownsType::const_iterator
@@ -432,5 +448,55 @@ void Task::Dump(std::ostream &os) const
 	os << "\n\n";
 }
 
+void Task::CategoryStr2List(const std::string &str,Barry::CategoryList &list)
+{
+	// start fresh
+	
+	list.clear();
+
+	if( !str.size() )
+		return;
+
+	// parse the comma-delimited string to a list, stripping away
+	// any white space around each category name
+	string::size_type start = 0, end = 0, delim = str.find(',', start);
+	while( start != string::npos ) {
+		if( delim == string::npos )
+			end = str.size() - 1;
+		else
+			end = delim - 1;
+
+		// strip surrounding whitespace
+		while( str[start] == ' ' )
+			start++;
+		while( end && str[end] == ' ' )
+			end--;
+
+		if( start <= end ) {
+			string token = str.substr(start, end-start+1);
+			list.push_back(token);
+		}
+
+		// next
+		start = delim;
+		if( start != string::npos )
+			start++;
+		delim = str.find(',', start);
+	}
+}
+
+void Task::CategoryList2Str(const Barry::CategoryList &list, std::string &str)
+{
+	str.clear();
+
+	Barry::CategoryList::const_iterator i = list.begin();
+	for( ; i != list.end(); ++i ) {
+		if( str.size() )
+			str += ",";
+		str += *i;
+	}
+}
+
+
 } // namespace Barry
 
diff --git a/src/r_task.h b/src/r_task.h
index c6b9a54..41decb1 100644
--- a/src/r_task.h
+++ b/src/r_task.h
@@ -35,6 +35,8 @@ namespace Barry {
 // forward declarations
 class IConverter;
 
+typedef std::vector<std::string> CategoryList;
+
 class BXEXPORT Task : public RecurBase
 {
 public:
@@ -46,7 +48,7 @@ public:
 	uint8_t TaskType;
 	std::string Summary;
 	std::string Notes;
-	std::string Categories;
+	CategoryList Categories;
 	std::string UID;
 
 	time_t StartTime;
@@ -95,6 +97,12 @@ protected:
 
 	static StatusFlagType StatusProto2Rec(uint8_t s);
 	static uint8_t StatusRec2Proto(StatusFlagType s);
+	
+	// these two are common with Contact and duplicated. TODO: Should really make them
+	// functions, or hive them off up the object hierarchy. But there is no hierarchy 
+
+	static void CategoryStr2List(const std::string &str, Barry::CategoryList &list);
+	static void CategoryList2Str(const Barry::CategoryList &list, std::string &str);
 
 public:
 	Task();
diff --git a/src/record.cc b/src/record.cc
index ffce4c0..335cd71 100644
--- a/src/record.cc
+++ b/src/record.cc
@@ -620,9 +620,10 @@ void Date::ToTm(struct tm *timep) const
 std::string Date::ToYYYYMMDD() const
 {
 	std::ostringstream oss;
-	oss	<< setw(4) << Year
-		<< setw(2) << Month + 1
-		<< setw(2) << Day;
+	// setfill and setw not sticky.
+	oss	<< setw(4) << setfill('0') << Year
+		<< setw(2) << setfill('0') << Month + 1
+		<< setw(2) << setfill('0') << Day;
 	return oss.str();
 }
 
@@ -635,9 +636,10 @@ std::string Date::ToYYYYMMDD() const
 std::string Date::ToBBString() const
 {
 	std::ostringstream oss;
-	oss	<< setw(2) << Day
-		<< Month + 1
-		<< Year;
+	// setw() ain't 'sticky'!
+	oss	<< setw(2) << setfill('0') << Day << '/'
+		<< setw(2) << setfill('0') << Month + 1 << '/'
+		<< setw(2) << setfill('0') << Year;
 	return oss.str();
 }
 
------------------------------------------------------------------------------
The NEW KODAK i700 Series Scanners deliver under ANY circumstances! Your
production scanning environment may not be a perfect world - but thanks to
Kodak, there's a perfect scanner to get the job done! With the NEW KODAK i700
Series Scanner you'll get full speed at 300 dpi even with all image 
processing features enabled. http://p.sf.net/sfu/kodak-com
_______________________________________________
Barry-devel mailing list
Barry-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/barry-devel

Reply via email to