From 7ae1412df3d682f639683e751f46a3f07be16d0b Mon Sep 17 00:00:00 2001 From: caballero Date: Tue, 28 Apr 2009 16:02:28 +0000 Subject: [PATCH] Added synchronization --- src2/CMakeLists.txt | 2 +- src2/creaImageIOGimmick.cpp | 38 ++- src2/creaImageIOGimmick.h | 8 +- src2/creaImageIOGimmickView.cpp | 5 +- src2/creaImageIOGimmickView.h | 6 +- src2/creaImageIOSQLiteTreeHandler.cpp | 4 - src2/creaImageIOSynchron.cpp | 327 +++++++++++++++++++ src2/creaImageIOSynchron.h | 94 ++++++ src2/creaImageIOTimestampDatabaseHandler.cpp | 2 +- src2/creaImageIOTimestampDatabaseHandler.h | 2 +- src2/creaImageIOTreeHandlerImageAdder.cpp | 316 +++++++++++++++++- src2/creaImageIOTreeHandlerImageAdder.h | 33 +- src2/creaImageIOTreeNode.cpp | 2 +- src2/creaImageIOTreeView.cpp | 3 +- src2/creaImageIOTreeView.h | 12 +- src2/creaImageIOWxGimmickView.cpp | 65 +++- src2/creaImageIOWxGimmickView.h | 7 +- src2/creaImageIOWxTreeView.cpp | 9 +- src2/creaImageIOWxTreeView.h | 5 +- src2/icons/synchronize-48x48.png | Bin 0 -> 1993 bytes src2/icons/synchronize.xpm | 211 ++++++++++++ 21 files changed, 1091 insertions(+), 60 deletions(-) create mode 100644 src2/creaImageIOSynchron.cpp create mode 100644 src2/creaImageIOSynchron.h create mode 100644 src2/icons/synchronize-48x48.png create mode 100644 src2/icons/synchronize.xpm diff --git a/src2/CMakeLists.txt b/src2/CMakeLists.txt index 54b2ba8..fca3c0e 100644 --- a/src2/CMakeLists.txt +++ b/src2/CMakeLists.txt @@ -33,7 +33,7 @@ SET( SRCS # creaImageIOGimmick - creaImageIOSynchronizer + creaImageIOSynchron creaImageIOTimestampDatabaseHandler # Abstract views diff --git a/src2/creaImageIOGimmick.cpp b/src2/creaImageIOGimmick.cpp index a9080fd..9b8c924 100644 --- a/src2/creaImageIOGimmick.cpp +++ b/src2/creaImageIOGimmick.cpp @@ -35,7 +35,7 @@ namespace creaImageIO CreateUserSettingsDirectory(); // Sets the current directory to the home dir mCurrentDirectory = GetHomeDirectory(); - mSynchronizer=0; + mSynchronizer= new Synchronizer(GetUserSettingsDirectory()); std::string dbpath = GetLocalDatabasePath(); // Create or open local database @@ -276,8 +276,9 @@ namespace creaImageIO mImageAdder.SetTreeHandler(GetTreeHandler(d)); mImageAdder.SetTimestampHandler(mTimestampDatabase); + mImageAdder.SetSynchronizer(mSynchronizer); mImageAdder.AddFiles(filenames); - + } //======================================================================== @@ -292,28 +293,31 @@ namespace creaImageIO TreeHandler * handler=GetTreeHandler(d); mImageAdder.SetTreeHandler(handler); mImageAdder.SetTimestampHandler(mTimestampDatabase); - mImageAdder.AddDirectory(f,recurse); - //Synchronize(true, handler); - + mImageAdder.SetSynchronizer(mSynchronizer); + mImageAdder.AddDirectory(f,recurse); } //======================================================================== //======================================================================== - - void Gimmick::Synchronize(bool update, TreeHandler* handler) + void Gimmick::RemoveFile(const std::string& d, + const tree::Node* & node) { - GimmickMessage(4,"Synchronizing. Update:"<SetTreeHandler(handler); - } - mSynchronizer->Synchronize(update); + mImageAdder.SetSynchronizer(mSynchronizer); + mTimestampDatabase->RemoveNode("PATH",node); + mImageAdder.RemoveFile(node); + } + //======================================================================== + //======================================================================== + + std::string Gimmick::Synchronize(bool repair, bool checkAttributes) + { + TreeHandler * handler=GetTreeHandler("Local database"); + mImageAdder.SetTreeHandler(handler); + mImageAdder.SetTimestampHandler(mTimestampDatabase); + mImageAdder.SetSynchronizer(mSynchronizer); + return mImageAdder.Synchronize(repair, checkAttributes); } //======================================================================== diff --git a/src2/creaImageIOGimmick.h b/src2/creaImageIOGimmick.h index fa39035..f93bac1 100644 --- a/src2/creaImageIOGimmick.h +++ b/src2/creaImageIOGimmick.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace creaImageIO { @@ -91,9 +91,13 @@ namespace creaImageIO void AddDir(const std::string& handler, const std::string& path, bool recurse); + /// Removes a file from the given TreeHandler + void RemoveFile(const std::string& d, + const tree::Node* & filename); + ///Synchronizes the loaded data with the database. If remove is true the database will be updated, otherwise ///only a warning sign will be issued - void Synchronize(bool update, TreeHandler* handler); + std::string Synchronize(bool repair, bool checkAttributes); /// Prints the tree handled by the handler void Print(const std::string& handler); diff --git a/src2/creaImageIOGimmickView.cpp b/src2/creaImageIOGimmickView.cpp index f7e894d..945b091 100644 --- a/src2/creaImageIOGimmickView.cpp +++ b/src2/creaImageIOGimmickView.cpp @@ -109,15 +109,14 @@ namespace creaImageIO i!= mGimmick->GetTreeHandlerMap().end(); ++i) { - this->CreateTreeView(i->second, mGimmick->GetTimestampDatabase()); + this->CreateTreeView(i->second); } } /// Create a tree view with a given name void GimmickView::CreateSingleTreeView(std::string &i_name) { - this->CreateTreeView(mGimmick->GetTreeHandlerMap()[i_name], - mGimmick->GetTimestampDatabase()); + this->CreateTreeView(mGimmick->GetTreeHandlerMap()[i_name]); } diff --git a/src2/creaImageIOGimmickView.h b/src2/creaImageIOGimmickView.h index 3474715..ec888e3 100644 --- a/src2/creaImageIOGimmickView.h +++ b/src2/creaImageIOGimmickView.h @@ -78,6 +78,10 @@ namespace creaImageIO virtual void ClearSelection() { GimmickError("INTERNAL ERROR : ClearSelection not implemented"); } + + ///Adds a file to ignore + virtual void AddIgnoreFile(const tree::Node* & toRemove) + { GimmickError("INTERNAL ERROR : AddIgnoreFile not implemented"); } ///Validates the dimension compliance of the images with the maximum and minimum given, and between their sizes @@ -104,7 +108,7 @@ namespace creaImageIO void CreateSingleTreeView(std::string &i_name); /// Create the tree view for TreeHandler provided - virtual void CreateTreeView( TreeHandler*, TimestampDatabaseHandler* ) + virtual void CreateTreeView( TreeHandler* ) { GimmickError("INTERNAL ERROR : CreateTreeView not implemented"); } /// Updates the TreeView of given name from level l to bottom diff --git a/src2/creaImageIOSQLiteTreeHandler.cpp b/src2/creaImageIOSQLiteTreeHandler.cpp index 0a953fa..14bb3db 100644 --- a/src2/creaImageIOSQLiteTreeHandler.cpp +++ b/src2/creaImageIOSQLiteTreeHandler.cpp @@ -169,10 +169,6 @@ namespace creaImageIO // std::cout << "DELETE OK"< +#include +#include + +namespace fs = boost::filesystem; + +//===================================================================== + + +namespace creaImageIO +{ + + //===================================================================== + #define QUERYSYNCDB(QUER,RES) \ + try \ + { \ + RES = mDB->execQuery(QUER.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite query '"<execDML(UP.c_str()); \ + } \ + catch (CppSQLite3Exception& e) \ + { \ + GimmickError("SQLite update '"<open(pathDB.c_str()); + } + catch (CppSQLite3Exception& e) + { + GimmickError("Opening '"<open(pathDB.c_str()); + // CREATING TABLES + std::string command; + command = "CREATE TABLE "; + command += "ADD_OPS"; + command += "\n(\nADD_KEY INTEGER PRIMARY KEY"; + command += ",\nPATH text"; + command += ",\nRECURSIVE boolean"; + command += ",\nFILES_ADDED int"; + command += "\n)"; + UPDATESYNCDB(command); + + command = "CREATE TABLE "; + command += "IGNORED_FILES"; + command += "\n(\nID INTEGER PRIMARY KEY"; + command += ",\nADD_KEY integer"; + command += ",\nPATH text"; + command += ",\nREMOVE boolean"; + command += ",\nTIME datetext"; + command += "\n)"; + UPDATESYNCDB(command); + } + + //===================================================================== + void Synchronizer::CleanName(std::string& str) const + { + size_t pos; + do + { + pos = str.find('\\'); + if (pos!=-1) + { + str.replace(pos, 1, "/"); + } + } + while (pos!=-1); + } + + //===================================================================== + void Synchronizer::GetFileList(std::vector & list) + { + CleanList(); + list=mAddList; + } + + //===================================================================== + void Synchronizer::GetIgnoredFiles(const std::string& key, std::vector &ignoreList) + { + ignoreList=GetIgnoreList(key); + } + + //===================================================================== + void Synchronizer::UpdateAddList() + { + std::string query = "SELECT * FROM ADD_OPS"; + CppSQLite3Query res; + QUERYSYNCDB(query, res); + while (!res.eof()) + { + AddList temp = AddList(res); + mAddList.push_back(temp); + res.nextRow(); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // remove an entry of the DB + //@param i_table : table where do the remove + // @param i_key : the add_key reference (one entry to remove for ADD_OP table, many for IGNORED_FILES table + //@result : - + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::RemoveEntry(const std::string i_table, const std::string i_key) + { + std::string query = "DELETE FROM " + i_table + " WHERE ADD_KEY = '" + i_key +"'"; + UPDATESYNCDB(query); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // clean DataBase if an operation has no child anymore + // @result : - + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::CleanList() + { + mAddList.clear(); + UpdateAddList(); + std::vector::iterator it_add = mAddList.begin(); + for(;it_add nbFiles == "0") + { + RemoveEntry("ADD_OPS", it_add->key); + RemoveEntry("IGNORED_FILES", it_add->key); + + } + } + mAddList.clear(); + UpdateAddList(); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Inserts a new add operation in the database + // @param path: the path of the directory that was added + // @param recursive: shows if the action was called recursively or not + // @param nChildren: the number of files affected by the operation + // @result : The operation has been added + ///////////////////////////////////////////////////////////////////////////////////////////////// + void Synchronizer::InsertAddOp(const std::string& path, const std::string& recursive, const std::string& nChildren) + { + std::string insert; + std::string pat=path.c_str(); + CleanName(pat); + insert="INSERT INTO ADD_OPS (PATH,RECURSIVE,FILES_ADDED) VALUES('"; + insert+=pat+"','"; + insert+=recursive+"',"; + insert+=nChildren+");"; + UPDATESYNCDB(insert); + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Inserts a new ignored file in the database + // @param add_key: the key of the add_op to which it corresponds + // @param path: the path of the directory that was added + // @param remove: shows if the file was removed or not + // @param time: the time in which the file was removed + // @result : The file has been inserted + ///////////////////////////////////////////////////////////////////////////////////////////////// + + void Synchronizer::InsertIgnoreFile(const std::string& addKey, const std::string& path, const std::string& remove, const std::string& time) + { + std::string pat=path.c_str(); + CleanName(pat); + std::string id=GetAttribute("ID","IGNORED_FILES","PATH",pat); + if(id.compare("")==0) + { + std::string insert; + insert="INSERT INTO IGNORED_FILES (ADD_KEY,PATH,REMOVE,TIME) VALUES('"; + insert+=addKey+"','"; + insert+=pat+"','"; + insert+=remove+"',"; + insert+=time+");"; + UPDATESYNCDB(insert); + } + else + { + //Gets the add key + std::string ak=GetAttribute("ADD_KEY","IGNORED_FILES","ID",id); + //Sets the new add key attribute for the file + SetAttribute("ADD_KEY","IGNORED_FILES",addKey,"ID", id); + //Sets the new remove attribute for the file + SetAttribute("REMOVE","IGNORED_FILES",remove,"ID", id); + //Sets the new time attribute for the file + SetAttribute("TIME","IGNORED_FILES",time,"ID", id); + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // get the files name to ignore for a add operation synchronization + // @param : the add key + //@result : list (path) of ignore files + ///////////////////////////////////////////////////////////////////////////////////////////////// + std::vector Synchronizer::GetIgnoreList(const std::string &i_key) + { + mIgnoreList.clear(); + std::vector i_names; + std::string query = "SELECT * FROM IGNORED_FILES WHERE ADD_KEY = "; + query+=i_key; + CppSQLite3Query res; + QUERYSYNCDB(query, res); + while (!res.eof()) + { + RemoveList temp = RemoveList(res); + if(temp.remove.compare("0")==0) + { + mIgnoreList.push_back(temp); + } + res.nextRow(); + } + std::vector::iterator it; + + for(it = mIgnoreList.begin();it != mIgnoreList.end(); ++it) + { + i_names.push_back((*it).path); + } + return i_names; + } + + ///////////////////////////////////////////////////////////////////////////////////////////////// + // Gets the required attribute in the required table + // @param attribute: the attribute to look for + // @param table: the table to look in + // @param searchParam: the search parameter + // @param searchValue: the search value + // @result : required attribute + ///////////////////////////////////////////////////////////////////////////////////////////////// + std::string Synchronizer::GetAttribute(const std::string& attribute, + const std::string& table, + const std::string& searchParam, + const std::string& searchValue) + { + std::stringstream query; + std::string result; + std::string sVal=searchValue.c_str(); + CleanName(sVal); + query<<"SELECT "< +#include +#include +#include +#include "CppSQLite3.h" + +namespace creaImageIO +{ + using namespace std; + //================================================================================================================ + class AddList + { + public : + std::string key; + std::string path; + std::string recursive; + std::string nbFiles; + AddList(CppSQLite3Query& res): + key(res.getStringField(0)), + path(res.getStringField(1)), + recursive(res.getStringField(2)), + nbFiles(res.getStringField(3)) + {} + }; + //================================================================================================================ + + //================================================================================================================ + class RemoveList + { + public : + std::string key; + std::string path; + std::string remove; + std::string time; + + RemoveList(CppSQLite3Query& res): + key(res.getStringField(1)), + path(res.getStringField(2)), + remove(res.getStringField(3)), + time(res.getStringField(4)) + {} + }; + //================================================================================================================ + + //================================================================================================================ + class Synchronizer + { + public: + Synchronizer(const std::string& path); + virtual ~Synchronizer(); + void Initialize(); + void InsertAddOp(const std::string& path, + const std::string& recursive, + const std::string& nChildren); + void InsertIgnoreFile(const std::string& addKey, + const std::string& path, + const std::string& remove, + const std::string& time); + void RemoveEntry(const std::string i_table, const std::string i_key); + void GetFileList(std::vector& files); + void GetIgnoredFiles(const std::string& key, std::vector &ignoreList); + std::string GetAttribute(const std::string& attribute, + const std::string& table, + const std::string& searchParam, + const std::string& searchValue); + void SetAttribute(const std::string& attribute, + const std::string& table, + const std::string& value, + const std::string& searchParam, + const std::string& searchValue); + std::vector mAddList; + std::vector mIgnoreList; + private : + + /// The DB + CppSQLite3DB* mDB; + std::string pathDB; + void CreateDB(); + void UpdateAddList(); + void CleanList(); + void CleanName(std::string& str) const; + + std::vector GetIgnoreList(const std::string &i_key); + + }; + //================================================================================================================ + + +} +// EOF +#endif \ No newline at end of file diff --git a/src2/creaImageIOTimestampDatabaseHandler.cpp b/src2/creaImageIOTimestampDatabaseHandler.cpp index 9ee54b3..a05bc26 100644 --- a/src2/creaImageIOTimestampDatabaseHandler.cpp +++ b/src2/creaImageIOTimestampDatabaseHandler.cpp @@ -322,7 +322,7 @@ namespace creaImageIO } //===================================================================== - void TimestampDatabaseHandler::RemoveNode(const std::string& searchAtt, tree::Node* node) + void TimestampDatabaseHandler::RemoveNode(const std::string& searchAtt, const tree::Node* node) { int n=node->GetNumberOfChildren(); if(n>0) diff --git a/src2/creaImageIOTimestampDatabaseHandler.h b/src2/creaImageIOTimestampDatabaseHandler.h index ce01556..2a6d78e 100644 --- a/src2/creaImageIOTimestampDatabaseHandler.h +++ b/src2/creaImageIOTimestampDatabaseHandler.h @@ -61,7 +61,7 @@ namespace creaImageIO const std::string& searchParam, const std::string& searchValue); ///Removes the given node - void RemoveNode(const std::string& searchAtt, tree::Node* node); + void RemoveNode(const std::string& searchAtt, const tree::Node* node); ///Removes the filename with the given pathname void RemoveFile(const std::string& searchAtt, const std::string& searchVal); ///Cleans the path name diff --git a/src2/creaImageIOTreeHandlerImageAdder.cpp b/src2/creaImageIOTreeHandlerImageAdder.cpp index f68fa77..679e156 100644 --- a/src2/creaImageIOTreeHandlerImageAdder.cpp +++ b/src2/creaImageIOTreeHandlerImageAdder.cpp @@ -11,7 +11,7 @@ using boost::next; using boost::prior; -//using namespace crea; +using namespace crea; namespace creaImageIO { @@ -45,7 +45,7 @@ namespace creaImageIO void TreeHandlerImageAdder::AddFiles( const std::vector& filenames) { mProgress.Reset(); - + unsigned int nbf = filenames.size(); std::vector::const_iterator i; for (i=filenames.begin();i!=filenames.end();++i) @@ -55,6 +55,11 @@ namespace creaImageIO if (IsHandledFile(*i)) { mProgress.IncNumberHandledFiles(); + mSynchronizer->InsertAddOp((*i),"0","1"); + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",(*i)); + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey,(*i),"0",removedOn.str()); AddFile(*i); } mProgressSignal(mProgress); @@ -68,7 +73,17 @@ namespace creaImageIO bool recurse) { mProgress.Reset(); - AddDirectoryRecursor( directory, recurse ); + std::stringstream files; + + std::stringstream rec; + rec<InsertAddOp(directory,rec.str(),"0"); + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","ADD_OPS","PATH",directory); + AddDirectoryRecursor( directory, recurse, addKey ); + + int nFiles=GetProgress().GetNumberAddedFiles(); + files<SetAttribute("FILES_ADDED","ADD_OPS",files.str(),"ADD_KEY",addKey); GimmickDebugMessage(3,mProgress<GetNumberOfChildren(); + if(n>0) + { + RemoveFiles(node->GetChildrenList()); + } + else + { + std::string path=node->GetAttribute("FullFileName"); + //Gets the add key + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey)).c_str()); + files=files-1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey); + //Sets the file as removed + mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH",path); + } + } + + //===================================================================== + + void TreeHandlerImageAdder::RemoveFiles(const std::vector& nodes) + { + std::vector::const_iterator it; + for(it=nodes.begin();it!=nodes.end();++it) + { + int n=(*it)->GetNumberOfChildren(); + if(n>0) + { + RemoveFiles((*it)->GetChildrenList()); + } + else + { + std::string path=(*it)->GetAttribute("FullFileName"); + //Gets the add key + std::string addKey=mSynchronizer->GetAttribute("ADD_KEY","IGNORED_FILES","PATH",path); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",addKey)).c_str()); + files=files-1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",addKey); + //Sets the file as removed + mSynchronizer->SetAttribute("REMOVE","IGNORED_FILES","1","PATH",path); + } + + } + } + //===================================================================== void TreeHandlerImageAdder::AddDirectoryRecursor(const std::string &dirpath, - bool recursive) + bool recursive, + const std::string &addKey) { GimmickDebugMessage(4,"Scanning '"<string(), recursive); + AddDirectoryRecursor( itr->string(), recursive, addKey); } } else @@ -125,6 +196,9 @@ namespace creaImageIO AddFile( itr->string() ); mTreeHandler->GetTopLevelNodeId("FullFileName",itr->string(),parent_id); mTimestampHandler->SetAttribute("TopLevelNodeId",parent_id,"PATH",itr->string()); + std::stringstream removedOn; + removedOn<InsertIgnoreFile(addKey, itr->string(),"0",removedOn.str()); } mProgressSignal(mProgress); if (mProgress.GetStop()) @@ -139,6 +213,238 @@ namespace creaImageIO } //======================================================================= + + //======================================================================= + + void TreeHandlerImageAdder::CheckSyncDirectory(const std::string &dirpath, + bool recursive, + bool repair, + bool checkAttributes, + std::vector &i_ignorefiles, + std::vector & attsModified, + std::vector & newfiles) + { + if ( !fs::exists( dirpath ) ) return; + fs::directory_iterator end_itr; // default construction yields past-the-end + + for ( fs::directory_iterator itr( dirpath ); itr != end_itr; ++itr ) + { + // If is directory & recurse : do recurse + if ( fs::is_directory(itr->status()) ) + { + if (recursive) + { + CheckSyncDirectory( itr->string(), recursive, repair, checkAttributes, i_ignorefiles, attsModified, newfiles); + } + } + else + { + if (IsHandledFile(itr->string())) + { + bool bfound = false; + for(std::vector::iterator it_new = i_ignorefiles.begin(); it_new < i_ignorefiles.end(); ++it_new) + { + if((*it_new) == itr->string()) + { + bfound = true; + //Additional checking of attributes + if(checkAttributes) + { + CheckAttributes(repair,(*it_new),attsModified); + } + i_ignorefiles.erase(it_new); + break; + } + } + if(!bfound && i_ignorefiles.size()>0 ) + { + newfiles.push_back( itr->string() ); + } + } + } + } + } + + //======================================================================= + + //======================================================================= + + std::string TreeHandlerImageAdder::Synchronize(bool repair, bool checkAttributes) + { + std::vector fileList; + std::vector ignoreList; + std::vector newFiles; + std::vector attsModified; + std::stringstream mess; + std::vector::iterator iter; + + //Gets the list of added files + mSynchronizer->GetFileList(fileList); + + std::vector::iterator i; + //Actions to take if the user doesn't want to repair + if(!repair) + { + //Iterates to see if they are in sync + for(iter=fileList.begin();iter!=fileList.end();++iter) + { + mSynchronizer->GetIgnoredFiles((*iter).key,ignoreList); + bool rec=true; + if((*iter).recursive=="0"){rec=false;} + CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles); + } + + //Add to message the result of new found files + mess<<"New Files Found: "<0) + { + mess<<"Filenames: "<0) + { + mess<<"Filenames: "<0) + { + mess<<"Filenames: "<GetIgnoredFiles((*iter).key,ignoreList); + bool rec=true; + if((*iter).recursive=="0"){rec=false;} + CheckSyncDirectory((*iter).path,rec,repair,checkAttributes,ignoreList,attsModified,newFiles); + + //For the new files, add them + for (i=newFiles.begin();i!=newFiles.end();++i) + { + mTimestampHandler->AddFile((*i), fs::last_write_time(*i), time(0)); + if (IsHandledFile(*i)) + { + std::stringstream removedOn; + removedOn<InsertIgnoreFile((*iter).key,(*i),"0",removedOn.str()); + //Gets the number of files added + int files=atoi((mSynchronizer->GetAttribute("FILES_ADDED","ADD_OPS","ADD_KEY",(*iter).key)).c_str()); + files=files+1; + std::stringstream out; + out<SetAttribute("FILES_ADDED","ADD_OPS",out.str(),"ADD_KEY",(*iter).key); + AddFile(*i); + } + } + nf+=newFiles.size(); + newFiles.clear(); + + } + //Reports number of added files + mess<<"Files Added: "<0) + { + tree::Node* node; + mTreeHandler->LoadChildren(NULL,4); + for(i=ignoreList.begin();i!=ignoreList.end();++i) + { + FindNode(mTreeHandler->GetTree().GetChildrenList()[0],3,"FullFileName",*i,node); + RemoveFile(node); + mTreeHandler->Remove(node); + } + } + mess<<"Files Removed: "<& attsModified) + { + std::map< std::string, std::string> attr; + mTreeHandler->GetTree().GetDescriptor().BuildAttributeMap(attr); + mReader.ReadAttributes(file,attr); + tree::LevelDescriptor::AttributeDescriptorListType adl= mTreeHandler->GetTree().GetAttributeDescriptorList(mTreeHandler->GetTree().GetNumberOfLevels()-1); + tree::LevelDescriptor::AttributeDescriptorListType::const_iterator a; + for (a = adl.begin();a!=adl.end();++a) + { + std::string databaseVal; + mTreeHandler->GetAttribute("Image","FullFileName",file,a->GetKey(),databaseVal); + std::string fileVal=attr.find(a->GetKey())->second; + if ( a->GetFlags()==0 && databaseVal.compare(fileVal)!=0 ) + { + if(repair) + { + mTreeHandler->SetAttribute("Image",a->GetKey(),fileVal,"FullFileName", file); + } + attsModified.push_back(file); + } + + } + + } + + //======================================================================= + + + void TreeHandlerImageAdder::FindNode(tree::Node* parent, int level, const std::string& searchParam, const std::string& searchVal, tree::Node*& node) + { + if(level>1) + { + std::vector::iterator iter; + for(iter=parent->GetChildrenList().begin();iter!=parent->GetChildrenList().end();++iter) + { + FindNode(*iter,level-1,searchParam,searchVal,node); + } + } + else + { + if(parent->GetAttribute(searchParam).compare(searchVal)==0) + { + node=parent; + } + + } + } + + //======================================================================= diff --git a/src2/creaImageIOTreeHandlerImageAdder.h b/src2/creaImageIOTreeHandlerImageAdder.h index e490509..c3024dd 100644 --- a/src2/creaImageIOTreeHandlerImageAdder.h +++ b/src2/creaImageIOTreeHandlerImageAdder.h @@ -3,9 +3,11 @@ #include #include +#include #include #include #include +#include // Signal/slot mechanism for progress events #include #include @@ -22,6 +24,7 @@ namespace creaImageIO class TreeHandlerImageAdder { + public: //==================================================================== /// Ctor @@ -32,6 +35,8 @@ namespace creaImageIO void SetTreeHandler(TreeHandler* tree) { mTreeHandler = tree;} /// Sets the TimestampDatabaseHandler void SetTimestampHandler(TimestampDatabaseHandler* tdh) { mTimestampHandler = tdh;} + /// Sets the synchronizer + void SetSynchronizer(Synchronizer* s){mSynchronizer=s;} //==================================================================== //==================================================================== @@ -101,7 +106,29 @@ namespace creaImageIO void AddFiles( const std::vector& filename ); /// (Recursively) adds the files of a directory to the TreeHandler void AddDirectory( const std::string& directory, - bool recurse ); + bool recurse); + /// Removes a file from the databases + void RemoveFile(const tree::Node*& node); + /// Removes files from the databases + void RemoveFiles(const std::vector& nodes); + /// Synchronizes the DB and disk by repeating the operations the user has done and returns a report + std::string Synchronize(bool repair, bool checkAttributes); + ///Recursively checks if the directory is synchronized and optionally the state of the attributes + void CheckSyncDirectory(const std::string &dirpath, + bool recursive, + bool repair, + bool checkAttributes, + std::vector &i_ignorefiles, + std::vector & attsModified, + std::vector & newfiles); + ///Finds the node that matches the specified parameters + void FindNode(tree::Node* parent, int level, + const std::string& searchParam, + const std::string& searchVal, + tree::Node*& node); + ///Checks the attributes of the database against the ones in disk + void CheckAttributes(bool repair, std::string& file, std::vector& attsModified); + //==================================================================== private: @@ -113,10 +140,12 @@ namespace creaImageIO /// Recursive method which does the main job for AddDirectory void AddDirectoryRecursor( const std::string& directory, - bool recurse ); + bool recurse, + const std::string &addKey); TreeHandler* mTreeHandler; TimestampDatabaseHandler* mTimestampHandler; + Synchronizer* mSynchronizer; ImageReader mReader; Progress mProgress; diff --git a/src2/creaImageIOTreeNode.cpp b/src2/creaImageIOTreeNode.cpp index faa0bf0..a8cf792 100644 --- a/src2/creaImageIOTreeNode.cpp +++ b/src2/creaImageIOTreeNode.cpp @@ -9,7 +9,7 @@ namespace creaImageIO { //============================================================= - /// Ctor with parent Why does it enter once while charging? + /// Ctor with parent Node::Node(Node* parent) : mParent(parent), mData(0), diff --git a/src2/creaImageIOTreeView.cpp b/src2/creaImageIOTreeView.cpp index 42d4fd3..2fa1475 100644 --- a/src2/creaImageIOTreeView.cpp +++ b/src2/creaImageIOTreeView.cpp @@ -4,9 +4,8 @@ namespace creaImageIO { // CTor - TreeView::TreeView(TreeHandler* handler, TimestampDatabaseHandler* tdh, GimmickView* gimmick ) + TreeView::TreeView(TreeHandler* handler, GimmickView* gimmick ) : mTreeHandler(handler), - mTimestampDatabaseHandler (tdh), mGimmickView(gimmick) { GimmickDebugMessage(1,"TreeView::TreeView" diff --git a/src2/creaImageIOTreeView.h b/src2/creaImageIOTreeView.h index c5fab53..df92b14 100644 --- a/src2/creaImageIOTreeView.h +++ b/src2/creaImageIOTreeView.h @@ -23,7 +23,7 @@ namespace creaImageIO { public: /// Ctor - TreeView(TreeHandler*,TimestampDatabaseHandler*,GimmickView*); + TreeView(TreeHandler*,GimmickView*); /// Virtual destructor virtual ~TreeView(); @@ -40,6 +40,9 @@ namespace creaImageIO virtual void ValidateSelectedImages() { GimmickError("INTERNAL ERROR : TreeView::ValidateSelected not overloaded");} + ///Returns the last selected level + virtual unsigned int GetLastSelectedLevel(){GimmickError("INTERNAL ERROR : TreeView::GetLastSelectedLevel not overloaded");} + ///Returns the maximum number of levels virtual int GetNumberOfLevels(){ GimmickError("INTERNAL ERROR : TreeView::GetLevels not overloaded"); } ///Gets the current selections filenames @@ -53,15 +56,12 @@ namespace creaImageIO protected: TreeHandler* GetTreeHandler() { return mTreeHandler; } - TimestampDatabaseHandler* GetTimestampDatabaseHandler() { return mTimestampDatabaseHandler; } - GimmickView* GetGimmickView() { return mGimmickView; } + GimmickView* GetGimmickView() { return mGimmickView; } private: /// The TreeHandler with which it corresponds TreeHandler* mTreeHandler; - /// The Timestamp handler with which it corresponds - TimestampDatabaseHandler* mTimestampDatabaseHandler; - /// The GimmickView which holds the TreeView + /// The GimmickView which holds the TreeView GimmickView* mGimmickView; }; diff --git a/src2/creaImageIOWxGimmickView.cpp b/src2/creaImageIOWxGimmickView.cpp index 024bc8c..9f5a53f 100644 --- a/src2/creaImageIOWxGimmickView.cpp +++ b/src2/creaImageIOWxGimmickView.cpp @@ -10,8 +10,10 @@ using namespace crea; #include "icons/remove.xpm" #include "icons/database-add.xpm" #include "icons/help.xpm" +#include "icons/synchronize.xpm" #include +#include #include namespace creaImageIO @@ -25,13 +27,14 @@ namespace creaImageIO TOOL_ADDDIR_ID = 2, TOOL_REMOVE_ID = 3, TOOL_ADDDATABASE_ID = 4, - TOOL_HELP_ID = 5 + TOOL_HELP_ID = 5, + TOOL_SYNCHRONIZE_ID = 6 }; //====================================================================== //================================================================ // - const int icon_number = 7; + const int icon_number = 8; // Icon ids typedef enum { @@ -41,7 +44,8 @@ namespace creaImageIO Icon_page_down, Icon_remove, Icon_database_add, - Icon_help + Icon_help, + Icon_synchronize } icon_id; //================================================================ @@ -197,6 +201,11 @@ namespace creaImageIO mIcon->GetBitmap(Icon_help), _T("Open help window") ); + mToolSynchronize = mToolBar->AddTool( TOOL_SYNCHRONIZE_ID, + _T("Synchronize"), + mIcon->GetBitmap(Icon_synchronize), + _T("Synchronizes the database with disk") + ); //const wxBitmap& bitmap1, const wxString& shortHelpString = "", wxItemKind kind = wxITEM_NORMAL) mToolBar->Realize(); @@ -206,13 +215,13 @@ namespace creaImageIO //====================================================================== /// Create the tree view for TreeHandler provided - void WxGimmickView::CreateTreeView( TreeHandler* h, TimestampDatabaseHandler* tdh) + void WxGimmickView::CreateTreeView( TreeHandler* h) { std::string name(h->GetTree().GetAttribute("Name")); GimmickMessage(2,"Creating the tree view for '"<< name<<"'"<RemoveSelected(); - ClearSelection(); } //================================================= + + //================================================= + void WxGimmickView::AddIgnoreFile(const tree::Node* & toRemove) + { + mGimmick->RemoveFile("Local database",toRemove); + GetTreeViewMap()["Local database"]->UpdateLevel(1); + } + + //================================================= + void WxGimmickView::OnSynchronize(wxCommandEvent& event) + { + wxBusyCursor busy; + const wxString choices[] = { _T("Check database for file deletion or addition and give a report."), + _T("Check database for file deletion or addition and attribute change and give a report."), + _T("Repair database (removes deleted files and adds new files)."), + _T("Repair database (removes deleted files, adds new files and resets changed attributes).") } ; + + wxSingleChoiceDialog dialog(this, + _T("Select one of the following synchronization actions:\n") + _T("Plase note that, due to the heavy amount of operations required, this action might take a while."), + _T("Synchronization Settings"), + WXSIZEOF(choices), choices); + + //dialog.SetSelection(0); + + if (dialog.ShowModal() == wxID_OK) + { + int sel=dialog.GetSelection(); + bool repair=false; + bool checkAttributes=false; + if(sel==2 || sel==3){repair=true;} + if(sel==1 || sel==3){checkAttributes=true;} + std::string mess=mGimmick->Synchronize(repair, checkAttributes); + wxMessageBox(std2wx(mess),_T("Synchronization result"),wxOK,this); + GetTreeViewMap()["Local database"]->UpdateLevel(1); + + } + } + //================================================= + //================================================= /// AddProgress Gimmick callback void WxGimmickView::OnAddProgress( Gimmick::AddProgress& p) @@ -682,6 +730,7 @@ namespace creaImageIO EVT_TOOL(TOOL_ADDFILES_ID, WxGimmickView::OnAddFiles) EVT_TOOL(TOOL_ADDDIR_ID, WxGimmickView::OnAddDir) EVT_TOOL(TOOL_REMOVE_ID, WxGimmickView::OnRemove) + EVT_TOOL(TOOL_SYNCHRONIZE_ID, WxGimmickView::OnSynchronize) EVT_TOOL(TOOL_ADDDATABASE_ID, WxGimmickView::OnAddDB) END_EVENT_TABLE() //================================================= diff --git a/src2/creaImageIOWxGimmickView.h b/src2/creaImageIOWxGimmickView.h index 7dd38c9..3f46561 100644 --- a/src2/creaImageIOWxGimmickView.h +++ b/src2/creaImageIOWxGimmickView.h @@ -59,6 +59,8 @@ namespace creaImageIO bool isSelection, int selection, bool mProcess); ///Stops the player void StopPlayer(){mViewer->StopPlayer();} + ///Adds a file to ignore + void AddIgnoreFile(const tree::Node* & toRemove); ///Resets the default image void ClearSelection(); @@ -73,7 +75,7 @@ namespace creaImageIO /// Create the tree view for TreeHandler provided /// (overloaded from GimmickView) - void CreateTreeView( TreeHandler*, TimestampDatabaseHandler* ); + void CreateTreeView( TreeHandler* ); private: @@ -88,6 +90,7 @@ namespace creaImageIO wxToolBarToolBase* mToolRemove; wxToolBarToolBase* mToolAddDatabase; wxToolBarToolBase* mToolHelp; + wxToolBarToolBase* mToolSynchronize; wxSplitterWindow* mSplitter; wxPanel* mBottomPanel; @@ -105,6 +108,8 @@ namespace creaImageIO void OnAddDir(wxCommandEvent& event); /// Callback for removing files void OnRemove(wxCommandEvent& event); + /// Callback for removing files + void OnSynchronize(wxCommandEvent& event); /// Display a message box with the last addition statistics void DisplayAddSummary(); diff --git a/src2/creaImageIOWxTreeView.cpp b/src2/creaImageIOWxTreeView.cpp index 73ff7c4..e644d5e 100644 --- a/src2/creaImageIOWxTreeView.cpp +++ b/src2/creaImageIOWxTreeView.cpp @@ -106,12 +106,11 @@ namespace creaImageIO //===================================================================== // CTor WxTreeView::WxTreeView(TreeHandler* handler, - TimestampDatabaseHandler* tdh, GimmickView* gimmick, wxWindow* parent, const wxWindowID id) : wxPanel(parent,id), - TreeView(handler, tdh, gimmick) + TreeView(handler, gimmick) { GimmickDebugMessage(1,"WxTreeView::WxTreeView" <RemoveNode("PATH",(*i)); - GetTreeHandler()->Remove(*i); + tree::Node* n= (tree::Node*)(*i); + GetTreeHandler()->LoadChildren((*i),4); + GetGimmickView()->AddIgnoreFile(n); + GetTreeHandler()->Remove(*i); } if(needRefresh && mLastLevel>1) diff --git a/src2/creaImageIOWxTreeView.h b/src2/creaImageIOWxTreeView.h index 9e2072e..c8697b6 100644 --- a/src2/creaImageIOWxTreeView.h +++ b/src2/creaImageIOWxTreeView.h @@ -24,7 +24,7 @@ namespace creaImageIO { public: /// Ctor - WxTreeView(TreeHandler*, TimestampDatabaseHandler*, GimmickView*, + WxTreeView(TreeHandler*, GimmickView*, wxWindow* parent, const wxWindowID id); /// Virtual destructor virtual ~WxTreeView(); @@ -36,6 +36,9 @@ namespace creaImageIO ///Removes selected nodes on given level virtual void RemoveSelected(); + ///Returns the last selected level + virtual unsigned int GetLastSelectedLevel(){return mLastLevel;} + /// Callback for item selection void OnItemSelected(wxListEvent& event); diff --git a/src2/icons/synchronize-48x48.png b/src2/icons/synchronize-48x48.png new file mode 100644 index 0000000000000000000000000000000000000000..b9dce6fd23bd7fe49b682e89c8a8f978ede9d51c GIT binary patch literal 1993 zcmV;)2R8VLP)FV00001b5ch_0Itp) z=>Px#1ZP1_K>z@;j|==^1poj5AY({UO#lFTCIA3{ga82g0001h=l}q9FaQARU;qF* zm;eA5aGbhPJOBUy7<5HgbW?9;ba!ELWdKlNX>N2bPDNB8b~7$BHmT?0B>(^iBS}O- zRA@u(T4_*IR}>aeTxzYYBoHL5Szb1hK*&p2vkHU}V^zeW)B$M=Rm6o3R=}!LrYbFJ z2Q9U>)~VKUtCl)bml_cltVO8{b=*L4L6pT7ktHF$j{yhhyN{O_(di7Cxj#bA{l4>^ zd*40hp2v_OgP*|&yqXAPWo3T`eJnmu*+b$m)d|t9%(7?RS>Y!O!R_9*Bufpe&zX{1j9=p}{ z{f%v*TZQ!=x6~KqtL7D^jDP7u6ehm}!6WA1eI3KTvs-8#vh%#JmKJ_A7IQ4~+l;rA zW#XMIGpz&9p4aI1(ElhB^-@FxE|LxnKIXrT)&AdzW6p^hJuK4moH#sZkb7=wVDku1 z1gDj5MNOi03WZ|mfC^C`SNwqIK-b@z!VjQHL=Nz)+=F4_Ssl(+*$Mu-^z`(B5g|V! zOk2!{MRo+lSb{nmJ>W`DW`vvM@pxqNJL0TD2FcH!U z_HX(5`NM6&gxZQqebm_ggpsMwq3fv-X)4eiFieh_ZiziKSxW`C*POJpG)I25`za_< z8@+aBZY+Ze(TCgLXLH+J8erIHrJ%&_Qz>${O2TQUrP@dk8c?2gLwx9!!9!*7HGb zZth6R1YjaKkw$}3q#nv~*Q;{QoH>q^IHI@erQqDRz`(^7zI&(ut5hmSFxcILe}}mX z#(}iBT8&F$^gZmEq&Zb4bRog^vC2;i=aB~MVHmJcI7(K=+sA39TiDO&RyL$8>E>p) z(yX0!&7c4*@y;jBDXZjeArcU=NrRFG>tkp%8lBX|XfE=U0X?4ka5srAcrOPl;Xs;` zU&A~OUD_v%wwX(2xRVBh80vosox-VeeLxf_ZmWy+?L*PzBad+BkUD`&7bk<$ z&0-TPkunFk=dxmoCKelxs!Q=k)DcxKW$=C`q6HEexbX!J6#oAkH#0NSuFOlM9z1tL z!9ZZcU@%a>{DXAvFuf?|6VXG@>WJ@E(F3szpkxp0Oj?j(+#9u>5-*lKq-WWSkTynp zXV<|@YYI9c$w*F4exZFGaO&xZ@`3a)cQGXaqGF#lpe54lL}FWLR>c+JLA_o-juI%& zq=*CI8;ArT^=?L+xZ>jC9K}>WIGmVB2er%q>=F?)Fh*paC(RdxXJw$dbw$(TZ|i{$zAj#BO^x$?|7~z<9rXo z_K>sw-=(IezD{*Dq&2=bVFLJd6B@zwb`&Y>zt4dJ2y9dfM;yFZHHL!%g znzDdNr}Ro9@PKO03d-PJ6G0znAC$vFvqY`e$|-Z?tjK;%@PzpnbbmIuS1(+a{svWo zDdX`-@`pX73`$S$>H6N&GWC^G2G>qfbO{jt5)qDOCzb1}yUcvQ4w?AYu zAZ&q=2A`8`S-yOEzm2Hu>};oi%iIIdrol5tQ|M8deQiZpB!f)SJXp6HBsOcWZk=rmOEs@*S0ud}KH zFY%xH-SR#eaFeq}zd2kH8X7t c #2BE41D", +", c #2AE61C", +"< c #2BE41E", +"1 c #27E819", +"2 c #26ED18", +"3 c #28EB19", +"4 c #28EC1A", +"5 c #29ED1B", +"6 c #2AED1B", +"7 c #2AE81D", +"8 c #2BEB1D", +"9 c #2DE91F", +"0 c #2CEA1E", +"q c #2DEA1F", +"w c #2AEF1C", +"e c #2BEF1D", +"r c #2CEF1D", +"t c #2CED1E", +"y c #2CEE1E", +"u c #2DEE1F", +"i c #25F116", +"p c #25F616", +"a c #25F716", +"s c #26F616", +"d c #25F815", +"f c #25F915", +"g c #25FA15", +"h c #24FB15", +"j c #25FB15", +"k c #25F816", +"l c #26F816", +"z c #26F916", +"x c #27F917", +"c c #25FB16", +"v c #26FA16", +"b c #26FB16", +"n c #26FA17", +"m c #27FA17", +"M c #26FB17", +"N c #24FD14", +"B c #24FC15", +"V c #25FC15", +"C c #24FD15", +"Z c #25FD15", +"A c #24FE14", +"S c #24FF14", +"D c #24FE15", +"F c #25FD16", +"G c #26FC16", +"H c #27F318", +"J c #27F419", +"K c #28F019", +"L c #28F119", +"P c #29F01A", +"I c #28F519", +"U c #28F619", +"Y c #28F719", +"T c #29F51A", +"R c #29F71A", +"E c #2AF61B", +"W c #2BF11D", +"Q c #2DF01E", +"! c #2BF41D", +"~ c #2BF61D", +"^ c #27FA18", +"/ c #28F818", +"( c #28F919", +") c #28FA19", +"_ c #2CC321", +"` c #2DC122", +"' c #2DC222", +"] c #2FC424", +"[ c #34C22A", +"{ c #35C32A", +"} c #37C12C", +"| c #34CD29", +" . c #35CD29", +".. c #34CE29", +"X. c #37C82C", +"o. c #39CD2F", +"O. c #2ED622", +"+. c #2FD623", +"@. c #2EDC21", +"#. c #2EDE21", +"$. c #31D625", +"%. c #33D427", +"&. c #30DB23", +"*. c #31D824", +"=. c #31DB24", +"-. c #31DC24", +";. c #31DF24", +":. c #33DD26", +">. c #33DD27", +",. c #34DF27", +"<. c #33D028", +"1. c #34D028", +"2. c #35D32A", +"3. c #3AD02F", +"4. c #39D22E", +"5. c #38D52C", +"6. c #36DF2A", +"7. c #37DF2B", +"8. c #3BDB2F", +"9. c #3BCB30", +"0. c #3CC932", +"q. c #3BCE30", +"w. c #3CCF32", +"e. c #3FD434", +"r. c #2EE021", +"t. c #2FE222", +"y. c #2DE420", +"u. c #2DE620", +"i. c #2DE720", +"p. c #2EE721", +"a. c #2FE522", +"s. c #2DEA20", +"d. c #30E023", +"f. c #30E123", +"g. c #31E024", +"h. c #33E326", +"j. c #31E823", +"k. c #37E32A", +"l. c #41C137", +"z. c #41CB37", +"x. c #41C038", +"c. c #45C63C", +"v. c #41D236", +"b. c #42D637", +"n. c #4BAD44", +"m. c #50AD49", +"M. c #4EB346", +"N. c #49BB40", +"B. c #4ABC41", +"V. c #54B34D", +"C. c #55B34E", +"Z. c #52B94B", +"A. c #5AB354", +"S. c #699C65", +"D. c None", +/* pixels */ +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.O D.D.S.A.A.A.A.V.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.e.e N S S S S O.. 4 S N N N S y v.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.8 N N S S S b ..D.D.D.o.N N N S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.@ h S S S S S Q D.D.D.D.D.D.D.S S S S S S b X D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.% S S S S S S S D.D.D.D.D.D.D.D.D.b S S S S S S { D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.b S S S S S S D.D.D.D.D.D.D.D.D.D.X.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.b S S S S S S *.D.D.D.D.D.D.D.D.D.D.D.f.S S S S S S Y D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.#.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.( S S S S S S $.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.S S S S S S S z.D.D.D.D.D.D.D.D.D.D.D.D.D.b.S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.- S S S S S S b D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.( S S S S S S 6.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.b S S S S S S 6.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.f.S S S S S S ^ D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.<.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S w.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.a.S S S S S S 0 D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.e S S S S S S a.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.S S S S S S S $.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.- S S S S S S Y D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D...s s b S S S S S S S b s s _ D.D.D.D.D.D.", +"D.D.D.D.6.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.x.Y N N S S S S S S S S D.D.D.D.D.D.D.D.", +"D.D.D.D.8 S S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.2 N S S S S S S b Z.D.D.D.D.D.D.D.D.", +"D.D.D.D.;.S S S S S S 0 D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.f.S S S S N b D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.i S S S S S S -.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D...b S S b D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D., S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.y b D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.M.B.B.B.B.B.B.$ D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.V.s.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.b f s b b s d m.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.( S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.` S S S S S 5.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.$ S S S S S S S D.D.D.D.D.D.D.D.", +"D.D.D.D.D.] b S S S S S S t.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.* S S S S S S S D.D.D.D.D.D.D.D.", +"D.D.D.D.c.S S S S S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.6.S S S S S S b D.D.D.D.D.D.D.D.", +"D.D.D.- b S S S S S S S S S S S 0.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.j.S S S S S S 4 D.D.D.D.D.D.D.D.", +"D.D.D.` _ ` y S S S S S S b ` ` D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.Y S S S S S S #.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.4.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S { D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.N S S S S S S f.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.$.S S S S S S S D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.4 S S S S S S W D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.J S S S S S S : D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.e.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.~ S S S S S S k.D.D.D.D.D.D.D.D.D.D.D.D.D.D.a.S S S S S S y D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.v.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.S S S S S S S D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.y S S S S S S s.D.D.D.D.D.D.D.D.D.D.D.D.4 S S S S S S y D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.S S S S S S S 0.D.D.D.D.D.D.D.D.D.D.$.S S S S S S Y D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.@ S S S S S S b D.D.D.D.D.D.D.D.D.} S S S S S S S # D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.& S S S S S S ~ D.D.D.D.D.D.D.n.Y S S S S S S D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.% J S S S S S Y D.D.D.D.D.1.! S S S S S P D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.3 S S S S S N *.D.D.a.N N N S S S a.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.t.8.1 1 3 > ..} - ;.2.-.;.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.", +"D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D.D." +}; -- 2.45.2