1 #include "Browser.h"
2 
3 ArrayMap<String, TopicEditor::FileInfo> TopicEditor::editstate;
4 VectorMap<String, String>               TopicEditor::grouptopic; // the last topic edited in group
5 
6 String     TopicEditor::lasttemplate;
7 int        TopicEditor::lastlang;
8 bool       TopicEditor::allfonts;
9 
10 struct ListOrder : FileList::Order {
11 public:
operator ()ListOrder12 	virtual bool operator()(const FileList::File& a, const FileList::File& b) const {
13 		return ToUpper(a.name) < ToUpper(b.name);
14 	}
15 };
16 
LoadTopics(Vector<String> & topics,const String & grouppath)17 bool LoadTopics(Vector<String>& topics, const String& grouppath)
18 {
19 	bool renamed = false;
20 
21 	for(int pass = 0; pass < 2; pass++) {
22 		topics.Clear();
23 
24 		FindFile ff(AppendFileName(grouppath, "*.*"));
25 		for(; ff; ff.Next()) {
26 			if(ff.IsFile() && GetFileExt(ff.GetName()) == ".tppi") {
27 				String n = ff.GetName();
28 				int q = n.ReverseFind('$');
29 				if(q < 0)
30 					q = n.ReverseFind('@');
31 				if(q >= 0 && q > n.GetLength() - 12) {
32 					String nn = n;
33 					n.Set(q, '_');
34 					FileMove(AppendFileName(grouppath, nn), AppendFileName(grouppath, n));
35 					renamed = true;
36 				}
37 			}
38 			if(ff.IsFile() && GetFileExt(ff.GetName()) == ".tpp") {
39 				String n = ff.GetName();
40 				int q = n.ReverseFind('$');
41 				if(q < 0)
42 					q = n.ReverseFind('@');
43 				if(q >= 0 && q > n.GetLength() - 12) {
44 					String nn = n;
45 					n.Set(q, '_');
46 					FileMove(AppendFileName(grouppath, nn), AppendFileName(grouppath, n));
47 					renamed = true;
48 				}
49 
50 				topics.Add(GetFileTitle(n));
51 			}
52 		}
53 		if(!renamed)
54 			break;
55 	}
56 
57 	return renamed;
58 }
59 
FillTopicsList(FileList & list,const Vector<String> & topics)60 void FillTopicsList(FileList& list, const Vector<String>& topics)
61 {
62 	list.Clear();
63 
64 	for (const auto& topic : topics)
65 		list.Add(topic, TopicImg::Topic());
66 
67 	list.Sort(ListOrder());
68 	list.Enable();
69 
70 }
71 
Open(const String & group_path)72 void TopicEditor::Open(const String& group_path)
73 {
74 
75 	topics_search.Clear();
76 
77 	grouppath = group_path;
78 	if(FileExists(grouppath))
79 		DeleteFile(grouppath);
80 	DirectoryCreate(grouppath);
81 
82 	if(LoadTopics(topics, grouppath))
83 		SaveInc();
84 	FillTopicsList(topics_list, topics);
85 
86 	int q = grouptopic.Find(grouppath);
87 	if(q >= 0)
88 		topics_list.FindSetCursor(grouptopic[q]);
89 	else
90 		topics_list.SetCursor(0);
91 }
92 
OpenFile(const String & path)93 void TopicEditor::OpenFile(const String& path)
94 {
95 	grouppath.Clear();
96 	singlefilepath = path;
97 	topics_list.Clear();
98 	topics_list.Add(GetFileTitle(path), TopicImg::Topic());
99 	topics_list.Enable();
100 	topics_list.SetCursor(0);
101 }
102 
OnSearch()103 void TopicEditor::OnSearch()
104 {
105 	auto current_topic = topics_list.GetCurrentName();
106 	auto topic_list_sel_callback = topics_list.WhenSel;
107 	topics_list.WhenSel = {};
108 
109 	DoSearch();
110 
111 	auto idx = topics_list.Find(current_topic);
112 	if (idx >= 0) {
113 		topics_list.SetCursor(idx);
114 	}
115 
116 	topics_list.WhenSel = topic_list_sel_callback;
117 }
118 
DoSearch()119 void TopicEditor::DoSearch()
120 {
121 	auto search_phase = ToLower(topics_search.GetData().ToString());
122 
123 	auto found_topics = Vector<String>();
124 	for (const auto& topic : topics) {
125 		const auto normalized_topic = ToLower(topic);
126 		if (normalized_topic.Find(search_phase) >= 0) {
127 			found_topics.Add(topic);
128 		}
129 	}
130 	FillTopicsList(topics_list, found_topics);
131 }
132 
GetCurrentTopicPath()133 String TopicEditor::GetCurrentTopicPath()
134 {
135 	if(topics_list.IsCursor())
136 		return NormalizePath(AppendFileName(grouppath, topics_list.GetCurrentName() + ".tpp"));
137 	else
138 		return Null;
139 }
140 
ShowTopic(bool b)141 void TopicEditor::ShowTopic(bool b)
142 {
143 	title.Enable(b);
144 	title.Show(b);
145 	editor.Enable(b);
146 	editor.Show(b);
147 }
148 
TopicCursor()149 void TopicEditor::TopicCursor()
150 {
151 	String h;
152 	HideTopic();
153 	if(IsNull(grouppath)) {
154 		ShowTopic();
155 		h = singlefilepath;
156 	}
157 	else {
158 		if(!topics_list.IsCursor())
159 			return;
160 		h = GetCurrentTopicPath();
161 	}
162 	if(h != topicpath)
163 		Load(h);
164 	else
165 		ShowTopic();
166 }
167 
Load(const String & fn)168 void TopicEditor::Load(const String& fn)
169 {
170 	Flush();
171 
172 	Topic t = ReadTopic(LoadFile(fn));
173 	if(t.text.IsVoid()) {
174 		Exclamation("Error loading the topic file:&[* " + DeQtf(fn));
175 		topics_list.KillCursor();
176 		return;
177 	}
178 
179 	title <<= t.title;
180 	editor <<= t.text;
181 	topicpath = fn;
182 
183 	int q = editstate.Find(fn);
184 	if(q >= 0) {
185 		FileInfo& fi = editstate[q];
186 		if(fi.time == FileGetTime(fn)) {
187 			editor.SetPickUndoInfo(pick(fi.undo));
188 			fi.time = Time(1, 1, 1);
189 			editor.SetPosInfo(fi.pos);
190 		}
191 	}
192 
193 	grouptopic.GetAdd(grouppath) = GetFileTitle(fn);
194 
195 	ShowTopic();
196 
197 	editor.SetFocus();
198 	editor.ClearModify();
199 	title.ClearModify();
200 }
201 
202 int TopicEditor::serial;
203 
GetSerial()204 int TopicEditor::GetSerial()
205 {
206 	return serial;
207 }
208 
SaveTopic()209 void TopicEditor::SaveTopic()
210 {
211 	if(IsNull(topicpath))
212 		return;
213 	if(IsNull(~title)) {
214 		const RichText& txt = editor.Get();
215 		if(txt.IsPara(0)) {
216 			RichPara para = txt.Get(0);
217 			WString t;
218 			for(int i = 0; i < para.GetCount(); i++)
219 				if(para[i].IsText())
220 					for(const wchar *s = para[i].text; *s; s++) {
221 						if(*s == '\t' || *s == 160)
222 							t.Cat(' ');
223 						else
224 						if(*s >= ' ')
225 							t.Cat(*s);
226 					}
227 			if(!IsNull(t))
228 				title <<= t;
229 		}
230 	}
231 	if(!editor.IsModified() && !title.IsModified())
232 		return;
233 	String r = WriteTopic((String)~title, editor.Get());
234 	if(LoadFile(topicpath) != r) {
235 		serial++;
236 		SaveFile(topicpath, r);
237 		if(FileExists(AppendFileName(grouppath, "all.i")))
238 			SaveFile(ForceExt(topicpath, ".tppi"), WriteTopicI((String)~title, editor.Get()));
239 		TopicLink tl = ParseTopicFilePath(topicpath);
240 		if(tl)
241 			SyncTopicFile(editor.Get(), TopicLinkString(tl), topicpath, ~title);
242 	}
243 }
244 
Flush()245 void TopicEditor::Flush()
246 {
247 	if(IsNull(topicpath))
248 		return;
249 	SaveTopic();
250 	FileInfo& fi = editstate.GetAdd(topicpath);
251 	fi.time = FileGetTime(topicpath);
252 	fi.pos = editor.GetPosInfo();
253 	fi.undo = editor.PickUndoInfo();
254 	topicpath.Clear();
255 	editor.Clear();
256 	HideTopic();
257 }
258 
SaveInc()259 void TopicEditor::SaveInc()
260 {
261 	SaveGroupInc(grouppath);
262 }
263 
ParseTopicFilePath(const String & path)264 TopicLink ParseTopicFilePath(const String& path)
265 {
266 	TopicLink tl;
267 	tl.topic = GetFileTitle(path);
268 	String q = GetFileFolder(path);
269 	tl.group = GetFileTitle(q);
270 	q = GetFileFolder(q);
271 	const Workspace& wspc = GetIdeWorkspace();
272 	for(int i = 0; i < wspc.GetCount(); i++)
273 		if(PathIsEqual(PackageDirectory(wspc[i]), q)) {
274 			tl.package = wspc[i];
275 			return tl;
276 		}
277 	return TopicLink();
278 }
279 
TopicFilePath(const TopicLink & tl)280 String TopicFilePath(const TopicLink& tl)
281 {
282 	return AppendFileName(AppendFileName(PackageDirectory(tl.package), tl.group + ".tpp"),
283 	                      tl.topic + ".tpp");
284 }
285