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