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