xref: /haiku/src/apps/people/PersonWindow.cpp (revision 2f76808976e7fde2567f6524c45e04fa6a70a805)
1 /*
2  * Copyright 2005-2010, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		Robert Polic
7  *		Stephan Aßmus <superstippi@gmx.de>
8  *
9  * Copyright 1999, Be Incorporated.   All Rights Reserved.
10  * This file may be used under the terms of the Be Sample Code License.
11  */
12 
13 #include "PersonWindow.h"
14 
15 #include <stdio.h>
16 #include <string.h>
17 
18 #include <Alert.h>
19 #include <Catalog.h>
20 #include <Clipboard.h>
21 #include <ControlLook.h>
22 #include <FilePanel.h>
23 #include <FindDirectory.h>
24 #include <Font.h>
25 #include <LayoutBuilder.h>
26 #include <Locale.h>
27 #include <MenuBar.h>
28 #include <MenuItem.h>
29 #include <NodeInfo.h>
30 #include <NodeMonitor.h>
31 #include <Path.h>
32 #include <String.h>
33 #include <TextView.h>
34 #include <Volume.h>
35 
36 #include "PeopleApp.h"
37 #include "PersonView.h"
38 
39 
40 #undef B_TRANSLATE_CONTEXT
41 #define B_TRANSLATE_CONTEXT "People"
42 
43 
44 PersonWindow::PersonWindow(BRect frame, const char* title,
45 		const char* nameAttribute, const char* categoryAttribute,
46 		const entry_ref* ref)
47 	:
48 	BWindow(frame, title, B_TITLED_WINDOW, B_NOT_RESIZABLE | B_NOT_ZOOMABLE
49 		| B_AUTO_UPDATE_SIZE_LIMITS),
50 	fPanel(NULL),
51 	fNameAttribute(nameAttribute)
52 {
53 	BMenu* menu;
54 	BMenuItem* item;
55 
56 	BMenuBar* menuBar = new BMenuBar("");
57 	menu = new BMenu(B_TRANSLATE("File"));
58 	menu->AddItem(item = new BMenuItem(
59 		B_TRANSLATE("New person" B_UTF8_ELLIPSIS),
60 		new BMessage(M_NEW), 'N'));
61 	item->SetTarget(be_app);
62 	menu->AddItem(new BMenuItem(B_TRANSLATE("Close"),
63 		new BMessage(B_QUIT_REQUESTED), 'W'));
64 	menu->AddSeparatorItem();
65 	menu->AddItem(fSave = new BMenuItem(B_TRANSLATE("Save"),
66 		new BMessage(M_SAVE), 'S'));
67 	fSave->SetEnabled(FALSE);
68 	menu->AddItem(new BMenuItem(
69 		B_TRANSLATE("Save as"B_UTF8_ELLIPSIS),
70 		new BMessage(M_SAVE_AS)));
71 	menu->AddItem(fRevert = new BMenuItem(B_TRANSLATE("Revert"),
72 		new BMessage(M_REVERT), 'R'));
73 	fRevert->SetEnabled(FALSE);
74 	menu->AddSeparatorItem();
75 	item = new BMenuItem(B_TRANSLATE("Quit"),
76 		new BMessage(B_QUIT_REQUESTED), 'Q');
77 	item->SetTarget(be_app);
78 	menu->AddItem(item);
79 	menuBar->AddItem(menu);
80 
81 	menu = new BMenu(B_TRANSLATE("Edit"));
82 	menu->AddItem(fUndo = new BMenuItem(B_TRANSLATE("Undo"),
83 		new BMessage(B_UNDO), 'Z'));
84 	fUndo->SetEnabled(false);
85 	menu->AddSeparatorItem();
86 	menu->AddItem(fCut = new BMenuItem(B_TRANSLATE("Cut"),
87 		new BMessage(B_CUT), 'X'));
88 	menu->AddItem(fCopy = new BMenuItem(B_TRANSLATE("Copy"),
89 		new BMessage(B_COPY), 'C'));
90 	menu->AddItem(fPaste = new BMenuItem(B_TRANSLATE("Paste"),
91 		new BMessage(B_PASTE), 'V'));
92 	BMenuItem* selectAllItem = new BMenuItem(B_TRANSLATE("Select all"),
93 		new BMessage(M_SELECT), 'A');
94 	menu->AddItem(selectAllItem);
95 	menu->AddSeparatorItem();
96 	menu->AddItem(item = new BMenuItem(B_TRANSLATE("Configure attributes"),
97 		new BMessage(M_CONFIGURE_ATTRIBUTES), 'F'));
98 	item->SetTarget(be_app);
99 	menuBar->AddItem(menu);
100 
101 	if (ref != NULL) {
102 		SetTitle(ref->name);
103 		_SetToRef(new entry_ref(*ref));
104 	} else
105 		_SetToRef(NULL);
106 
107 	fView = new PersonView("PeopleView", categoryAttribute, fRef);
108 
109 	BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
110 		.SetInsets(0, 0, 0, 0)
111 		.Add(menuBar)
112 		.Add(fView);
113 
114 	fRevert->SetTarget(fView);
115 	selectAllItem->SetTarget(fView);
116 }
117 
118 
119 PersonWindow::~PersonWindow()
120 {
121 	_SetToRef(NULL);
122 }
123 
124 
125 void
126 PersonWindow::MenusBeginning()
127 {
128 	bool enabled = !fView->IsSaved();
129 	fSave->SetEnabled(enabled);
130 	fRevert->SetEnabled(enabled);
131 
132 	BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
133 	if (textView != NULL) {
134 		bool isRedo = false;
135 		undo_state state = textView->UndoState(&isRedo);
136 		fUndo->SetEnabled(state != B_UNDO_UNAVAILABLE);
137 
138 		if (isRedo)
139 			fUndo->SetLabel(B_TRANSLATE("Redo"));
140 		else
141 			fUndo->SetLabel(B_TRANSLATE("Undo"));
142 
143 		enabled = fView->IsTextSelected();
144 		fCut->SetEnabled(enabled);
145 		fCopy->SetEnabled(enabled);
146 	}
147 
148 	be_clipboard->Lock();
149 	fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE));
150 	be_clipboard->Unlock();
151 
152 	fView->BuildGroupMenu();
153 }
154 
155 
156 void
157 PersonWindow::MessageReceived(BMessage* msg)
158 {
159 	char			str[256];
160 	BDirectory		directory;
161 	BEntry			entry;
162 	BFile			file;
163 	BNodeInfo		*node;
164 
165 	switch (msg->what) {
166 		case M_SAVE:
167 			if (!fRef) {
168 				SaveAs();
169 				break;
170 			}
171 			// supposed to fall through
172 		case M_REVERT:
173 		case M_SELECT:
174 			fView->MessageReceived(msg);
175 			break;
176 
177 		case M_SAVE_AS:
178 			SaveAs();
179 			break;
180 
181 		case B_SAVE_REQUESTED:
182 		{
183 			entry_ref dir;
184 			if (msg->FindRef("directory", &dir) == B_OK) {
185 				const char* name = NULL;
186 				msg->FindString("name", &name);
187 				directory.SetTo(&dir);
188 				if (directory.InitCheck() == B_NO_ERROR) {
189 					directory.CreateFile(name, &file);
190 					if (file.InitCheck() == B_NO_ERROR) {
191 						node = new BNodeInfo(&file);
192 						node->SetType("application/x-person");
193 						delete node;
194 
195 						directory.FindEntry(name, &entry);
196 						entry.GetRef(&dir);
197 						_SetToRef(new entry_ref(dir));
198 						SetTitle(fRef->name);
199 						fView->CreateFile(fRef);
200 					}
201 					else {
202 						sprintf(str, B_TRANSLATE("Could not create %s."), name);
203 						(new BAlert("", str, B_TRANSLATE("Sorry")))->Go();
204 					}
205 				}
206 			}
207 			break;
208 		}
209 
210 		case B_NODE_MONITOR:
211 		{
212 			int32 opcode;
213 			if (msg->FindInt32("opcode", &opcode) == B_OK) {
214 				switch (opcode) {
215 					case B_ENTRY_REMOVED:
216 						// We lost our file. Close the window.
217 						PostMessage(B_QUIT_REQUESTED);
218 						break;
219 
220 					case B_ENTRY_MOVED:
221 					{
222 						// We may have renamed our entry. Obtain relevant data
223 						// from message.
224 						BString name;
225 						msg->FindString("name", &name);
226 
227 						int64 directory;
228 						msg->FindInt64("to directory", &directory);
229 
230 						int32 device;
231 						msg->FindInt32("device", &device);
232 
233 						// Update our ref.
234 						delete fRef;
235 						fRef = new entry_ref(device, directory, name.String());
236 
237 						// And our window title.
238 						SetTitle(name);
239 
240 						// If moved to Trash, close window.
241 						BVolume volume(device);
242 						BPath trash;
243 						find_directory(B_TRASH_DIRECTORY, &trash, false,
244 							&volume);
245 						BPath folder(fRef);
246 						folder.GetParent(&folder);
247 						if (folder == trash)
248 							PostMessage(B_QUIT_REQUESTED);
249 
250 						break;
251 					}
252 
253 					case B_ATTR_CHANGED:
254 					{
255 						// An attribute was updated.
256 						BString attr;
257 						if (msg->FindString("attr", &attr) == B_OK)
258 							fView->SetAttribute(attr.String(), true);
259 						break;
260 					}
261 					case B_STAT_CHANGED:
262 						fView->UpdatePicture(fRef);
263 						break;
264 				}
265 			}
266 			break;
267 		}
268 
269 		default:
270 			BWindow::MessageReceived(msg);
271 	}
272 }
273 
274 
275 bool
276 PersonWindow::QuitRequested()
277 {
278 	status_t result;
279 
280 	if (!fView->IsSaved()) {
281 		result = (new BAlert("", B_TRANSLATE("Save changes before quitting?"),
282 							B_TRANSLATE("Cancel"), B_TRANSLATE("Quit"),
283 							B_TRANSLATE("Save")))->Go();
284 		if (result == 2) {
285 			if (fRef)
286 				fView->Save();
287 			else {
288 				SaveAs();
289 				return false;
290 			}
291 		} else if (result == 0)
292 			return false;
293 	}
294 
295 	delete fPanel;
296 
297 	BMessage message(M_WINDOW_QUITS);
298 	message.AddRect("frame", Frame());
299 	if (be_app->Lock()) {
300 		be_app->PostMessage(&message);
301 		be_app->Unlock();
302 	}
303 
304 	return true;
305 }
306 
307 
308 void
309 PersonWindow::Show()
310 {
311 	fView->MakeFocus();
312 	BWindow::Show();
313 }
314 
315 
316 void
317 PersonWindow::AddAttribute(const char* label, const char* attribute)
318 {
319 	fView->AddAttribute(label, attribute);
320 }
321 
322 
323 void
324 PersonWindow::SaveAs()
325 {
326 	char name[B_FILE_NAME_LENGTH];
327 	_GetDefaultFileName(name);
328 
329 	if (fPanel == NULL) {
330 		BMessenger target(this);
331 		fPanel = new BFilePanel(B_SAVE_PANEL, &target);
332 
333 		BPath path;
334 		find_directory(B_USER_DIRECTORY, &path, true);
335 
336 		BDirectory dir;
337 		dir.SetTo(path.Path());
338 
339 		BEntry entry;
340 		if (dir.FindEntry("people", &entry) == B_OK
341 			|| (dir.CreateDirectory("people", &dir) == B_OK
342 					&& dir.GetEntry(&entry) == B_OK)) {
343 			fPanel->SetPanelDirectory(&entry);
344 		}
345 	}
346 
347 	if (fPanel->Window()->Lock()) {
348 		fPanel->SetSaveText(name);
349 		if (fPanel->Window()->IsHidden())
350 			fPanel->Window()->Show();
351 		else
352 			fPanel->Window()->Activate();
353 		fPanel->Window()->Unlock();
354 	}
355 }
356 
357 
358 bool
359 PersonWindow::RefersPersonFile(const entry_ref& ref) const
360 {
361 	if (fRef == NULL)
362 		return false;
363 	return *fRef == ref;
364 }
365 
366 
367 void
368 PersonWindow::_GetDefaultFileName(char* name)
369 {
370 	strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH);
371 	while (*name) {
372 		if (*name == '/')
373 			*name = '-';
374 		name++;
375 	}
376 }
377 
378 
379 void
380 PersonWindow::_SetToRef(entry_ref* ref)
381 {
382 	if (fRef != NULL) {
383 		_WatchChanges(false);
384 		delete fRef;
385 	}
386 
387 	fRef = ref;
388 
389 	_WatchChanges(true);
390 }
391 
392 
393 void
394 PersonWindow::_WatchChanges(bool enable)
395 {
396 	if (fRef == NULL)
397 		return;
398 
399 	node_ref nodeRef;
400 
401 	BNode node(fRef);
402 	node.GetNodeRef(&nodeRef);
403 
404 	uint32 flags;
405 	BString action;
406 
407 	if (enable) {
408 		// Start watching.
409 		flags = B_WATCH_ALL;
410 		action = "starting";
411 	} else {
412 		// Stop watching.
413 		flags = B_STOP_WATCHING;
414 		action = "stoping";
415 	}
416 
417 	if (watch_node(&nodeRef, flags, this) != B_OK) {
418 		printf("Error %s node monitor.\n", action.String());
419 	}
420 }
421