xref: /haiku/src/apps/people/PersonWindow.cpp (revision c302a243e15e640fae0f689e32cdf0c18749afee)
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_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_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_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 		.Add(menuBar)
112 		.AddGroup(B_VERTICAL, 0)
113 			.Add(fView)
114 			.SetInsets(B_USE_WINDOW_SPACING)
115 			.End();
116 
117 	fRevert->SetTarget(fView);
118 	selectAllItem->SetTarget(fView);
119 }
120 
121 
122 PersonWindow::~PersonWindow()
123 {
124 	_SetToRef(NULL);
125 }
126 
127 
128 void
129 PersonWindow::MenusBeginning()
130 {
131 	bool enabled = !fView->IsSaved();
132 	fSave->SetEnabled(enabled);
133 	fRevert->SetEnabled(enabled);
134 
135 	bool isRedo = false;
136 	bool undoEnabled = false;
137 	bool cutAndCopyEnabled = false;
138 
139 	BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
140 	if (textView != NULL) {
141 		undo_state state = textView->UndoState(&isRedo);
142 		undoEnabled = state != B_UNDO_UNAVAILABLE;
143 
144 		cutAndCopyEnabled = fView->IsTextSelected();
145 	}
146 
147 	if (isRedo)
148 		fUndo->SetLabel(B_TRANSLATE("Redo"));
149 	else
150 		fUndo->SetLabel(B_TRANSLATE("Undo"));
151 	fUndo->SetEnabled(undoEnabled);
152 	fCut->SetEnabled(cutAndCopyEnabled);
153 	fCopy->SetEnabled(cutAndCopyEnabled);
154 
155 	be_clipboard->Lock();
156 	fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE));
157 	be_clipboard->Unlock();
158 
159 	fView->BuildGroupMenu();
160 }
161 
162 
163 void
164 PersonWindow::MessageReceived(BMessage* msg)
165 {
166 	char			str[256];
167 	BDirectory		directory;
168 	BEntry			entry;
169 	BFile			file;
170 	BNodeInfo		*node;
171 
172 	switch (msg->what) {
173 		case M_SAVE:
174 			if (!fRef) {
175 				SaveAs();
176 				break;
177 			}
178 			// supposed to fall through
179 		case M_REVERT:
180 		case M_SELECT:
181 			fView->MessageReceived(msg);
182 			break;
183 
184 		case M_SAVE_AS:
185 			SaveAs();
186 			break;
187 
188 		case B_UNDO: // fall through
189 		case B_CUT:
190 		case B_COPY:
191 		case B_PASTE:
192 		{
193 			BView* view = CurrentFocus();
194 			if (view != NULL)
195 				view->MessageReceived(msg);
196 			break;
197 		}
198 
199 		case B_SAVE_REQUESTED:
200 		{
201 			entry_ref dir;
202 			if (msg->FindRef("directory", &dir) == B_OK) {
203 				const char* name = NULL;
204 				msg->FindString("name", &name);
205 				directory.SetTo(&dir);
206 				if (directory.InitCheck() == B_NO_ERROR) {
207 					directory.CreateFile(name, &file);
208 					if (file.InitCheck() == B_NO_ERROR) {
209 						node = new BNodeInfo(&file);
210 						node->SetType("application/x-person");
211 						delete node;
212 
213 						directory.FindEntry(name, &entry);
214 						entry.GetRef(&dir);
215 						_SetToRef(new entry_ref(dir));
216 						SetTitle(fRef->name);
217 						fView->CreateFile(fRef);
218 					}
219 					else {
220 						sprintf(str, B_TRANSLATE("Could not create %s."), name);
221 						BAlert* alert = new BAlert("", str, B_TRANSLATE("Sorry"));
222 						alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
223 						alert->Go();
224 					}
225 				}
226 			}
227 			break;
228 		}
229 
230 		case B_NODE_MONITOR:
231 		{
232 			int32 opcode;
233 			if (msg->FindInt32("opcode", &opcode) == B_OK) {
234 				switch (opcode) {
235 					case B_ENTRY_REMOVED:
236 						// We lost our file. Close the window.
237 						PostMessage(B_QUIT_REQUESTED);
238 						break;
239 
240 					case B_ENTRY_MOVED:
241 					{
242 						// We may have renamed our entry. Obtain relevant data
243 						// from message.
244 						BString name;
245 						msg->FindString("name", &name);
246 
247 						int64 directory;
248 						msg->FindInt64("to directory", &directory);
249 
250 						int32 device;
251 						msg->FindInt32("device", &device);
252 
253 						// Update our ref.
254 						delete fRef;
255 						fRef = new entry_ref(device, directory, name.String());
256 
257 						// And our window title.
258 						SetTitle(name);
259 
260 						// If moved to Trash, close window.
261 						BVolume volume(device);
262 						BPath trash;
263 						find_directory(B_TRASH_DIRECTORY, &trash, false,
264 							&volume);
265 						BPath folder(fRef);
266 						folder.GetParent(&folder);
267 						if (folder == trash)
268 							PostMessage(B_QUIT_REQUESTED);
269 
270 						break;
271 					}
272 
273 					case B_ATTR_CHANGED:
274 					{
275 						// An attribute was updated.
276 						BString attr;
277 						if (msg->FindString("attr", &attr) == B_OK)
278 							fView->SetAttribute(attr.String(), true);
279 						break;
280 					}
281 					case B_STAT_CHANGED:
282 						fView->UpdatePicture(fRef);
283 						break;
284 				}
285 			}
286 			break;
287 		}
288 
289 		default:
290 			BWindow::MessageReceived(msg);
291 	}
292 }
293 
294 
295 bool
296 PersonWindow::QuitRequested()
297 {
298 	status_t result;
299 
300 	if (!fView->IsSaved()) {
301 		BAlert* alert = new BAlert("",
302 			B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"),
303 			B_TRANSLATE("Don't save"), B_TRANSLATE("Save"),
304 			B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
305 		alert->SetShortcut(0, B_ESCAPE);
306 		alert->SetShortcut(1, 'd');
307 		alert->SetShortcut(2, 's');
308 		result = alert->Go();
309 
310 		if (result == 2) {
311 			if (fRef)
312 				fView->Save();
313 			else {
314 				SaveAs();
315 				return false;
316 			}
317 		} else if (result == 0)
318 			return false;
319 	}
320 
321 	delete fPanel;
322 
323 	BMessage message(M_WINDOW_QUITS);
324 	message.AddRect("frame", Frame());
325 	if (be_app->Lock()) {
326 		be_app->PostMessage(&message);
327 		be_app->Unlock();
328 	}
329 
330 	return true;
331 }
332 
333 
334 void
335 PersonWindow::Show()
336 {
337 	fView->MakeFocus();
338 	BWindow::Show();
339 }
340 
341 
342 void
343 PersonWindow::AddAttribute(const char* label, const char* attribute)
344 {
345 	fView->AddAttribute(label, attribute);
346 }
347 
348 
349 void
350 PersonWindow::SaveAs()
351 {
352 	char name[B_FILE_NAME_LENGTH];
353 	_GetDefaultFileName(name);
354 
355 	if (fPanel == NULL) {
356 		BMessenger target(this);
357 		fPanel = new BFilePanel(B_SAVE_PANEL, &target);
358 
359 		BPath path;
360 		find_directory(B_USER_DIRECTORY, &path, true);
361 
362 		BDirectory dir;
363 		dir.SetTo(path.Path());
364 
365 		BEntry entry;
366 		if (dir.FindEntry("people", &entry) == B_OK
367 			|| (dir.CreateDirectory("people", &dir) == B_OK
368 					&& dir.GetEntry(&entry) == B_OK)) {
369 			fPanel->SetPanelDirectory(&entry);
370 		}
371 	}
372 
373 	if (fPanel->Window()->Lock()) {
374 		fPanel->SetSaveText(name);
375 		if (fPanel->Window()->IsHidden())
376 			fPanel->Window()->Show();
377 		else
378 			fPanel->Window()->Activate();
379 		fPanel->Window()->Unlock();
380 	}
381 }
382 
383 
384 bool
385 PersonWindow::RefersPersonFile(const entry_ref& ref) const
386 {
387 	if (fRef == NULL)
388 		return false;
389 	return *fRef == ref;
390 }
391 
392 
393 void
394 PersonWindow::_GetDefaultFileName(char* name)
395 {
396 	strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH);
397 	while (*name) {
398 		if (*name == '/')
399 			*name = '-';
400 		name++;
401 	}
402 }
403 
404 
405 void
406 PersonWindow::_SetToRef(entry_ref* ref)
407 {
408 	if (fRef != NULL) {
409 		_WatchChanges(false);
410 		delete fRef;
411 	}
412 
413 	fRef = ref;
414 
415 	_WatchChanges(true);
416 }
417 
418 
419 void
420 PersonWindow::_WatchChanges(bool enable)
421 {
422 	if (fRef == NULL)
423 		return;
424 
425 	node_ref nodeRef;
426 
427 	BNode node(fRef);
428 	node.GetNodeRef(&nodeRef);
429 
430 	uint32 flags;
431 	BString action;
432 
433 	if (enable) {
434 		// Start watching.
435 		flags = B_WATCH_ALL;
436 		action = "starting";
437 	} else {
438 		// Stop watching.
439 		flags = B_STOP_WATCHING;
440 		action = "stoping";
441 	}
442 
443 	if (watch_node(&nodeRef, flags, this) != B_OK) {
444 		printf("Error %s node monitor.\n", action.String());
445 	}
446 }
447