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