1/*
2 * Copyright (c) 1998-2007 Matthijs Hollemans
3 * Distributed under the terms of the MIT License.
4 */
5
6
7#include "Model.h"
8
9#include <new>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13
14#include <Directory.h>
15#include <Entry.h>
16#include <File.h>
17#include <FindDirectory.h>
18#include <List.h>
19#include <MenuItem.h>
20#include <Path.h>
21#include <Roster.h>
22
23#include "GlobalDefs.h"
24
25
26using std::nothrow;
27
28
29Model::Model()
30	:
31	fDirectory(),
32	fSelectedFiles((uint32)0),
33
34	fRecurseDirs(true),
35	fRecurseLinks(false),
36	fSkipDotDirs(true),
37	fCaseSensitive(false),
38	fRegularExpression(false),
39	fTextOnly(true),
40	fInvokeEditor(false),
41
42	fFrame(100, 100, 500, 400),
43
44	fState(STATE_IDLE),
45
46	fFilePanelPath(""),
47
48	fShowLines(true),
49	fEncoding(0)
50{
51	BPath path;
52	if (find_directory(B_USER_DIRECTORY, &path) == B_OK)
53		fFilePanelPath = path.Path();
54	else
55		fFilePanelPath = "/boot/home";
56}
57
58
59status_t
60Model::LoadPrefs()
61{
62	BFile file;
63	status_t status = _OpenFile(&file, PREFS_FILE);
64	if (status != B_OK)
65		return status;
66
67	status = file.Lock();
68	if (status != B_OK)
69		return status;
70
71	int32 value;
72
73	if (file.ReadAttr("RecurseDirs", B_INT32_TYPE, 0, &value,
74			sizeof(int32)) > 0)
75		fRecurseDirs = (value != 0);
76
77	if (file.ReadAttr("RecurseLinks", B_INT32_TYPE, 0, &value,
78			sizeof(int32)) > 0)
79		fRecurseLinks = (value != 0);
80
81	if (file.ReadAttr("SkipDotDirs", B_INT32_TYPE, 0, &value,
82			sizeof(int32)) > 0)
83		fSkipDotDirs = (value != 0);
84
85	if (file.ReadAttr("CaseSensitive", B_INT32_TYPE, 0, &value,
86			sizeof(int32)) > 0)
87		fCaseSensitive = (value != 0);
88
89	if (file.ReadAttr("RegularExpression", B_INT32_TYPE, 0, &value,
90			sizeof(int32)) > 0)
91		fRegularExpression = (value != 0);
92
93	if (file.ReadAttr("TextOnly", B_INT32_TYPE, 0, &value, sizeof(int32)) > 0)
94		fTextOnly = (value != 0);
95
96	if (file.ReadAttr("InvokeEditor", B_INT32_TYPE, 0, &value, sizeof(int32))
97			> 0)
98		fInvokeEditor = (value != 0);
99
100	char buffer [B_PATH_NAME_LENGTH+1];
101	int32 length = file.ReadAttr("FilePanelPath", B_STRING_TYPE, 0, &buffer,
102		sizeof(buffer));
103	if (length > 0) {
104		buffer[length] = '\0';
105		fFilePanelPath = buffer;
106	}
107
108	file.ReadAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));
109
110	if (file.ReadAttr("ShowLines", B_INT32_TYPE, 0, &value,
111			sizeof(int32)) > 0)
112		fShowLines = (value != 0);
113
114	if (file.ReadAttr("Encoding", B_INT32_TYPE, 0, &value, sizeof(int32)) > 0)
115		fEncoding = value;
116
117	file.Unlock();
118
119	return B_OK;
120}
121
122
123status_t
124Model::SavePrefs()
125{
126	BFile file;
127	status_t status = _OpenFile(&file, PREFS_FILE,
128		B_CREATE_FILE | B_WRITE_ONLY);
129	if (status != B_OK)
130		return status;
131
132	status = file.Lock();
133	if (status != B_OK)
134		return status;
135
136	int32 value = 2;
137	file.WriteAttr("Version", B_INT32_TYPE, 0, &value, sizeof(int32));
138
139	value = fRecurseDirs ? 1 : 0;
140	file.WriteAttr("RecurseDirs", B_INT32_TYPE, 0, &value, sizeof(int32));
141
142	value = fRecurseLinks ? 1 : 0;
143	file.WriteAttr("RecurseLinks", B_INT32_TYPE, 0, &value, sizeof(int32));
144
145	value = fSkipDotDirs ? 1 : 0;
146	file.WriteAttr("SkipDotDirs", B_INT32_TYPE, 0, &value, sizeof(int32));
147
148	value = fCaseSensitive ? 1 : 0;
149	file.WriteAttr("CaseSensitive", B_INT32_TYPE, 0, &value, sizeof(int32));
150
151	value = fRegularExpression ? 1 : 0;
152	file.WriteAttr("RegularExpression", B_INT32_TYPE, 0, &value, sizeof(int32));
153
154	value = fTextOnly ? 1 : 0;
155	file.WriteAttr("TextOnly", B_INT32_TYPE, 0, &value, sizeof(int32));
156
157	value = fInvokeEditor ? 1 : 0;
158	file.WriteAttr("InvokeEditor", B_INT32_TYPE, 0, &value, sizeof(int32));
159
160	file.WriteAttr("WindowFrame", B_RECT_TYPE, 0, &fFrame, sizeof(BRect));
161
162	file.WriteAttr("FilePanelPath", B_STRING_TYPE, 0, fFilePanelPath.String(),
163		fFilePanelPath.Length() + 1);
164
165	value = fShowLines ? 1 : 0;
166	file.WriteAttr("ShowLines", B_INT32_TYPE, 0, &value, sizeof(int32));
167
168	file.WriteAttr("Encoding", B_INT32_TYPE, 0, &fEncoding, sizeof(int32));
169
170	file.Sync();
171	file.Unlock();
172
173	return B_OK;
174}
175
176
177void
178Model::AddToHistory(const char* text)
179{
180	BList items;
181	_LoadHistory(items);
182
183	BString* string = new (nothrow) BString(text);
184	if (string == NULL || !items.AddItem(string)) {
185		delete string;
186		_FreeHistory(items);
187		return;
188	}
189
190	int32 count = items.CountItems() - 1;
191		// don't check last item, since that's the one we just added
192	for (int32 t = 0; t < count; ++t) {
193		// If the same text is already in the list,
194		// then remove it first. Case-sensitive.
195		BString* string = static_cast<BString*>(items.ItemAt(t));
196		if (*string == text) {
197			delete static_cast<BString*>(items.RemoveItem(t));
198			break;
199		}
200	}
201
202	while (items.CountItems() >= HISTORY_LIMIT)
203		delete static_cast<BString*>(items.RemoveItem((int32)0));
204
205	_SaveHistory(items);
206	_FreeHistory(items);
207}
208
209
210void
211Model::FillHistoryMenu(BMenu* menu) const
212{
213	BList items;
214	if (!_LoadHistory(items))
215		return;
216
217	for (int32 t = items.CountItems() - 1; t >= 0; --t) {
218		BString* item = static_cast<BString*>(items.ItemAtFast(t));
219		BMessage* message = new BMessage(MSG_SELECT_HISTORY);
220		message->AddString("text", item->String());
221		menu->AddItem(new BMenuItem(item->String(), message));
222	}
223
224	_FreeHistory(items);
225}
226
227
228// #pragma mark - private
229
230
231bool
232Model::_LoadHistory(BList& items) const
233{
234	BFile file;
235	status_t status = _OpenFile(&file, PREFS_FILE);
236	if (status != B_OK)
237		return false;
238
239	status = file.Lock();
240	if (status != B_OK)
241		return false;
242
243	BMessage message;
244	status = message.Unflatten(&file);
245	if (status != B_OK)
246		return false;
247
248	file.Unlock();
249
250	BString string;
251	for (int32 x = 0; message.FindString("string", x, &string) == B_OK; x++) {
252		BString* copy = new (nothrow) BString(string);
253		if (copy == NULL || !items.AddItem(copy)) {
254			delete copy;
255			break;
256		}
257	}
258
259	return true;
260}
261
262
263status_t
264Model::_SaveHistory(const BList& items) const
265{
266	BFile file;
267	status_t status = _OpenFile(&file, PREFS_FILE,
268		B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE,
269		B_USER_SETTINGS_DIRECTORY, NULL);
270	if (status != B_OK)
271		return status;
272
273	status = file.Lock();
274	if (status != B_OK)
275		return status;
276
277	BMessage message;
278	int32 count = items.CountItems();
279	for (int32 i = 0; i < count; i++) {
280		BString* string = static_cast<BString*>(items.ItemAtFast(i));
281
282		if (message.AddString("string", string->String()) != B_OK)
283			break;
284	}
285
286	status = message.Flatten(&file);
287	file.SetSize(message.FlattenedSize());
288	file.Sync();
289	file.Unlock();
290
291	return status;
292}
293
294
295void
296Model::_FreeHistory(const BList& items) const
297{
298	for (int32 t = items.CountItems() - 1; t >= 0; --t)
299		delete static_cast<BString*>((items.ItemAtFast(t)));
300}
301
302
303status_t
304Model::_OpenFile(BFile* file, const char* name, uint32 openMode,
305	directory_which which, BVolume* volume) const
306{
307	if (file == NULL)
308		return B_BAD_VALUE;
309
310	BPath path;
311	status_t status = find_directory(which, &path, true, volume);
312	if (status != B_OK)
313		return status;
314
315	status = path.Append(PREFS_FILE);
316	if (status != B_OK)
317		return status;
318
319	status = file->SetTo(path.Path(), openMode);
320	if (status != B_OK)
321		return status;
322
323	status = file->InitCheck();
324	if (status != B_OK)
325		return status;
326
327	return B_OK;
328}
329