1 /*
2  * Part of WCM Commander
3  * https://github.com/corporateshark/WCMCommander
4  * wcm@linderdaum.com
5  */
6 
7 #include "nchistory.h"
8 #include "wcm-config.h"
9 
10 
11 #define MAX_FIELD_HISTORY_COUNT	50
12 
13 static const char fieldHistSection[] = "FieldsHistory";
14 
15 static cstrhash<HistCollect_t> g_FieldsHistHash;
16 
17 
LoadFieldsHistory()18 void LoadFieldsHistory()
19 {
20 	IniHash iniHash;
21 	IniHashLoad( iniHash, fieldHistSection );
22 
23 	g_FieldsHistHash.clear();
24 	std::vector<const char*> SecList = iniHash.Keys();
25 	char Name[64];
26 
27 	for ( int i = 0, count = SecList.size(); i < count; i++ )
28 	{
29 		const char* Section = SecList[i];
30 		HistCollect_t& List = g_FieldsHistHash.get( Section );
31 
32 		for ( int j = 0; j < MAX_FIELD_HISTORY_COUNT; j++ )
33 		{
34 			Lsnprintf( Name, sizeof( Name ), "v%i", j );
35 			const char* Value = iniHash.GetStrValue( Section, Name, nullptr );
36 			if ( Value )
37 			{
38 				std::vector<unicode_t> Str = utf8_to_unicode( Value );
39 				List.push_back( Str );
40 			}
41 		}
42 	}
43 }
44 
SaveFieldsHistory()45 void SaveFieldsHistory()
46 {
47 	IniHash iniHash;
48 	std::vector<const char*> SecList = g_FieldsHistHash.keys();
49 	char Name[64];
50 
51 	for ( int i = 0, SecCount = SecList.size(); i < SecCount; i++ )
52 	{
53 		const char* Section = SecList[i];
54 		HistCollect_t* List = g_FieldsHistHash.exist( Section );
55 		if ( !List )
56 		{
57 			continue;
58 		}
59 
60 		for ( int j = 0, ValCount = List->size(); j < ValCount; j++ )
61 		{
62 			Lsnprintf( Name, sizeof( Name ), "v%i", j );
63 			std::string Val = unicode_to_utf8_string( List->at(j).data() );
64 
65 			iniHash.SetStrValue( Section, Name, Val.c_str());
66 		}
67 	}
68 
69 	IniHashSave( iniHash, fieldHistSection );
70 }
71 
GetFieldHistCollect(const char * FieldName)72 HistCollect_t* GetFieldHistCollect( const char* FieldName )
73 {
74 	if ( !FieldName || !FieldName[0] )
75 	{
76 		return nullptr;
77 	}
78 
79 	HistCollect_t* List = g_FieldsHistHash.exist( FieldName );
80 	return List ? List : nullptr;
81 }
82 
83 // Returns index of element with the specified text, or -1 if no such found
FindHistElement(HistCollect_t * List,const unicode_t * Text)84 int FindHistElement( HistCollect_t* List, const unicode_t* Text )
85 {
86 	for (int i = 0, count = List->size(); i < count; i++)
87 	{
88 		if (unicode_is_equal( Text, List->at(i).data() ))
89 		{
90 			return i;
91 		}
92 	}
93 
94 	return -1;
95 }
96 
AddFieldTextToHistory(const char * FieldName,const unicode_t * Txt)97 void AddFieldTextToHistory( const char* FieldName, const unicode_t* Txt )
98 {
99 	if ( !FieldName || !FieldName[0] || !Txt || !Txt[0] )
100 	{
101 		return;
102 	}
103 
104 	std::vector<unicode_t> Str = new_unicode_str( Txt );
105 
106 	HistCollect_t& List = g_FieldsHistHash.get( FieldName );
107 
108 	// check if item already exists in the list
109 	const int Index = FindHistElement( &List, Str.data() );
110 	if ( Index != -1 )
111 	{
112 		// remove existing item
113 		List.erase( List.begin() + Index );
114 	}
115 
116 	// add item to the begining of the list
117 	List.insert( List.begin(), Str );
118 
119 	// limit number of elements in the list
120 	while ( List.size() > MAX_FIELD_HISTORY_COUNT )
121 	{
122 		List.erase( List.end() );
123 	}
124 }
125 
126 
Clear()127 void NCHistory::Clear()
128 {
129 	m_List.clear();
130 }
131 
DeleteAll(const unicode_t * Str)132 void NCHistory::DeleteAll( const unicode_t* Str )
133 {
134 	size_t i = 0;
135 
136 	while ( i < m_List.size() )
137 	{
138 		if ( unicode_is_equal( Str, m_List[i].data() ) )
139 		{
140 			m_List.erase( m_List.begin() + i );
141 			continue;
142 		}
143 
144 		i++;
145 	}
146 
147 	// Maybe we need to keep m_Current pointing to specific command, if the latter survived?
148 	m_Current = -1;
149 }
150 
Put(const unicode_t * str)151 void NCHistory::Put( const unicode_t* str )
152 {
153 	m_Current = -1;
154 
155 	for ( size_t i = 0; i < m_List.size(); i++ )
156 	{
157 		const unicode_t* s = str;
158 		const unicode_t* t = m_List[i].data();
159 
160 		while ( *t && *s )
161 		{
162 			if ( *t != *s ) { break; }
163 
164 			s++;
165 			t++;
166 		}
167 
168 		if ( *t == *s )
169 		{
170 			std::vector<unicode_t> p = m_List[i];
171 			m_List.erase( m_List.begin() + i );
172 			m_List.insert( m_List.begin(), p );
173 			return;
174 		}
175 	}
176 
177 	const size_t MaxHistorySize = 1000;
178 
179 	if ( m_List.size() > MaxHistorySize )
180 	{
181 		m_List.pop_back();
182 	}
183 
184 	m_List.insert( m_List.begin(), new_unicode_str( str ) );
185 }
186 
Count() const187 size_t NCHistory::Count() const
188 {
189 	return m_List.size();
190 }
191 
operator [](size_t n)192 const unicode_t* NCHistory::operator[] ( size_t n )
193 {
194 	return n < m_List.size() ? m_List[n].data() : nullptr;
195 }
196 
Prev()197 const unicode_t* NCHistory::Prev()
198 {
199     if ( !m_List.size() ) return nullptr;
200 
201 	if ( m_Current >= (int) m_List.size()-1 )
202 	{
203 		m_Current = (int) m_List.size()-1;
204 		return m_List[m_Current].data();
205 	}
206 
207 	return m_List[++m_Current].data();
208 }
209 
Next()210 const unicode_t* NCHistory::Next()
211 {
212 	if ( m_Current <= 0 )
213 	{
214 		m_Current = -1;
215 		return nullptr;
216 	}
217 
218 	return ( m_Current == 0 || m_Current > (int) m_List.size() )
219 			? nullptr
220 			: m_List[--m_Current].data();
221 }
222