vfs: check userland buffers before reading them.
[haiku.git] / src / apps / people / PersonWindow.cpp
blob3a99669e4db04734246b0ffafad31aacf9b0ed7a
1 /*
2 * Copyright 2005-2010, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT license.
5 * Authors:
6 * Robert Polic
7 * Stephan Aßmus <superstippi@gmx.de>
9 * Copyright 1999, Be Incorporated. All Rights Reserved.
10 * This file may be used under the terms of the Be Sample Code License.
13 #include "PersonWindow.h"
15 #include <stdio.h>
16 #include <string.h>
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>
36 #include "PeopleApp.h"
37 #include "PersonView.h"
40 #undef B_TRANSLATION_CONTEXT
41 #define B_TRANSLATION_CONTEXT "People"
44 PersonWindow::PersonWindow(BRect frame, const char* title,
45 const char* nameAttribute, const char* categoryAttribute,
46 const entry_ref* ref)
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)
54 BMenu* menu;
55 BMenuItem* item;
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);
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);
102 if (ref != NULL) {
103 SetTitle(ref->name);
104 _SetToRef(new entry_ref(*ref));
105 } else
106 _SetToRef(NULL);
108 fView = new PersonView("PeopleView", categoryAttribute, fRef);
110 BLayoutBuilder::Group<>(this, B_VERTICAL, 0)
111 .Add(menuBar)
112 .Add(fView);
114 fRevert->SetTarget(fView);
115 selectAllItem->SetTarget(fView);
119 PersonWindow::~PersonWindow()
121 _SetToRef(NULL);
125 void
126 PersonWindow::MenusBeginning()
128 bool enabled = !fView->IsSaved();
129 fSave->SetEnabled(enabled);
130 fRevert->SetEnabled(enabled);
132 bool isRedo = false;
133 bool undoEnabled = false;
134 bool cutAndCopyEnabled = false;
136 BTextView* textView = dynamic_cast<BTextView*>(CurrentFocus());
137 if (textView != NULL) {
138 undo_state state = textView->UndoState(&isRedo);
139 undoEnabled = state != B_UNDO_UNAVAILABLE;
141 cutAndCopyEnabled = fView->IsTextSelected();
144 if (isRedo)
145 fUndo->SetLabel(B_TRANSLATE("Redo"));
146 else
147 fUndo->SetLabel(B_TRANSLATE("Undo"));
148 fUndo->SetEnabled(undoEnabled);
149 fCut->SetEnabled(cutAndCopyEnabled);
150 fCopy->SetEnabled(cutAndCopyEnabled);
152 be_clipboard->Lock();
153 fPaste->SetEnabled(be_clipboard->Data()->HasData("text/plain", B_MIME_TYPE));
154 be_clipboard->Unlock();
156 fView->BuildGroupMenu();
160 void
161 PersonWindow::MessageReceived(BMessage* msg)
163 char str[256];
164 BDirectory directory;
165 BEntry entry;
166 BFile file;
167 BNodeInfo *node;
169 switch (msg->what) {
170 case M_SAVE:
171 if (!fRef) {
172 SaveAs();
173 break;
175 // supposed to fall through
176 case M_REVERT:
177 case M_SELECT:
178 fView->MessageReceived(msg);
179 break;
181 case M_SAVE_AS:
182 SaveAs();
183 break;
185 case B_UNDO: // fall through
186 case B_CUT:
187 case B_COPY:
188 case B_PASTE:
190 BView* view = CurrentFocus();
191 if (view != NULL)
192 view->MessageReceived(msg);
193 break;
196 case B_SAVE_REQUESTED:
198 entry_ref dir;
199 if (msg->FindRef("directory", &dir) == B_OK) {
200 const char* name = NULL;
201 msg->FindString("name", &name);
202 directory.SetTo(&dir);
203 if (directory.InitCheck() == B_NO_ERROR) {
204 directory.CreateFile(name, &file);
205 if (file.InitCheck() == B_NO_ERROR) {
206 node = new BNodeInfo(&file);
207 node->SetType("application/x-person");
208 delete node;
210 directory.FindEntry(name, &entry);
211 entry.GetRef(&dir);
212 _SetToRef(new entry_ref(dir));
213 SetTitle(fRef->name);
214 fView->CreateFile(fRef);
216 else {
217 sprintf(str, B_TRANSLATE("Could not create %s."), name);
218 BAlert* alert = new BAlert("", str, B_TRANSLATE("Sorry"));
219 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
220 alert->Go();
224 break;
227 case B_NODE_MONITOR:
229 int32 opcode;
230 if (msg->FindInt32("opcode", &opcode) == B_OK) {
231 switch (opcode) {
232 case B_ENTRY_REMOVED:
233 // We lost our file. Close the window.
234 PostMessage(B_QUIT_REQUESTED);
235 break;
237 case B_ENTRY_MOVED:
239 // We may have renamed our entry. Obtain relevant data
240 // from message.
241 BString name;
242 msg->FindString("name", &name);
244 int64 directory;
245 msg->FindInt64("to directory", &directory);
247 int32 device;
248 msg->FindInt32("device", &device);
250 // Update our ref.
251 delete fRef;
252 fRef = new entry_ref(device, directory, name.String());
254 // And our window title.
255 SetTitle(name);
257 // If moved to Trash, close window.
258 BVolume volume(device);
259 BPath trash;
260 find_directory(B_TRASH_DIRECTORY, &trash, false,
261 &volume);
262 BPath folder(fRef);
263 folder.GetParent(&folder);
264 if (folder == trash)
265 PostMessage(B_QUIT_REQUESTED);
267 break;
270 case B_ATTR_CHANGED:
272 // An attribute was updated.
273 BString attr;
274 if (msg->FindString("attr", &attr) == B_OK)
275 fView->SetAttribute(attr.String(), true);
276 break;
278 case B_STAT_CHANGED:
279 fView->UpdatePicture(fRef);
280 break;
283 break;
286 default:
287 BWindow::MessageReceived(msg);
292 bool
293 PersonWindow::QuitRequested()
295 status_t result;
297 if (!fView->IsSaved()) {
298 BAlert* alert = new BAlert("",
299 B_TRANSLATE("Save changes before closing?"), B_TRANSLATE("Cancel"),
300 B_TRANSLATE("Don't save"), B_TRANSLATE("Save"),
301 B_WIDTH_AS_USUAL, B_OFFSET_SPACING, B_WARNING_ALERT);
302 alert->SetShortcut(0, B_ESCAPE);
303 alert->SetShortcut(1, 'd');
304 alert->SetShortcut(2, 's');
305 result = alert->Go();
307 if (result == 2) {
308 if (fRef)
309 fView->Save();
310 else {
311 SaveAs();
312 return false;
314 } else if (result == 0)
315 return false;
318 delete fPanel;
320 BMessage message(M_WINDOW_QUITS);
321 message.AddRect("frame", Frame());
322 if (be_app->Lock()) {
323 be_app->PostMessage(&message);
324 be_app->Unlock();
327 return true;
331 void
332 PersonWindow::Show()
334 fView->MakeFocus();
335 BWindow::Show();
339 void
340 PersonWindow::AddAttribute(const char* label, const char* attribute)
342 fView->AddAttribute(label, attribute);
346 void
347 PersonWindow::SaveAs()
349 char name[B_FILE_NAME_LENGTH];
350 _GetDefaultFileName(name);
352 if (fPanel == NULL) {
353 BMessenger target(this);
354 fPanel = new BFilePanel(B_SAVE_PANEL, &target);
356 BPath path;
357 find_directory(B_USER_DIRECTORY, &path, true);
359 BDirectory dir;
360 dir.SetTo(path.Path());
362 BEntry entry;
363 if (dir.FindEntry("people", &entry) == B_OK
364 || (dir.CreateDirectory("people", &dir) == B_OK
365 && dir.GetEntry(&entry) == B_OK)) {
366 fPanel->SetPanelDirectory(&entry);
370 if (fPanel->Window()->Lock()) {
371 fPanel->SetSaveText(name);
372 if (fPanel->Window()->IsHidden())
373 fPanel->Window()->Show();
374 else
375 fPanel->Window()->Activate();
376 fPanel->Window()->Unlock();
381 bool
382 PersonWindow::RefersPersonFile(const entry_ref& ref) const
384 if (fRef == NULL)
385 return false;
386 return *fRef == ref;
390 void
391 PersonWindow::_GetDefaultFileName(char* name)
393 strncpy(name, fView->AttributeValue(fNameAttribute), B_FILE_NAME_LENGTH);
394 while (*name) {
395 if (*name == '/')
396 *name = '-';
397 name++;
402 void
403 PersonWindow::_SetToRef(entry_ref* ref)
405 if (fRef != NULL) {
406 _WatchChanges(false);
407 delete fRef;
410 fRef = ref;
412 _WatchChanges(true);
416 void
417 PersonWindow::_WatchChanges(bool enable)
419 if (fRef == NULL)
420 return;
422 node_ref nodeRef;
424 BNode node(fRef);
425 node.GetNodeRef(&nodeRef);
427 uint32 flags;
428 BString action;
430 if (enable) {
431 // Start watching.
432 flags = B_WATCH_ALL;
433 action = "starting";
434 } else {
435 // Stop watching.
436 flags = B_STOP_WATCHING;
437 action = "stoping";
440 if (watch_node(&nodeRef, flags, this) != B_OK) {
441 printf("Error %s node monitor.\n", action.String());